From cff9dab650b8bc0e453be6b312a96b778fdcbc93 Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Tue, 14 Dec 2021 03:11:04 +0000
Subject: [PATCH 0001/1255] Remove combo scaling and change miss penalty
---
.../Difficulty/OsuPerformanceCalculator.cs | 17 +++--------------
1 file changed, 3 insertions(+), 14 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index 8d45c7a8cc..6de6572b93 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -101,13 +101,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
aimValue *= lengthBonus;
- // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
if (effectiveMissCount > 0)
- aimValue *= 0.97 * Math.Pow(1 - Math.Pow((double)effectiveMissCount / totalHits, 0.775), effectiveMissCount);
-
- // Combo scaling.
- if (Attributes.MaxCombo > 0)
- aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
+ aimValue *= Math.Pow(0.97, effectiveMissCount);
double approachRateFactor = 0.0;
if (Attributes.ApproachRate > 10.33)
@@ -151,13 +146,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
speedValue *= lengthBonus;
- // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
if (effectiveMissCount > 0)
- speedValue *= 0.97 * Math.Pow(1 - Math.Pow((double)effectiveMissCount / totalHits, 0.775), Math.Pow(effectiveMissCount, .875));
-
- // Combo scaling.
- if (Attributes.MaxCombo > 0)
- speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
+ speedValue *= Math.Pow(0.97, effectiveMissCount);
double approachRateFactor = 0.0;
if (Attributes.ApproachRate > 10.33)
@@ -238,9 +228,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
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 (effectiveMissCount > 0)
- flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)effectiveMissCount / totalHits, 0.775), Math.Pow(effectiveMissCount, .875));
+ flashlightValue *= Math.Pow(0.97, effectiveMissCount);
// Combo scaling.
if (Attributes.MaxCombo > 0)
From 86ad42a7440ec0a1478e2030209c2675985e3220 Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Tue, 14 Dec 2021 17:47:41 +0000
Subject: [PATCH 0002/1255] Nerf length bonus
---
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 6de6572b93..6ade8c1732 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -96,7 +96,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.
- double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
+ double lengthBonus = 0.95 + 0.25 * Math.Min(1.0, totalHits / 2000.0) +
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
aimValue *= lengthBonus;
@@ -142,7 +142,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.
- double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
+ double lengthBonus = 0.95 + 0.25 * Math.Min(1.0, totalHits / 2000.0) +
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
speedValue *= lengthBonus;
From 489aa43b1bab6efeb0ed05e97ec66181dfe3b4f4 Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Tue, 14 Dec 2021 19:57:36 +0000
Subject: [PATCH 0003/1255] Make miss penalty harsher
---
.../Difficulty/OsuPerformanceCalculator.cs | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index 6ade8c1732..0459c64c0e 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -96,13 +96,13 @@ 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.
- double lengthBonus = 0.95 + 0.25 * Math.Min(1.0, totalHits / 2000.0) +
+ double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
aimValue *= lengthBonus;
if (effectiveMissCount > 0)
- aimValue *= Math.Pow(0.97, effectiveMissCount);
+ aimValue *= Math.Pow(0.96, effectiveMissCount);
double approachRateFactor = 0.0;
if (Attributes.ApproachRate > 10.33)
@@ -142,12 +142,12 @@ 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.
- double lengthBonus = 0.95 + 0.25 * Math.Min(1.0, totalHits / 2000.0) +
+ 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;
if (effectiveMissCount > 0)
- speedValue *= Math.Pow(0.97, effectiveMissCount);
+ speedValue *= Math.Pow(0.96, effectiveMissCount);
double approachRateFactor = 0.0;
if (Attributes.ApproachRate > 10.33)
@@ -229,7 +229,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
flashlightValue *= 1.3;
if (effectiveMissCount > 0)
- flashlightValue *= Math.Pow(0.97, effectiveMissCount);
+ flashlightValue *= Math.Pow(0.96, effectiveMissCount);
// Combo scaling.
if (Attributes.MaxCombo > 0)
From bac4cfed50be4e3d1e0264ce0cd9099c3132d4b5 Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Thu, 16 Dec 2021 18:37:57 +0000
Subject: [PATCH 0004/1255] Use frost's miss count penalty
---
.../Difficulty/OsuPerformanceCalculator.cs | 21 ++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index 0459c64c0e..bba53c92fc 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
aimValue *= lengthBonus;
if (effectiveMissCount > 0)
- aimValue *= Math.Pow(0.96, effectiveMissCount);
+ aimValue *= calculateMissPenalty(effectiveMissCount);
double approachRateFactor = 0.0;
if (Attributes.ApproachRate > 10.33)
@@ -147,7 +147,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
speedValue *= lengthBonus;
if (effectiveMissCount > 0)
- speedValue *= Math.Pow(0.96, effectiveMissCount);
+ speedValue *= calculateMissPenalty(effectiveMissCount);
double approachRateFactor = 0.0;
if (Attributes.ApproachRate > 10.33)
@@ -229,7 +229,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
flashlightValue *= 1.3;
if (effectiveMissCount > 0)
- flashlightValue *= Math.Pow(0.96, effectiveMissCount);
+ flashlightValue *= calculateMissPenalty(effectiveMissCount);
// Combo scaling.
if (Attributes.MaxCombo > 0)
@@ -265,6 +265,21 @@ namespace osu.Game.Rulesets.Osu.Difficulty
return Math.Max(countMiss, (int)Math.Floor(comboBasedMissCount));
}
+ private double calculateMissPenalty(double missCount)
+ {
+ double leniency = 2.0;
+
+ if (missCount > totalHits - leniency)
+ return 0;
+
+ double missApprox = erfInvApprox((totalHits - leniency - missCount) / totalHits);
+ double fcApprox = erfInvApprox((totalHits - leniency) / totalHits);
+
+ return Math.Pow(missApprox / fcApprox, 1.5);
+ }
+
+ private double logit(double x) => Math.Log(x / (1 - x));
+ private double erfInvApprox(double x) => (Math.Sqrt(Math.PI) / 4) * logit((x + 1) / 2);
private int totalHits => countGreat + countOk + countMeh + countMiss;
private int totalSuccessfulHits => countGreat + countOk + countMeh;
}
From 60e2a8ed4b2f0f38dafe0cc9bc6ab8c55128b189 Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Tue, 21 Dec 2021 17:54:26 +0000
Subject: [PATCH 0005/1255] Use MathNet for miss penalty calculation, and use
old penalty formula for Flashlight
---
.../Difficulty/OsuPerformanceCalculator.cs | 14 +++++++-------
osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 4 ++++
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index bba53c92fc..90caa64512 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -9,6 +9,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
+using MathNet.Numerics;
namespace osu.Game.Rulesets.Osu.Difficulty
{
@@ -228,8 +229,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
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 (effectiveMissCount > 0)
- flashlightValue *= calculateMissPenalty(effectiveMissCount);
+ flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)effectiveMissCount / totalHits, 0.775), Math.Pow(effectiveMissCount, .875));
// Combo scaling.
if (Attributes.MaxCombo > 0)
@@ -267,19 +269,17 @@ namespace osu.Game.Rulesets.Osu.Difficulty
private double calculateMissPenalty(double missCount)
{
- double leniency = 2.0;
+ double leniency = 4.3;
if (missCount > totalHits - leniency)
return 0;
- double missApprox = erfInvApprox((totalHits - leniency - missCount) / totalHits);
- double fcApprox = erfInvApprox((totalHits - leniency) / totalHits);
+ double missApprox = SpecialFunctions.ErfInv((totalHits - leniency - missCount) / totalHits);
+ double fcApprox = SpecialFunctions.ErfInv((totalHits - leniency) / totalHits);
- return Math.Pow(missApprox / fcApprox, 1.5);
+ return Math.Pow(missApprox / fcApprox, 3.5);
}
- private double logit(double x) => Math.Log(x / (1 - x));
- private double erfInvApprox(double x) => (Math.Sqrt(Math.PI) / 4) * logit((x + 1) / 2);
private int totalHits => countGreat + countOk + countMeh + countMiss;
private int totalSuccessfulHits => countGreat + countOk + countMeh;
}
diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
index 98f1e69bd1..018bdb93df 100644
--- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
+++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
@@ -15,4 +15,8 @@
+
+
+
+
\ No newline at end of file
From 5640918c8c7c8af2e29feec4c7ad6d501619dafc Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Sun, 26 Dec 2021 23:51:49 +0000
Subject: [PATCH 0006/1255] New miss penalty formula, using relevant difficult
notes in each skill (targets diffspikes)
---
.../Difficulty/OsuDifficultyAttributes.cs | 6 ++++++
.../Difficulty/OsuDifficultyCalculator.cs | 9 +++++++++
.../Difficulty/OsuPerformanceCalculator.cs | 16 ++++------------
.../Difficulty/Skills/OsuStrainSkill.cs | 7 +++++++
4 files changed, 26 insertions(+), 12 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
index 4b2e54da17..6102f4a8b2 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
@@ -24,6 +24,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty
[JsonProperty("slider_factor")]
public double SliderFactor { get; set; }
+ [JsonProperty("aim_difficult_strain_count")]
+ public int AimDifficultStrainCount { get; set; }
+
+ [JsonProperty("speed_difficult_strain_count")]
+ public int SpeedDifficultStrainCount { get; set; }
+
[JsonProperty("approach_rate")]
public double ApproachRate { get; set; }
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index ed42f333c0..ac228b0a32 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -40,6 +40,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
+ int aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).RelevantDifficultStrains();
+ int speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).RelevantDifficultStrains();
+
+ // Total number of strains in a map can vary by clockrate, and this needs to be corrected for.
+ aimDifficultyStrainCount = (int)(aimDifficultyStrainCount * clockRate);
+ speedDifficultyStrainCount = (int)(aimDifficultyStrainCount * clockRate);
+
if (mods.Any(h => h is OsuModRelax))
speedRating = 0.0;
@@ -78,6 +85,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
SpeedStrain = speedRating,
FlashlightRating = flashlightRating,
SliderFactor = sliderFactor,
+ AimDifficultStrainCount = aimDifficultyStrainCount,
+ SpeedDifficultStrainCount = speedDifficultyStrainCount,
ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
OverallDifficulty = (80 - hitWindowGreat) / 6,
DrainRate = drainRate,
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index 90caa64512..3efb8951b3 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
aimValue *= lengthBonus;
if (effectiveMissCount > 0)
- aimValue *= calculateMissPenalty(effectiveMissCount);
+ aimValue *= calculateMissPenalty(effectiveMissCount, Attributes.AimDifficultStrainCount);
double approachRateFactor = 0.0;
if (Attributes.ApproachRate > 10.33)
@@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
speedValue *= lengthBonus;
if (effectiveMissCount > 0)
- speedValue *= calculateMissPenalty(effectiveMissCount);
+ speedValue *= calculateMissPenalty(effectiveMissCount, Attributes.SpeedDifficultStrainCount);
double approachRateFactor = 0.0;
if (Attributes.ApproachRate > 10.33)
@@ -267,17 +267,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
return Math.Max(countMiss, (int)Math.Floor(comboBasedMissCount));
}
- private double calculateMissPenalty(double missCount)
+ private double calculateMissPenalty(double missCount, double strainCount)
{
- double leniency = 4.3;
-
- if (missCount > totalHits - leniency)
- return 0;
-
- double missApprox = SpecialFunctions.ErfInv((totalHits - leniency - missCount) / totalHits);
- double fcApprox = SpecialFunctions.ErfInv((totalHits - leniency) / totalHits);
-
- return Math.Pow(missApprox / fcApprox, 3.5);
+ return 0.95 / ((missCount / (3 * Math.Sqrt(strainCount))) + 1);
}
private int totalHits => countGreat + countOk + countMeh + countMiss;
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
index e47edc37cc..43e39482a7 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
@@ -57,5 +57,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
return difficulty * DifficultyMultiplier;
}
+
+ public int RelevantDifficultStrains()
+ {
+ List strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList();
+
+ return strains.Count(s => s > strains[0] * 0.66);
+ }
}
}
From e9589e57a662bc47297fec2429d7bfce1256db68 Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Mon, 27 Dec 2021 02:23:03 +0000
Subject: [PATCH 0007/1255] Fix logical error
---
osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index ac228b0a32..87ab12248f 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
// Total number of strains in a map can vary by clockrate, and this needs to be corrected for.
aimDifficultyStrainCount = (int)(aimDifficultyStrainCount * clockRate);
- speedDifficultyStrainCount = (int)(aimDifficultyStrainCount * clockRate);
+ speedDifficultyStrainCount = (int)(speedDifficultyStrainCount * clockRate);
if (mods.Any(h => h is OsuModRelax))
speedRating = 0.0;
From 8ce6e3c573104d55956c9a3c4cd3c8beffd41b09 Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Wed, 29 Dec 2021 18:49:13 +0000
Subject: [PATCH 0008/1255] Remove mathnet
---
osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 ----
osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 4 ----
2 files changed, 8 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index d7d294df47..f6e5481feb 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -99,8 +99,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (effectiveMissCount > 0)
aimValue *= 0.97 * Math.Pow(1 - Math.Pow((double)effectiveMissCount / totalHits, 0.775), effectiveMissCount);
- aimValue *= getComboScalingFactor();
-
double approachRateFactor = 0.0;
if (Attributes.ApproachRate > 10.33)
approachRateFactor = 0.3 * (Attributes.ApproachRate - 10.33);
@@ -146,8 +144,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (effectiveMissCount > 0)
speedValue *= 0.97 * Math.Pow(1 - Math.Pow((double)effectiveMissCount / totalHits, 0.775), Math.Pow(effectiveMissCount, .875));
- speedValue *= getComboScalingFactor();
-
double approachRateFactor = 0.0;
if (Attributes.ApproachRate > 10.33)
approachRateFactor = 0.3 * (Attributes.ApproachRate - 10.33);
diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
index 018bdb93df..98f1e69bd1 100644
--- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
+++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
@@ -15,8 +15,4 @@
-
-
-
-
\ No newline at end of file
From 4f257d6987b1144912f561d5a3360593598be834 Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Wed, 29 Dec 2021 18:59:17 +0000
Subject: [PATCH 0009/1255] Clean up unsuccessful merge
---
.../Difficulty/OsuPerformanceCalculator.cs | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index f6e5481feb..793f9e790f 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -95,9 +95,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
aimValue *= lengthBonus;
- // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
if (effectiveMissCount > 0)
- aimValue *= 0.97 * Math.Pow(1 - Math.Pow((double)effectiveMissCount / totalHits, 0.775), effectiveMissCount);
+ aimValue *= calculateMissPenalty(effectiveMissCount, Attributes.AimDifficultStrainCount);
double approachRateFactor = 0.0;
if (Attributes.ApproachRate > 10.33)
@@ -140,9 +139,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
speedValue *= lengthBonus;
- // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
if (effectiveMissCount > 0)
- speedValue *= 0.97 * Math.Pow(1 - Math.Pow((double)effectiveMissCount / totalHits, 0.775), Math.Pow(effectiveMissCount, .875));
+ speedValue *= calculateMissPenalty(effectiveMissCount, Attributes.SpeedDifficultStrainCount);
double approachRateFactor = 0.0;
if (Attributes.ApproachRate > 10.33)
@@ -259,6 +257,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty
}
private double getComboScalingFactor() => Attributes.MaxCombo <= 0 ? 1.0 : Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
+
+ private double calculateMissPenalty(double missCount, double strainCount)
+ {
+ return 0.95 / ((missCount / (3 * Math.Sqrt(strainCount))) + 1);
+ }
+
private int totalHits => countGreat + countOk + countMeh + countMiss;
private int totalSuccessfulHits => countGreat + countOk + countMeh;
}
From fd1028f3bb5c0f19ac2154974fafa528ea6e52e9 Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Wed, 29 Dec 2021 23:49:07 +0000
Subject: [PATCH 0010/1255] Use clockrate in the difficult strain count method
---
.../Difficulty/OsuDifficultyCalculator.cs | 8 ++------
.../Difficulty/Skills/OsuStrainSkill.cs | 9 +++++++--
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index 44ba6c6a58..27d97e9d75 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -40,12 +40,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
- int aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).RelevantDifficultStrains();
- int speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).RelevantDifficultStrains();
-
- // Total number of strains in a map can vary by clockrate, and this needs to be corrected for.
- aimDifficultyStrainCount = (int)(aimDifficultyStrainCount * clockRate);
- speedDifficultyStrainCount = (int)(speedDifficultyStrainCount * clockRate);
+ int aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).CountDifficultStrains(clockRate);
+ int speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).CountDifficultStrains(clockRate);
if (mods.Any(h => h is OsuModRelax))
speedRating = 0.0;
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
index 43e39482a7..fd751727a9 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
@@ -58,11 +58,16 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
return difficulty * DifficultyMultiplier;
}
- public int RelevantDifficultStrains()
+ ///
+ /// Returns the number of difficult strains.
+ /// A strain is considered difficult if it's higher than 66% of the highest strain.
+ ///
+ public int CountDifficultStrains(double clockRate)
{
List strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList();
- return strains.Count(s => s > strains[0] * 0.66);
+ // Total number of strains in a map can vary by clockrate, and this needs to be corrected for.
+ return (int)(strains.Count(s => s > strains[0] * 0.66) * clockRate);
}
}
}
From d2b815b745b1cfaef241488c436b26fede000571 Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Sun, 2 Jan 2022 19:20:20 +0000
Subject: [PATCH 0011/1255] Add miss penalty comment
---
.../Difficulty/OsuPerformanceCalculator.cs | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index 793f9e790f..d8fe85d645 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -256,13 +256,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
return Math.Max(countMiss, (int)Math.Floor(comboBasedMissCount));
}
+ // Miss penalty assumes that a player will miss on the relatively hard parts of a map, not the easy parts, hence the strain count.
+ private double calculateMissPenalty(double missCount, double strainCount) => 0.95 / ((missCount / (3 * Math.Sqrt(strainCount))) + 1);
+
private double getComboScalingFactor() => Attributes.MaxCombo <= 0 ? 1.0 : Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
-
- private double calculateMissPenalty(double missCount, double strainCount)
- {
- return 0.95 / ((missCount / (3 * Math.Sqrt(strainCount))) + 1);
- }
-
private int totalHits => countGreat + countOk + countMeh + countMiss;
private int totalSuccessfulHits => countGreat + countOk + countMeh;
}
From 75be4e83d6a44adc7016c150b5cbd1c74d1caf34 Mon Sep 17 00:00:00 2001
From: Luminiscental
Date: Mon, 3 Jan 2022 22:22:32 +0000
Subject: [PATCH 0012/1255] Remove abusable 0.66 threshold by averaging
---
.../Difficulty/Skills/OsuStrainSkill.cs | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
index fd751727a9..1513befad5 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
@@ -59,15 +59,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
}
///
- /// Returns the number of difficult strains.
- /// A strain is considered difficult if it's higher than 66% of the highest strain.
+ /// Returns the number of strains above a threshold averaged as the threshold varies.
+ /// The result is scaled by clock rate as it affects the total number of strains.
///
public int CountDifficultStrains(double clockRate)
{
- List strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList();
-
- // Total number of strains in a map can vary by clockrate, and this needs to be corrected for.
- return (int)(strains.Count(s => s > strains[0] * 0.66) * clockRate);
+ List strains = GetCurrentStrainPeaks().ToList();
+ // This is the average value of strains.Count(s => s > p * strains.Max()) for p between 0 and 1.
+ double realtimeCount = strains.Sum() / strains.Max();
+ return (int)(clockRate * realtimeCount);
}
}
}
From 132079004ca5e6df33a2bc646a52f3036edff85e Mon Sep 17 00:00:00 2001
From: Luminiscental
Date: Tue, 4 Jan 2022 12:30:05 +0000
Subject: [PATCH 0013/1255] Remove unnecessary truncation
---
osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs | 4 ++--
osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 4 ++--
osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
index 51842f65d5..7ab4232a19 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
@@ -25,10 +25,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
public double SliderFactor { get; set; }
[JsonProperty("aim_difficult_strain_count")]
- public int AimDifficultStrainCount { get; set; }
+ public double AimDifficultStrainCount { get; set; }
[JsonProperty("speed_difficult_strain_count")]
- public int SpeedDifficultStrainCount { get; set; }
+ public double SpeedDifficultStrainCount { get; set; }
[JsonProperty("approach_rate")]
public double ApproachRate { get; set; }
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index 27d97e9d75..3c039c9b7e 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -40,8 +40,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
- int aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).CountDifficultStrains(clockRate);
- int speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).CountDifficultStrains(clockRate);
+ double aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).CountDifficultStrains(clockRate);
+ double speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).CountDifficultStrains(clockRate);
if (mods.Any(h => h is OsuModRelax))
speedRating = 0.0;
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
index 1513befad5..46dc9c683b 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
@@ -62,12 +62,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
/// Returns the number of strains above a threshold averaged as the threshold varies.
/// The result is scaled by clock rate as it affects the total number of strains.
///
- public int CountDifficultStrains(double clockRate)
+ public double CountDifficultStrains(double clockRate)
{
List strains = GetCurrentStrainPeaks().ToList();
// This is the average value of strains.Count(s => s > p * strains.Max()) for p between 0 and 1.
double realtimeCount = strains.Sum() / strains.Max();
- return (int)(clockRate * realtimeCount);
+ return clockRate * realtimeCount;
}
}
}
From 443640a48c8bed53510947240e8337c9350d8d6d Mon Sep 17 00:00:00 2001
From: apollo <83023433+apollo-dw@users.noreply.github.com>
Date: Tue, 4 Jan 2022 16:39:30 +0000
Subject: [PATCH 0014/1255] Revert "Remove abusable 0.66 threshold by
averaging"
---
.../Difficulty/OsuDifficultyAttributes.cs | 4 ++--
.../Difficulty/OsuDifficultyCalculator.cs | 4 ++--
.../Difficulty/Skills/OsuStrainSkill.cs | 14 +++++++-------
3 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
index 7ab4232a19..51842f65d5 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
@@ -25,10 +25,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
public double SliderFactor { get; set; }
[JsonProperty("aim_difficult_strain_count")]
- public double AimDifficultStrainCount { get; set; }
+ public int AimDifficultStrainCount { get; set; }
[JsonProperty("speed_difficult_strain_count")]
- public double SpeedDifficultStrainCount { get; set; }
+ public int SpeedDifficultStrainCount { get; set; }
[JsonProperty("approach_rate")]
public double ApproachRate { get; set; }
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index 3c039c9b7e..27d97e9d75 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -40,8 +40,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
- double aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).CountDifficultStrains(clockRate);
- double speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).CountDifficultStrains(clockRate);
+ int aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).CountDifficultStrains(clockRate);
+ int speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).CountDifficultStrains(clockRate);
if (mods.Any(h => h is OsuModRelax))
speedRating = 0.0;
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
index 46dc9c683b..fd751727a9 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
@@ -59,15 +59,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
}
///
- /// Returns the number of strains above a threshold averaged as the threshold varies.
- /// The result is scaled by clock rate as it affects the total number of strains.
+ /// Returns the number of difficult strains.
+ /// A strain is considered difficult if it's higher than 66% of the highest strain.
///
- public double CountDifficultStrains(double clockRate)
+ public int CountDifficultStrains(double clockRate)
{
- List strains = GetCurrentStrainPeaks().ToList();
- // This is the average value of strains.Count(s => s > p * strains.Max()) for p between 0 and 1.
- double realtimeCount = strains.Sum() / strains.Max();
- return clockRate * realtimeCount;
+ List strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList();
+
+ // Total number of strains in a map can vary by clockrate, and this needs to be corrected for.
+ return (int)(strains.Count(s => s > strains[0] * 0.66) * clockRate);
}
}
}
From dcb969316dd5a7e2bd1b06ebd1450a0ab56ef831 Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Tue, 4 Jan 2022 17:33:23 +0000
Subject: [PATCH 0015/1255] Weight difficult strain count against the top
strain
---
osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
index 46dc9c683b..3e0fccb6c4 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
@@ -59,14 +59,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
}
///
- /// Returns the number of strains above a threshold averaged as the threshold varies.
+ /// Returns the number of strains weighted against the top strain.
/// The result is scaled by clock rate as it affects the total number of strains.
///
public double CountDifficultStrains(double clockRate)
{
List strains = GetCurrentStrainPeaks().ToList();
- // This is the average value of strains.Count(s => s > p * strains.Max()) for p between 0 and 1.
- double realtimeCount = strains.Sum() / strains.Max();
+ double topStrain = strains.Max();
+
+ double realtimeCount = strains.Sum(s => Math.Pow(s / topStrain, 4));
return clockRate * realtimeCount;
}
}
From 400abc147b1f3742061125d91e80a62f5e1333f7 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Thu, 6 Jan 2022 16:28:04 +0900
Subject: [PATCH 0016/1255] Add attribute ids to mapping functions
---
osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs | 4 ++++
osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs | 2 ++
2 files changed, 6 insertions(+)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
index 7ab4232a19..7041acc16c 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
@@ -60,6 +60,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
yield return (ATTRIB_ID_FLASHLIGHT, FlashlightDifficulty);
yield return (ATTRIB_ID_SLIDER_FACTOR, SliderFactor);
+ yield return (ATTRIB_ID_AIM_DIFFICULT_STRAIN_COUNT, AimDifficultStrainCount);
+ yield return (ATTRIB_ID_SPEED_DIFFICULT_STRAIN_COUNT, SpeedDifficultStrainCount);
}
public override void FromDatabaseAttributes(IReadOnlyDictionary values)
@@ -74,6 +76,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
StarRating = values[ATTRIB_ID_DIFFICULTY];
FlashlightDifficulty = values.GetValueOrDefault(ATTRIB_ID_FLASHLIGHT);
SliderFactor = values[ATTRIB_ID_SLIDER_FACTOR];
+ AimDifficultStrainCount = values[ATTRIB_ID_AIM_DIFFICULT_STRAIN_COUNT];
+ SpeedDifficultStrainCount = values[ATTRIB_ID_SPEED_DIFFICULT_STRAIN_COUNT];
}
#region Newtonsoft.Json implicit ShouldSerialize() methods
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs
index 991b567f57..803a5bdac7 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs
@@ -24,6 +24,8 @@ namespace osu.Game.Rulesets.Difficulty
protected const int ATTRIB_ID_SCORE_MULTIPLIER = 15;
protected const int ATTRIB_ID_FLASHLIGHT = 17;
protected const int ATTRIB_ID_SLIDER_FACTOR = 19;
+ protected const int ATTRIB_ID_AIM_DIFFICULT_STRAIN_COUNT = 21;
+ protected const int ATTRIB_ID_SPEED_DIFFICULT_STRAIN_COUNT = 23;
///
/// The mods which were applied to the beatmap.
From 598946737f07c0f2a3ac7523666a868722906db1 Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Wed, 12 Jan 2022 14:38:53 +0000
Subject: [PATCH 0017/1255] Reword comment and rename argument
---
.../Difficulty/OsuPerformanceCalculator.cs | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index d8fe85d645..52658dfe38 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -256,9 +256,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
return Math.Max(countMiss, (int)Math.Floor(comboBasedMissCount));
}
- // Miss penalty assumes that a player will miss on the relatively hard parts of a map, not the easy parts, hence the strain count.
- private double calculateMissPenalty(double missCount, double strainCount) => 0.95 / ((missCount / (3 * Math.Sqrt(strainCount))) + 1);
-
+ // Miss penalty assumes that a player will miss on the hardest parts of a map,
+ // so we use the amount of relatively difficult sections to adjust miss penalty
+ // to make it more punishing on maps with lower amount of hard sections.
+ private double calculateMissPenalty(double missCount, double difficultStrainCount) => 0.95 / ((missCount / (3 * Math.Sqrt(difficultStrainCount))) + 1);
private double getComboScalingFactor() => Attributes.MaxCombo <= 0 ? 1.0 : Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
private int totalHits => countGreat + countOk + countMeh + countMiss;
private int totalSuccessfulHits => countGreat + countOk + countMeh;
From da31ca17e7c12a2922ab83657df4d8d9b6826bbd Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Mon, 14 Feb 2022 01:53:03 +0000
Subject: [PATCH 0018/1255] Use note strains instead of sectional strains
---
osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 2 ++
osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 7 ++++---
osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 6 +++++-
3 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
index a6301aed6d..3486db04af 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
@@ -159,6 +159,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
currentStrain *= strainDecay(current.DeltaTime);
currentStrain += strainValueOf(current) * skillMultiplier;
+ objectStrains.Add(currentStrain);
+
return currentStrain;
}
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
index 3e0fccb6c4..94e2c9d774 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 List objectStrains = new List();
+
protected OsuStrainSkill(Mod[] mods)
: base(mods)
{
@@ -64,10 +66,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
///
public double CountDifficultStrains(double clockRate)
{
- List strains = GetCurrentStrainPeaks().ToList();
- double topStrain = strains.Max();
+ double topStrain = objectStrains.Max();
- double realtimeCount = strains.Sum(s => Math.Pow(s / topStrain, 4));
+ double realtimeCount = objectStrains.Sum(s => Math.Pow(s / topStrain, 4));
return clockRate * realtimeCount;
}
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
index 06d1ef7346..108edc6f2d 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
@@ -173,7 +173,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
currentRhythm = calculateRhythmBonus(current);
- return currentStrain * currentRhythm;
+ double totalStrain = currentStrain * currentRhythm;
+
+ objectStrains.Add(totalStrain);
+
+ return totalStrain;
}
}
}
From 94a46ab640b36c3ca58f03eaf2644ab51c3abd51 Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Mon, 14 Feb 2022 02:02:46 +0000
Subject: [PATCH 0019/1255] Rescale miss penalty for note strains
---
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 cf2116cc5d..cf1af18d39 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -259,7 +259,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
// Miss penalty assumes that a player will miss on the hardest parts of a map,
// so we use the amount of relatively difficult sections to adjust miss penalty
// to make it more punishing on maps with lower amount of hard sections.
- private double calculateMissPenalty(double missCount, double difficultStrainCount) => 0.95 / ((missCount / (3 * Math.Sqrt(difficultStrainCount))) + 1);
+ private double calculateMissPenalty(double missCount, double difficultStrainCount) => 0.94 / ((missCount / (2 * Math.Sqrt(difficultStrainCount))) + 1);
private double getComboScalingFactor() => Attributes.MaxCombo <= 0 ? 1.0 : Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
private int totalHits => countGreat + countOk + countMeh + countMiss;
private int totalSuccessfulHits => countGreat + countOk + countMeh;
From c18df86720244a7d5bba22bc502afd4f959c9082 Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Sat, 19 Feb 2022 15:33:28 +0000
Subject: [PATCH 0020/1255] Remove clockrate factor
---
osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 4 ++--
osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 5 ++---
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index 3c039c9b7e..788b515d7f 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -40,8 +40,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
- double aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).CountDifficultStrains(clockRate);
- double speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).CountDifficultStrains(clockRate);
+ double aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).CountDifficultStrains();
+ double speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).CountDifficultStrains();
if (mods.Any(h => h is OsuModRelax))
speedRating = 0.0;
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
index 94e2c9d774..1124c4466f 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
@@ -64,12 +64,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
/// Returns the number of strains weighted against the top strain.
/// The result is scaled by clock rate as it affects the total number of strains.
///
- public double CountDifficultStrains(double clockRate)
+ public double CountDifficultStrains()
{
double topStrain = objectStrains.Max();
- double realtimeCount = objectStrains.Sum(s => Math.Pow(s / topStrain, 4));
- return clockRate * realtimeCount;
+ return objectStrains.Sum(s => Math.Pow(s / topStrain, 4));
}
}
}
From 2f335a76dca77b0304f5ff21ed7ca2c8e1130757 Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Thu, 17 Mar 2022 22:08:56 +0000
Subject: [PATCH 0021/1255] Switch to using osuAttributes
---
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 48b4f53a3e..964bd73e81 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
aimValue *= lengthBonus;
if (effectiveMissCount > 0)
- aimValue *= calculateMissPenalty(effectiveMissCount, Attributes.AimDifficultStrainCount);
+ aimValue *= calculateMissPenalty(effectiveMissCount, attributes.AimDifficultStrainCount);
double approachRateFactor = 0.0;
if (attributes.ApproachRate > 10.33)
@@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
speedValue *= lengthBonus;
if (effectiveMissCount > 0)
- speedValue *= calculateMissPenalty(effectiveMissCount, Attributes.SpeedDifficultStrainCount);
+ speedValue *= calculateMissPenalty(effectiveMissCount, attributes.SpeedDifficultStrainCount);
double approachRateFactor = 0.0;
if (attributes.ApproachRate > 10.33)
From 442e68ac1a496529869e7cbb75ad40bc4881aef4 Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Tue, 25 Oct 2022 17:41:20 -0400
Subject: [PATCH 0022/1255] Implement taiko deviation estimation
---
.../Difficulty/TaikoPerformanceAttributes.cs | 3 +++
.../Difficulty/TaikoPerformanceCalculator.cs | 27 +++++++++++++++----
.../osu.Game.Rulesets.Taiko.csproj | 4 +++
3 files changed, 29 insertions(+), 5 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs
index b61c13a2df..fa8c3fd27a 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs
@@ -19,6 +19,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
[JsonProperty("effective_miss_count")]
public double EffectiveMissCount { get; set; }
+
+ [JsonProperty("estimated_ur")]
+ public double EstimatedUR { get; set; }
public override IEnumerable GetAttributesForDisplay()
{
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index dc7bad2f75..7d5cf3accd 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -11,6 +11,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Scoring;
+using MathNet.Numerics;
namespace osu.Game.Rulesets.Taiko.Difficulty
{
@@ -21,6 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
private int countMeh;
private int countMiss;
private double accuracy;
+ private double estimatedUR;
private double effectiveMissCount;
@@ -37,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
countOk = score.Statistics.GetValueOrDefault(HitResult.Ok);
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
- accuracy = customAccuracy;
+ estimatedUR = 10 * computeEstimatedUR(score, taikoAttributes);
// The effectiveMissCount is calculated by gaining a ratio for totalSuccessfulHits and increasing the miss penalty for shorter object counts lower than 1000.
if (totalSuccessfulHits > 0)
@@ -64,6 +66,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
Difficulty = difficultyValue,
Accuracy = accuracyValue,
EffectiveMissCount = effectiveMissCount,
+ EstimatedUR = 10 * estimatedUR,
Total = totalValue
};
}
@@ -97,10 +100,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (attributes.GreatHitWindow <= 0)
return 0;
- double accuracyValue = Math.Pow(60.0 / attributes.GreatHitWindow, 1.1) * Math.Pow(accuracy, 8.0) * Math.Pow(attributes.StarRating, 0.4) * 27.0;
+ double accuracyValue = Math.Pow(75 / estimatedUR, 1.1) * Math.Pow(attributes.StarRating, 0.4) * 70.0;
double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
- accuracyValue *= lengthBonus;
// 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))
@@ -109,10 +111,25 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
return accuracyValue;
}
+ private double computeEstimatedUR(ScoreInfo score, TaikoDifficultyAttributes attributes)
+ {
+ if (totalHits == 0)
+ return double.PositiveInfinity;
+
+ double greatProbability = 1 - (countOk + countMiss + 1.0) / (totalHits + 1.0);
+
+ if (greatProbability <= 0)
+ {
+ return double.PositiveInfinity;
+ }
+
+ double deviation = attributes.GreatHitWindow / (Math.Sqrt(2) * SpecialFunctions.ErfInv(greatProbability));
+
+ return deviation;
+ }
+
private int totalHits => countGreat + countOk + countMeh + countMiss;
private int totalSuccessfulHits => countGreat + countOk + countMeh;
-
- private double customAccuracy => totalHits > 0 ? (countGreat * 300 + countOk * 150) / (totalHits * 300.0) : 0;
}
}
diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
index b752c13d18..b2953106d8 100644
--- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
+++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
@@ -15,4 +15,8 @@
+
+
+
+
From d5b06ae9454885e3eeec8de652a8cd51c86d8b7a Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Tue, 25 Oct 2022 17:52:34 -0400
Subject: [PATCH 0023/1255] Fix difficultyvalue acc scaling
---
.../Difficulty/TaikoPerformanceCalculator.cs | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 7d5cf3accd..ee2105d859 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -21,7 +21,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
private int countOk;
private int countMeh;
private int countMiss;
- private double accuracy;
private double estimatedUR;
private double effectiveMissCount;
@@ -92,7 +91,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (score.Mods.Any(m => m is ModFlashlight))
difficultyValue *= 1.050 * lengthBonus;
- return difficultyValue * Math.Pow(accuracy, 2.0);
+ return difficultyValue * Math.Pow(100 * SpecialFunctions.Erf(400 / (Math.Sqrt(2) * estimatedUR)), 2.0);
}
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
From 607a006c4f5b11be1f4e8c223c8cfea2b96a9c19 Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Tue, 25 Oct 2022 17:55:16 -0400
Subject: [PATCH 0024/1255] oops
---
.../Difficulty/TaikoPerformanceCalculator.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index ee2105d859..ba96e0b95e 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (score.Mods.Any(m => m is ModFlashlight))
difficultyValue *= 1.050 * lengthBonus;
- return difficultyValue * Math.Pow(100 * SpecialFunctions.Erf(400 / (Math.Sqrt(2) * estimatedUR)), 2.0);
+ return difficultyValue * Math.Pow(SpecialFunctions.Erf(400 / (Math.Sqrt(2) * estimatedUR)), 2.0);
}
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
From 87cba2d828455b88c26e6eb813667c916ec07c60 Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Tue, 25 Oct 2022 19:15:58 -0400
Subject: [PATCH 0025/1255] Slight adjustments
---
.../Difficulty/TaikoPerformanceCalculator.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index ba96e0b95e..ec5c150ea4 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
Difficulty = difficultyValue,
Accuracy = accuracyValue,
EffectiveMissCount = effectiveMissCount,
- EstimatedUR = 10 * estimatedUR,
+ EstimatedUR = estimatedUR,
Total = totalValue
};
}
@@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (attributes.GreatHitWindow <= 0)
return 0;
- double accuracyValue = Math.Pow(75 / estimatedUR, 1.1) * Math.Pow(attributes.StarRating, 0.4) * 70.0;
+ double accuracyValue = Math.Pow(75 / estimatedUR, 1.1) * Math.Pow(attributes.StarRating, 0.4) * 100.0;
double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
From 7d3338a0eacb1d4ecbcdb82ee98f8f34d2ac4a3d Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Wed, 26 Oct 2022 15:58:20 -0400
Subject: [PATCH 0026/1255] LTCA Balancing pass
---
.../Difficulty/TaikoPerformanceCalculator.cs | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index ec5c150ea4..2a7cb6c6b5 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
private int countOk;
private int countMeh;
private int countMiss;
- private double estimatedUR;
+ private double estimatedDeviation;
private double effectiveMissCount;
@@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
countOk = score.Statistics.GetValueOrDefault(HitResult.Ok);
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
- estimatedUR = 10 * computeEstimatedUR(score, taikoAttributes);
+ estimatedDeviation = computeEstimatedDeviation(score, taikoAttributes);
// The effectiveMissCount is calculated by gaining a ratio for totalSuccessfulHits and increasing the miss penalty for shorter object counts lower than 1000.
if (totalSuccessfulHits > 0)
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
Difficulty = difficultyValue,
Accuracy = accuracyValue,
EffectiveMissCount = effectiveMissCount,
- EstimatedUR = estimatedUR,
+ EstimatedUR = estimatedDeviation * 10,
Total = totalValue
};
}
@@ -86,12 +86,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
difficultyValue *= 1.025;
if (score.Mods.Any(m => m is ModHardRock))
- difficultyValue *= 1.050;
+ difficultyValue *= 1.10;
if (score.Mods.Any(m => m is ModFlashlight))
difficultyValue *= 1.050 * lengthBonus;
- return difficultyValue * Math.Pow(SpecialFunctions.Erf(400 / (Math.Sqrt(2) * estimatedUR)), 2.0);
+ return difficultyValue * Math.Pow(SpecialFunctions.Erf(400 / (Math.Sqrt(2) * estimatedDeviation)), 2.0);
}
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
@@ -99,18 +99,18 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (attributes.GreatHitWindow <= 0)
return 0;
- double accuracyValue = Math.Pow(75 / estimatedUR, 1.1) * Math.Pow(attributes.StarRating, 0.4) * 100.0;
+ double accuracyValue = Math.Pow(7.5 / estimatedDeviation, 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))
- accuracyValue *= Math.Max(1.050, 1.075 * lengthBonus);
+ accuracyValue *= Math.Max(1.0, 1.05 * lengthBonus);
return accuracyValue;
}
- private double computeEstimatedUR(ScoreInfo score, TaikoDifficultyAttributes attributes)
+ private double computeEstimatedDeviation(ScoreInfo score, TaikoDifficultyAttributes attributes)
{
if (totalHits == 0)
return double.PositiveInfinity;
From af919a6550f58da73784931883e59e4b042f910b Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Wed, 26 Oct 2022 16:10:36 -0400
Subject: [PATCH 0027/1255] harshen deviation scaling
---
.../Difficulty/TaikoPerformanceCalculator.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 2a7cb6c6b5..565d166c32 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (score.Mods.Any(m => m is ModFlashlight))
difficultyValue *= 1.050 * lengthBonus;
- return difficultyValue * Math.Pow(SpecialFunctions.Erf(400 / (Math.Sqrt(2) * estimatedDeviation)), 2.0);
+ return difficultyValue * Math.Pow(SpecialFunctions.Erf(40 / (Math.Sqrt(2) * estimatedDeviation)), 2.0);
}
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
From 2940d18d3393c1e006c715ec47d5d2fa88b1bf61 Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Thu, 27 Oct 2022 00:07:32 -0400
Subject: [PATCH 0028/1255] Fix formatting
---
.../Difficulty/TaikoPerformanceAttributes.cs | 2 +-
.../Difficulty/TaikoPerformanceCalculator.cs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs
index fa8c3fd27a..6893f24d20 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
[JsonProperty("effective_miss_count")]
public double EffectiveMissCount { get; set; }
-
+
[JsonProperty("estimated_ur")]
public double EstimatedUR { get; set; }
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 565d166c32..1e57d77d91 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -114,7 +114,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
{
if (totalHits == 0)
return double.PositiveInfinity;
-
+
double greatProbability = 1 - (countOk + countMiss + 1.0) / (totalHits + 1.0);
if (greatProbability <= 0)
From 883790c7a78a7a69b08a3d5299557d2e245c16f5 Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Fri, 28 Oct 2022 16:18:17 -0400
Subject: [PATCH 0029/1255] Return null instead of infinity
---
.../Difficulty/TaikoPerformanceAttributes.cs | 2 +-
.../Difficulty/TaikoPerformanceCalculator.cs | 15 +++++++++------
2 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs
index 6893f24d20..3786009a1f 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
public double EffectiveMissCount { get; set; }
[JsonProperty("estimated_ur")]
- public double EstimatedUR { get; set; }
+ public double? EstimatedUR { get; set; }
public override IEnumerable GetAttributesForDisplay()
{
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 1e57d77d91..c27efb518d 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
private int countOk;
private int countMeh;
private int countMiss;
- private double estimatedDeviation;
+ private double? estimatedDeviation;
private double effectiveMissCount;
@@ -91,15 +91,18 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (score.Mods.Any(m => m is ModFlashlight))
difficultyValue *= 1.050 * lengthBonus;
- return difficultyValue * Math.Pow(SpecialFunctions.Erf(40 / (Math.Sqrt(2) * estimatedDeviation)), 2.0);
+ if (estimatedDeviation == null)
+ return 0;
+
+ return difficultyValue * Math.Pow(SpecialFunctions.Erf(40 / (Math.Sqrt(2) * (double)estimatedDeviation)), 2.0);
}
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
{
- if (attributes.GreatHitWindow <= 0)
+ if (attributes.GreatHitWindow <= 0 || estimatedDeviation == null)
return 0;
- double accuracyValue = Math.Pow(7.5 / estimatedDeviation, 1.1) * Math.Pow(attributes.StarRating, 0.4) * 100.0;
+ double accuracyValue = Math.Pow(7.5 / (double)estimatedDeviation, 1.1) * Math.Pow(attributes.StarRating, 0.4) * 100.0;
double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
@@ -110,10 +113,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
return accuracyValue;
}
- private double computeEstimatedDeviation(ScoreInfo score, TaikoDifficultyAttributes attributes)
+ private double? computeEstimatedDeviation(ScoreInfo score, TaikoDifficultyAttributes attributes)
{
if (totalHits == 0)
- return double.PositiveInfinity;
+ return null;
double greatProbability = 1 - (countOk + countMiss + 1.0) / (totalHits + 1.0);
From 01c79d8ef29abf4871335db06326eeef56d3fed6 Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Fri, 28 Oct 2022 16:20:21 -0400
Subject: [PATCH 0030/1255] remove other infinity reference
---
.../Difficulty/TaikoPerformanceCalculator.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index c27efb518d..4fba926205 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (greatProbability <= 0)
{
- return double.PositiveInfinity;
+ return null;
}
double deviation = attributes.GreatHitWindow / (Math.Sqrt(2) * SpecialFunctions.ErfInv(greatProbability));
From 7403c1cc862f6ebbd9e6a2bc791857b53a973da6 Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Fri, 28 Oct 2022 23:23:50 -0400
Subject: [PATCH 0031/1255] Return null for greatprobability >= 1
---
.../Difficulty/TaikoPerformanceCalculator.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 4fba926205..003e46d77b 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
double greatProbability = 1 - (countOk + countMiss + 1.0) / (totalHits + 1.0);
- if (greatProbability <= 0)
+ if (greatProbability <= 0 || greatProbability >= 1)
{
return null;
}
From 16301f052e8d64164e6f480a594e28868f940614 Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Sun, 30 Oct 2022 15:01:25 -0400
Subject: [PATCH 0032/1255] Fix low end accuracy, buff high end
---
.../Difficulty/TaikoPerformanceCalculator.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 003e46d77b..c57efd3c4d 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (attributes.GreatHitWindow <= 0 || estimatedDeviation == null)
return 0;
- double accuracyValue = Math.Pow(7.5 / (double)estimatedDeviation, 1.1) * Math.Pow(attributes.StarRating, 0.4) * 100.0;
+ double accuracyValue = Math.Pow(7.5 / (double)estimatedDeviation, 1.1) * Math.Pow(attributes.StarRating / 2.7, 0.8) * 100.0;
double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
From 37c21cdd7cbe4e00ae564dfce66d7a55ab1aa68e Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Tue, 1 Nov 2022 15:14:55 -0400
Subject: [PATCH 0033/1255] fix formatting
---
.../Difficulty/TaikoPerformanceAttributes.cs | 2 +-
.../Difficulty/TaikoPerformanceCalculator.cs | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs
index 3786009a1f..74770eaa79 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
public double EffectiveMissCount { get; set; }
[JsonProperty("estimated_ur")]
- public double? EstimatedUR { get; set; }
+ public double? EstimatedUr { get; set; }
public override IEnumerable GetAttributesForDisplay()
{
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index c57efd3c4d..0a24e33419 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
Difficulty = difficultyValue,
Accuracy = accuracyValue,
EffectiveMissCount = effectiveMissCount,
- EstimatedUR = estimatedDeviation * 10,
+ EstimatedUr = estimatedDeviation * 10,
Total = totalValue
};
}
@@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (estimatedDeviation == null)
return 0;
- return difficultyValue * Math.Pow(SpecialFunctions.Erf(40 / (Math.Sqrt(2) * (double)estimatedDeviation)), 2.0);
+ return difficultyValue * Math.Pow(SpecialFunctions.Erf(40 / (Math.Sqrt(2) * estimatedDeviation.Value)), 2.0);
}
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
@@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (attributes.GreatHitWindow <= 0 || estimatedDeviation == null)
return 0;
- double accuracyValue = Math.Pow(7.5 / (double)estimatedDeviation, 1.1) * Math.Pow(attributes.StarRating / 2.7, 0.8) * 100.0;
+ double accuracyValue = Math.Pow(7.5 / estimatedDeviation.Value, 1.1) * Math.Pow(attributes.StarRating / 2.7, 0.8) * 100.0;
double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
@@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
double greatProbability = 1 - (countOk + countMiss + 1.0) / (totalHits + 1.0);
- if (greatProbability <= 0 || greatProbability >= 1)
+ if (greatProbability is <= 0 or >= 1)
{
return null;
}
From 2ba163440aae865d656cf954bed3157d39b60d7f Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Thu, 24 Nov 2022 19:09:30 -0500
Subject: [PATCH 0034/1255] account for low acc FC deviation
---
.../Difficulty/TaikoPerformanceCalculator.cs | 51 ++++++++++++++++---
1 file changed, 45 insertions(+), 6 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 0a24e33419..281dc19837 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -12,6 +12,9 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Scoring;
using MathNet.Numerics;
+using MathNet.Numerics.RootFinding;
+using osu.Framework.Audio.Track;
+using osu.Framework.Extensions.IEnumerableExtensions;
namespace osu.Game.Rulesets.Taiko.Difficulty
{
@@ -66,7 +69,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
Accuracy = accuracyValue,
EffectiveMissCount = effectiveMissCount,
EstimatedUr = estimatedDeviation * 10,
- Total = totalValue
+ Total = (double)estimatedDeviation * 10
};
}
@@ -118,20 +121,56 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (totalHits == 0)
return null;
- double greatProbability = 1 - (countOk + countMiss + 1.0) / (totalHits + 1.0);
+ // Create a new track to properly calculate the hit windows of 100s and 50s.
+ var track = new TrackVirtual(10000);
+ score.Mods.OfType().ForEach(m => m.ApplyToTrack(track));
+ double clockRate = track.Rate;
+ double overallDifficulty = (50 - attributes.GreatHitWindow) / 3 * clockRate;
+ double goodHitWindow = 0;
+ if (overallDifficulty <= 5)
+ goodHitWindow = (120 - 8 * overallDifficulty) / clockRate;
+ if (overallDifficulty > 5)
+ goodHitWindow = 80 - 6 * (overallDifficulty - 5);
+
+ double root2 = Math.Sqrt(2);
- if (greatProbability is <= 0 or >= 1)
+ double logLikelihoodGradient(double d)
{
- return null;
+ if (d <= 0)
+ return 1;
+
+ double p300 = SpecialFunctions.Erf(attributes.GreatHitWindow / root2 * d);
+ double lnP300 = lnErfcApprox(attributes.GreatHitWindow / root2 * d);
+ double lnP100 = lnErfcApprox(goodHitWindow / root2 * d);
+
+ double t1 = countGreat * ((attributes.GreatHitWindow * Math.Exp(-Math.Pow(attributes.GreatHitWindow / root2 * d, 2))) / p300);
+
+ double t2a = Math.Exp(Math.Log(goodHitWindow * Math.Exp(-Math.Pow(goodHitWindow / root2 * d, 2))) - logDiff(lnP300, lnP100));
+ double t2b = Math.Exp(Math.Log(attributes.GreatHitWindow * Math.Exp(-Math.Pow(attributes.GreatHitWindow / root2 * d, 2))) - logDiff(lnP300, lnP100));
+ double t2 = (countOk + 1) * (t2a - t2b);
+
+ double t3 = countMiss * Math.Exp(Math.Log(goodHitWindow * Math.Exp(-Math.Pow(goodHitWindow / root2 * d, 2))) - lnP100);
+
+ return t1 + t2 - t3;
}
- double deviation = attributes.GreatHitWindow / (Math.Sqrt(2) * SpecialFunctions.ErfInv(greatProbability));
+ double root = Brent.FindRootExpand(logLikelihoodGradient, 0.02, 0.3, expandFactor: 2);
- return deviation;
+ return 1 / root;
}
private int totalHits => countGreat + countOk + countMeh + countMiss;
private int totalSuccessfulHits => countGreat + countOk + countMeh;
+
+ double lnErfcApprox(double x)
+ {
+ if (x <= 5)
+ return Math.Log(SpecialFunctions.Erfc(x));
+
+ return -Math.Pow(x, 2) - Math.Log(x) - Math.Log(Math.Sqrt(Math.PI));
+ }
+
+ double logDiff(double l1, double l2) => l1 + SpecialFunctions.Log1p(-Math.Exp(-(l1 - l2)));
}
}
From 0e4e92b344b6bce55ccf6ceb776cc8bfb450338f Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Thu, 24 Nov 2022 19:28:47 -0500
Subject: [PATCH 0035/1255] totalvalue
---
.../Difficulty/TaikoPerformanceCalculator.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 281dc19837..332f6f8267 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
Accuracy = accuracyValue,
EffectiveMissCount = effectiveMissCount,
EstimatedUr = estimatedDeviation * 10,
- Total = (double)estimatedDeviation * 10
+ Total = totalValue
};
}
From b579af674e128b94294ef9d379bc9a89d4dc94df Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Thu, 24 Nov 2022 19:46:55 -0500
Subject: [PATCH 0036/1255] fix dt
---
.../Difficulty/TaikoPerformanceCalculator.cs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 332f6f8267..f184a7cdce 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -125,7 +125,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
var track = new TrackVirtual(10000);
score.Mods.OfType().ForEach(m => m.ApplyToTrack(track));
double clockRate = track.Rate;
- double overallDifficulty = (50 - attributes.GreatHitWindow) / 3 * clockRate;
+
+ double overallDifficulty = (50 - attributes.GreatHitWindow * clockRate) / 3;
double goodHitWindow = 0;
if (overallDifficulty <= 5)
goodHitWindow = (120 - 8 * overallDifficulty) / clockRate;
From e3ef180c4669b3197c7a527daaad76435fa2fa7f Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Fri, 25 Nov 2022 15:18:39 -0500
Subject: [PATCH 0037/1255] fixes
---
.../Difficulty/TaikoPerformanceCalculator.cs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index f184a7cdce..d1e305fe9b 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
private double? computeEstimatedDeviation(ScoreInfo score, TaikoDifficultyAttributes attributes)
{
- if (totalHits == 0)
+ if (totalSuccessfulHits == 0)
return null;
// Create a new track to properly calculate the hit windows of 100s and 50s.
@@ -155,7 +155,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
return t1 + t2 - t3;
}
- double root = Brent.FindRootExpand(logLikelihoodGradient, 0.02, 0.3, expandFactor: 2);
+ double root = Brent.FindRootExpand(logLikelihoodGradient, 0.02, 0.3);
return 1 / root;
}
@@ -164,7 +164,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
private int totalSuccessfulHits => countGreat + countOk + countMeh;
- double lnErfcApprox(double x)
+ private double lnErfcApprox(double x)
{
if (x <= 5)
return Math.Log(SpecialFunctions.Erfc(x));
@@ -172,6 +172,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
return -Math.Pow(x, 2) - Math.Log(x) - Math.Log(Math.Sqrt(Math.PI));
}
- double logDiff(double l1, double l2) => l1 + SpecialFunctions.Log1p(-Math.Exp(-(l1 - l2)));
+ private double logDiff(double l1, double l2) => l1 + SpecialFunctions.Log1p(-Math.Exp(-(l1 - l2)));
}
}
From 7b5373ac5a8f70c332840a5b1fe374f96ac51c19 Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Sun, 27 Nov 2022 15:24:54 -0500
Subject: [PATCH 0038/1255] add comments
---
.../Difficulty/TaikoPerformanceCalculator.cs | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index d1e305fe9b..1d7af4e167 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -116,12 +116,17 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
return accuracyValue;
}
+ ///
+ /// Estimates the player's tap deviation based on the OD, number of objects, and number of 300s, 100s, and misses,
+ /// assuming the player's mean hit error is 0. The estimation is consistent in that two SS scores on the same map with the same settings
+ /// will always return the same deviation. See: https://www.desmos.com/calculator/qlr946netu
+ ///
private double? computeEstimatedDeviation(ScoreInfo score, TaikoDifficultyAttributes attributes)
{
if (totalSuccessfulHits == 0)
return null;
- // Create a new track to properly calculate the hit windows of 100s and 50s.
+ // Create a new track to properly calculate the hit window of 100s.
var track = new TrackVirtual(10000);
score.Mods.OfType().ForEach(m => m.ApplyToTrack(track));
double clockRate = track.Rate;
@@ -135,6 +140,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
double root2 = Math.Sqrt(2);
+ // Log of the most likely deviation resulting in the score's hit judgements, differentiated such that 1 over the most likely deviation returns 0.
double logLikelihoodGradient(double d)
{
if (d <= 0)
From 6a27206abdffa6e18bca1c3f27df6614b9abf869 Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Fri, 2 Dec 2022 17:01:15 -0500
Subject: [PATCH 0039/1255] bugfix + tests
---
.../Difficulty/TaikoPerformanceCalculator.cs | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 19b43d0422..ca4e5fe53a 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -135,11 +135,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
double clockRate = track.Rate;
double overallDifficulty = (50 - attributes.GreatHitWindow * clockRate) / 3;
- double goodHitWindow = 0;
+ double goodHitWindow;
if (overallDifficulty <= 5)
goodHitWindow = (120 - 8 * overallDifficulty) / clockRate;
- if (overallDifficulty > 5)
- goodHitWindow = 80 - 6 * (overallDifficulty - 5);
+ else
+ goodHitWindow = (80 - 6 * (overallDifficulty - 5)) / clockRate;
double root2 = Math.Sqrt(2);
@@ -155,9 +155,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
double t1 = countGreat * ((attributes.GreatHitWindow * Math.Exp(-Math.Pow(attributes.GreatHitWindow / root2 * d, 2))) / p300);
- double t2a = Math.Exp(Math.Log(goodHitWindow * Math.Exp(-Math.Pow(goodHitWindow / root2 * d, 2))) - logDiff(lnP300, lnP100));
- double t2b = Math.Exp(Math.Log(attributes.GreatHitWindow * Math.Exp(-Math.Pow(attributes.GreatHitWindow / root2 * d, 2))) - logDiff(lnP300, lnP100));
- double t2 = (countOk + 1) * (t2a - t2b);
+ double t2A = Math.Exp(Math.Log(goodHitWindow * Math.Exp(-Math.Pow(goodHitWindow / root2 * d, 2))) - logDiff(lnP300, lnP100));
+ double t2B = Math.Exp(Math.Log(attributes.GreatHitWindow * Math.Exp(-Math.Pow(attributes.GreatHitWindow / root2 * d, 2))) - logDiff(lnP300, lnP100));
+ double t2 = (countOk + 1) * (t2A - t2B);
double t3 = countMiss * Math.Exp(Math.Log(goodHitWindow * Math.Exp(-Math.Pow(goodHitWindow / root2 * d, 2))) - lnP100);
From 2b74c4ef8cd60e9d3d6f7907962759573a8896d8 Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Sat, 3 Dec 2022 15:39:38 -0500
Subject: [PATCH 0040/1255] tests return a greathitwindow of 0, add check
---
.../Difficulty/TaikoPerformanceCalculator.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index ca4e5fe53a..fd2dd47d30 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
///
private double? computeEstimatedDeviation(ScoreInfo score, TaikoDifficultyAttributes attributes)
{
- if (totalSuccessfulHits == 0)
+ if (totalSuccessfulHits == 0 || attributes.GreatHitWindow == 0)
return null;
// Create a new track to properly calculate the hit window of 100s.
From 45e8d18b1b9618d8c068731a389285dc270dd2b4 Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Mon, 5 Dec 2022 22:33:13 -0500
Subject: [PATCH 0041/1255] fix extremely low OD breaking deviation calc
---
.../Difficulty/TaikoPerformanceCalculator.cs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index fd2dd47d30..807e9359fc 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -153,13 +153,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
double lnP300 = lnErfcApprox(attributes.GreatHitWindow / root2 * d);
double lnP100 = lnErfcApprox(goodHitWindow / root2 * d);
- double t1 = countGreat * ((attributes.GreatHitWindow * Math.Exp(-Math.Pow(attributes.GreatHitWindow / root2 * d, 2))) / p300);
+ double t1 = countGreat * (Math.Exp(Math.Log(attributes.GreatHitWindow) + -Math.Pow(attributes.GreatHitWindow / root2 * d, 2)) / p300);
- double t2A = Math.Exp(Math.Log(goodHitWindow * Math.Exp(-Math.Pow(goodHitWindow / root2 * d, 2))) - logDiff(lnP300, lnP100));
- double t2B = Math.Exp(Math.Log(attributes.GreatHitWindow * Math.Exp(-Math.Pow(attributes.GreatHitWindow / root2 * d, 2))) - logDiff(lnP300, lnP100));
+ double t2A = Math.Exp(Math.Log(goodHitWindow) + -Math.Pow(goodHitWindow / root2 * d, 2) - logDiff(lnP300, lnP100));
+ double t2B = Math.Exp(Math.Log(attributes.GreatHitWindow) + -Math.Pow(attributes.GreatHitWindow / root2 * d, 2) - logDiff(lnP300, lnP100));
double t2 = (countOk + 1) * (t2A - t2B);
- double t3 = countMiss * Math.Exp(Math.Log(goodHitWindow * Math.Exp(-Math.Pow(goodHitWindow / root2 * d, 2))) - lnP100);
+ double t3 = countMiss * Math.Exp(Math.Log(goodHitWindow) + -Math.Pow(goodHitWindow / root2 * d, 2) - lnP100);
return t1 + t2 - t3;
}
From 334f60f528b130cb47634e960c8fe47ff01581cc Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Wed, 22 Feb 2023 14:55:48 -0500
Subject: [PATCH 0042/1255] Reformat everything to be simpler
---
.../Difficulty/TaikoPerformanceCalculator.cs | 78 ++++++++++---------
1 file changed, 40 insertions(+), 38 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 807e9359fc..84b71a98d4 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -12,7 +12,6 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Scoring;
using MathNet.Numerics;
-using MathNet.Numerics.RootFinding;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.IEnumerableExtensions;
@@ -24,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
private int countOk;
private int countMeh;
private int countMiss;
- private double? estimatedDeviation;
+ private double? estimatedUr;
private double effectiveMissCount;
@@ -41,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
countOk = score.Statistics.GetValueOrDefault(HitResult.Ok);
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
- estimatedDeviation = computeEstimatedDeviation(score, taikoAttributes);
+ estimatedUr = computeEstimatedUr(score, taikoAttributes);
// The effectiveMissCount is calculated by gaining a ratio for totalSuccessfulHits and increasing the miss penalty for shorter object counts lower than 1000.
if (totalSuccessfulHits > 0)
@@ -71,7 +70,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
Difficulty = difficultyValue,
Accuracy = accuracyValue,
EffectiveMissCount = effectiveMissCount,
- EstimatedUr = estimatedDeviation * 10,
+ EstimatedUr = estimatedUr,
Total = totalValue
};
}
@@ -97,18 +96,18 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (score.Mods.Any(m => m is ModFlashlight))
difficultyValue *= 1.050 * lengthBonus;
- if (estimatedDeviation == null)
+ if (estimatedUr == null)
return 0;
- return difficultyValue * Math.Pow(SpecialFunctions.Erf(40 / (Math.Sqrt(2) * estimatedDeviation.Value)), 2.0);
+ return difficultyValue * Math.Pow(SpecialFunctions.Erf(400 / (Math.Sqrt(2) * estimatedUr.Value)), 2.0);
}
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes, bool isConvert)
{
- if (attributes.GreatHitWindow <= 0 || estimatedDeviation == null)
+ if (attributes.GreatHitWindow <= 0 || estimatedUr == null)
return 0;
- double accuracyValue = Math.Pow(7.5 / estimatedDeviation.Value, 1.1) * Math.Pow(attributes.StarRating / 2.7, 0.8) * 100.0;
+ double accuracyValue = Math.Pow(75 / estimatedUr.Value, 1.1) * Math.Pow(attributes.StarRating / 2.7, 0.8) * 100.0;
double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
@@ -124,7 +123,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
/// assuming the player's mean hit error is 0. The estimation is consistent in that two SS scores on the same map with the same settings
/// will always return the same deviation. See: https://www.desmos.com/calculator/qlr946netu
///
- private double? computeEstimatedDeviation(ScoreInfo score, TaikoDifficultyAttributes attributes)
+ private double? computeEstimatedUr(ScoreInfo score, TaikoDifficultyAttributes attributes)
{
if (totalSuccessfulHits == 0 || attributes.GreatHitWindow == 0)
return null;
@@ -135,52 +134,55 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
double clockRate = track.Rate;
double overallDifficulty = (50 - attributes.GreatHitWindow * clockRate) / 3;
- double goodHitWindow;
- if (overallDifficulty <= 5)
- goodHitWindow = (120 - 8 * overallDifficulty) / clockRate;
- else
- goodHitWindow = (80 - 6 * (overallDifficulty - 5)) / clockRate;
+ double h300 = attributes.GreatHitWindow;
+ double h100 = overallDifficulty <= 5 ? (120 - 8 * overallDifficulty) / clockRate : (80 - 6 * (overallDifficulty - 5)) / clockRate;
- double root2 = Math.Sqrt(2);
-
- // Log of the most likely deviation resulting in the score's hit judgements, differentiated such that 1 over the most likely deviation returns 0.
- double logLikelihoodGradient(double d)
+ // Returns the likelihood of a deviation resulting in the score's hit judgements. The peak of the curve is the most likely deviation.
+ double likelihoodGradient(double d)
{
if (d <= 0)
- return 1;
+ return 0;
- double p300 = SpecialFunctions.Erf(attributes.GreatHitWindow / root2 * d);
- double lnP300 = lnErfcApprox(attributes.GreatHitWindow / root2 * d);
- double lnP100 = lnErfcApprox(goodHitWindow / root2 * d);
+ double p300 = logDiff(0, logPcHit(h300, d));
+ double p100 = logDiff(logPcHit(h300, d), logPcHit(h100, d));
+ double p0 = logPcHit(h100, d);
- double t1 = countGreat * (Math.Exp(Math.Log(attributes.GreatHitWindow) + -Math.Pow(attributes.GreatHitWindow / root2 * d, 2)) / p300);
+ double gradient = Math.Exp(
+ (countGreat * p300
+ + (countOk + 0.5) * p100
+ + countMiss * p0) / totalHits
+ );
- double t2A = Math.Exp(Math.Log(goodHitWindow) + -Math.Pow(goodHitWindow / root2 * d, 2) - logDiff(lnP300, lnP100));
- double t2B = Math.Exp(Math.Log(attributes.GreatHitWindow) + -Math.Pow(attributes.GreatHitWindow / root2 * d, 2) - logDiff(lnP300, lnP100));
- double t2 = (countOk + 1) * (t2A - t2B);
-
- double t3 = countMiss * Math.Exp(Math.Log(goodHitWindow) + -Math.Pow(goodHitWindow / root2 * d, 2) - lnP100);
-
- return t1 + t2 - t3;
+ return -gradient;
}
- double root = Brent.FindRootExpand(logLikelihoodGradient, 0.02, 0.3);
+ double deviation = FindMinimum.OfScalarFunction(likelihoodGradient, 30);
- return 1 / root;
+ return deviation * 10;
}
private int totalHits => countGreat + countOk + countMeh + countMiss;
private int totalSuccessfulHits => countGreat + countOk + countMeh;
- private double lnErfcApprox(double x)
+ private double logPcHit(double x, double deviation) => logErfcApprox(x / (deviation * Math.Sqrt(2)));
+
+ // There is a numerical approximation to increase how far you can calculate Erfc(x).
+ private double logErfcApprox(double x) => x <= 5 ? Math.Log(SpecialFunctions.Erfc(x)) : -Math.Pow(x, 2) - Math.Log(x) - Math.Log(Math.Sqrt(Math.PI));
+
+ // Log rules make subtraction of the non-log value non-trivial, this method simply subtracts the base value of 2 logs.
+ private double logDiff(double firstLog, double secondLog)
{
- if (x <= 5)
- return Math.Log(SpecialFunctions.Erfc(x));
+ double maxVal = Math.Max(firstLog, secondLog);
- return -Math.Pow(x, 2) - Math.Log(x) - Math.Log(Math.Sqrt(Math.PI));
+ // Avoid negative infinity - negative infinity (NaN) by checking if the higher value is negative infinity.
+ // Shouldn't ever happen, but good for redundancy purposes.
+ if (double.IsNegativeInfinity(maxVal))
+ {
+ return maxVal;
+ }
+
+ return firstLog + SpecialFunctions.Log1p(-Math.Exp(-(firstLog - secondLog)));
}
-
- private double logDiff(double l1, double l2) => l1 + SpecialFunctions.Log1p(-Math.Exp(-(l1 - l2)));
}
}
From adf16187b1d0e1ceb2520f5a3eccb7551ce7047a Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Wed, 22 Feb 2023 21:03:02 -0500
Subject: [PATCH 0043/1255] Change accuracy scaling
---
.../Difficulty/TaikoPerformanceCalculator.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 84b71a98d4..88c26e9e6a 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (attributes.GreatHitWindow <= 0 || estimatedUr == null)
return 0;
- double accuracyValue = Math.Pow(75 / estimatedUr.Value, 1.1) * Math.Pow(attributes.StarRating / 2.7, 0.8) * 100.0;
+ double accuracyValue = Math.Pow(60 / estimatedUr.Value, 1.1) * Math.Pow(attributes.StarRating, 0.4) * 100.0;
double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
From 858afcd0b3ecdd10869cf540bcd7d79a9e8b1b22 Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Mon, 20 Mar 2023 22:00:33 -0400
Subject: [PATCH 0044/1255] Pass OK hit window as a separate difficulty
attribute, fix erfc approximation
---
.../Difficulty/TaikoDifficultyAttributes.cs | 9 +++++++++
.../Difficulty/TaikoDifficultyCalculator.cs | 1 +
.../Difficulty/TaikoPerformanceCalculator.cs | 18 ++++++------------
3 files changed, 16 insertions(+), 12 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs
index 72452e27b3..f5c9280590 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs
@@ -43,6 +43,15 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
[JsonProperty("great_hit_window")]
public double GreatHitWindow { get; set; }
+ ///
+ /// The perceived hit window for an OK hit inclusive of rate-adjusting mods (DT/HT/etc).
+ ///
+ ///
+ /// Rate-adjusting mods don't directly affect the hit window, but have a perceived effect as a result of adjusting audio timing.
+ ///
+ [JsonProperty("ok_hit_window")]
+ public double OkHitWindow { get; set; }
+
public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes()
{
foreach (var v in base.ToDatabaseAttributes())
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs
index 24b5f5939a..90bcb74533 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs
@@ -95,6 +95,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
ColourDifficulty = colourRating,
PeakDifficulty = combinedRating,
GreatHitWindow = hitWindows.WindowFor(HitResult.Great) / clockRate,
+ OkHitWindow = hitWindows.WindowFor(HitResult.Ok) / clockRate,
MaxCombo = beatmap.HitObjects.Count(h => h is Hit),
};
}
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 88c26e9e6a..e430616d54 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -12,8 +12,6 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Scoring;
using MathNet.Numerics;
-using osu.Framework.Audio.Track;
-using osu.Framework.Extensions.IEnumerableExtensions;
namespace osu.Game.Rulesets.Taiko.Difficulty
{
@@ -125,17 +123,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
///
private double? computeEstimatedUr(ScoreInfo score, TaikoDifficultyAttributes attributes)
{
- if (totalSuccessfulHits == 0 || attributes.GreatHitWindow == 0)
+ if (totalSuccessfulHits == 0 || attributes.GreatHitWindow <= 0)
return null;
- // Create a new track to properly calculate the hit window of 100s.
- var track = new TrackVirtual(10000);
- score.Mods.OfType().ForEach(m => m.ApplyToTrack(track));
- double clockRate = track.Rate;
-
- double overallDifficulty = (50 - attributes.GreatHitWindow * clockRate) / 3;
double h300 = attributes.GreatHitWindow;
- double h100 = overallDifficulty <= 5 ? (120 - 8 * overallDifficulty) / clockRate : (80 - 6 * (overallDifficulty - 5)) / clockRate;
+ double h100 = attributes.OkHitWindow;
// Returns the likelihood of a deviation resulting in the score's hit judgements. The peak of the curve is the most likely deviation.
double likelihoodGradient(double d)
@@ -167,8 +159,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
private double logPcHit(double x, double deviation) => logErfcApprox(x / (deviation * Math.Sqrt(2)));
- // There is a numerical approximation to increase how far you can calculate Erfc(x).
- private double logErfcApprox(double x) => x <= 5 ? Math.Log(SpecialFunctions.Erfc(x)) : -Math.Pow(x, 2) - Math.Log(x) - Math.Log(Math.Sqrt(Math.PI));
+ // There's a numerical approximation to increase how far you can calculate ln(erfc(x)).
+ private double logErfcApprox(double x) => x <= 5
+ ? Math.Log(SpecialFunctions.Erfc(x))
+ : -Math.Pow(x, 2) - Math.Log(x * Math.Sqrt(Math.PI)); // https://www.desmos.com/calculator/kdbxwxgf01
// Log rules make subtraction of the non-log value non-trivial, this method simply subtracts the base value of 2 logs.
private double logDiff(double firstLog, double secondLog)
From 9aa11e00905452f9181755f3997d517c84c3365b Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Mon, 20 Mar 2023 23:13:33 -0400
Subject: [PATCH 0045/1255] update desmos
---
.../Difficulty/TaikoPerformanceCalculator.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index e430616d54..be250f3b80 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -119,7 +119,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
///
/// Estimates the player's tap deviation based on the OD, number of objects, and number of 300s, 100s, and misses,
/// assuming the player's mean hit error is 0. The estimation is consistent in that two SS scores on the same map with the same settings
- /// will always return the same deviation. See: https://www.desmos.com/calculator/qlr946netu
+ /// will always return the same deviation. See: https://www.desmos.com/calculator/x3mvtir093
///
private double? computeEstimatedUr(ScoreInfo score, TaikoDifficultyAttributes attributes)
{
From 31c8cf09332410866c994586f38483d55010c3a2 Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Thu, 27 Jul 2023 22:44:56 -0400
Subject: [PATCH 0046/1255] Buff accuracy scaling
---
.../Difficulty/TaikoPerformanceCalculator.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index a38eaf6a31..39246f962c 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (attributes.GreatHitWindow <= 0 || estimatedUr == null)
return 0;
- double accuracyValue = Math.Pow(60 / estimatedUr.Value, 1.1) * Math.Pow(attributes.StarRating, 0.4) * 100.0;
+ double accuracyValue = Math.Pow(70 / estimatedUr.Value, 1.1) * Math.Pow(attributes.StarRating, 0.4) * 110.0;
double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
From 4de024675c1738d788238d66e88605ff5652159e Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Thu, 27 Jul 2023 23:02:07 -0400
Subject: [PATCH 0047/1255] Make comments more professional
---
.../Difficulty/TaikoPerformanceCalculator.cs | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 39246f962c..f5cdb02c82 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -115,9 +115,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
}
///
- /// Estimates the player's tap deviation based on the OD, number of objects, and number of 300s, 100s, and misses,
- /// assuming the player's mean hit error is 0. The estimation is consistent in that two SS scores on the same map with the same settings
- /// will always return the same deviation. See: https://www.desmos.com/calculator/x3mvtir093
+ /// Calculates the tap deviation for a player using the OD, object count, and scores of 300s, 100s, and misses, with an assumed mean hit error of 0.
+ /// Consistency is ensured as identical SS scores on the same map and settings yield the same deviation.
///
private double? computeEstimatedUr(ScoreInfo score, TaikoDifficultyAttributes attributes)
{
@@ -127,7 +126,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
double h300 = attributes.GreatHitWindow;
double h100 = attributes.OkHitWindow;
- // Returns the likelihood of a deviation resulting in the score's hit judgements. The peak of the curve is the most likely deviation.
+ // Determines the probability of a deviation leading to the score's hit evaluations. The curve's apex represents the most probable deviation.
double likelihoodGradient(double d)
{
if (d <= 0)
@@ -157,18 +156,17 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
private double logPcHit(double x, double deviation) => logErfcApprox(x / (deviation * Math.Sqrt(2)));
- // There's a numerical approximation to increase how far you can calculate ln(erfc(x)).
+ // Utilises a numerical approximation to extend the computation range of ln(erfc(x)).
private double logErfcApprox(double x) => x <= 5
? Math.Log(SpecialFunctions.Erfc(x))
: -Math.Pow(x, 2) - Math.Log(x * Math.Sqrt(Math.PI)); // https://www.desmos.com/calculator/kdbxwxgf01
- // Log rules make subtraction of the non-log value non-trivial, this method simply subtracts the base value of 2 logs.
+ // Subtracts the base value of two logs, circumventing log rules that typically complicate subtraction of non-logarithmic values.
private double logDiff(double firstLog, double secondLog)
{
double maxVal = Math.Max(firstLog, secondLog);
- // Avoid negative infinity - negative infinity (NaN) by checking if the higher value is negative infinity.
- // Shouldn't ever happen, but good for redundancy purposes.
+ // To avoid a NaN result, a check is performed to prevent subtraction of two negative infinity values.
if (double.IsNegativeInfinity(maxVal))
{
return maxVal;
From e56942012cf24981d517ec3d50128412feb7e76b Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Fri, 28 Jul 2023 11:38:30 -0400
Subject: [PATCH 0048/1255] Serialize ok hit window attribute to db
---
osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs | 2 ++
osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs | 1 +
2 files changed, 3 insertions(+)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs
index 9d498d705a..451aed183d 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs
@@ -59,6 +59,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
yield return (ATTRIB_ID_DIFFICULTY, StarRating);
yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow);
+ yield return (ATTRIB_ID_OK_HIT_WINDOW, OkHitWindow);
}
public override void FromDatabaseAttributes(IReadOnlyDictionary values, IBeatmapOnlineInfo onlineInfo)
@@ -67,6 +68,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
StarRating = values[ATTRIB_ID_DIFFICULTY];
GreatHitWindow = values[ATTRIB_ID_GREAT_HIT_WINDOW];
+ OkHitWindow = values[ATTRIB_ID_OK_HIT_WINDOW];
}
}
}
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs
index 5a01faa417..b21b2cf567 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs
@@ -30,6 +30,7 @@ namespace osu.Game.Rulesets.Difficulty
protected const int ATTRIB_ID_LEGACY_ACCURACY_SCORE = 23;
protected const int ATTRIB_ID_LEGACY_COMBO_SCORE = 25;
protected const int ATTRIB_ID_LEGACY_BONUS_SCORE_RATIO = 27;
+ protected const int ATTRIB_ID_OK_HIT_WINDOW = 29;
///
/// The mods which were applied to the beatmap.
From 5f0020b0ca05aceeb6102d076dd7c3dfa8d59dd2 Mon Sep 17 00:00:00 2001
From: Natelytle
Date: Sun, 30 Jul 2023 20:14:15 -0400
Subject: [PATCH 0049/1255] Reduce accuracy scaling
---
.../Difficulty/TaikoPerformanceCalculator.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index af988ef2d1..723a6612bc 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (attributes.GreatHitWindow <= 0 || estimatedUr == null)
return 0;
- double accuracyValue = Math.Pow(70 / estimatedUr.Value, 1.1) * Math.Pow(attributes.StarRating, 0.4) * 110.0;
+ double accuracyValue = Math.Pow(65 / estimatedUr.Value, 1.1) * Math.Pow(attributes.StarRating, 0.4) * 100.0;
double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
From 3495510c7b651efee091bad375591b2aa7875102 Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Sat, 30 Dec 2023 19:16:05 +0100
Subject: [PATCH 0050/1255] kinda working grid from points
---
.../Edit/OsuBlueprintContainer.cs | 42 +++++++++++++++++++
.../Edit/OsuGridToolboxGroup.cs | 21 ++++++++++
2 files changed, 63 insertions(+)
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
index 54c54fca17..8b391cd06e 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Allocation;
+using osu.Framework.Input.Events;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
@@ -8,16 +10,26 @@ using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit.Compose.Components;
+using osuTK;
namespace osu.Game.Rulesets.Osu.Edit
{
public partial class OsuBlueprintContainer : ComposeBlueprintContainer
{
+ private OsuGridToolboxGroup gridToolbox = null!;
+
public OsuBlueprintContainer(HitObjectComposer composer)
: base(composer)
{
}
+ [BackgroundDependencyLoader]
+ private void load(OsuGridToolboxGroup gridToolbox)
+ {
+ this.gridToolbox = gridToolbox;
+ gridToolbox.GridFromPointsClicked += OnGridFromPointsClicked;
+ }
+
protected override SelectionHandler CreateSelectionHandler() => new OsuSelectionHandler();
public override HitObjectSelectionBlueprint? CreateHitObjectBlueprintFor(HitObject hitObject)
@@ -36,5 +48,35 @@ namespace osu.Game.Rulesets.Osu.Edit
return base.CreateHitObjectBlueprintFor(hitObject);
}
+
+ private bool isPlacingGridFromPoints;
+ private Vector2? gridFromPointsStart;
+
+ private void OnGridFromPointsClicked()
+ {
+ isPlacingGridFromPoints = true;
+ gridFromPointsStart = null;
+ }
+
+ protected override bool OnClick(ClickEvent e)
+ {
+ if (!isPlacingGridFromPoints)
+ return base.OnClick(e);
+
+ var pos = ToLocalSpace(Composer.FindSnappedPositionAndTime(e.ScreenSpaceMousePosition).ScreenSpacePosition);
+
+ if (!gridFromPointsStart.HasValue)
+ {
+ gridFromPointsStart = pos;
+ }
+ else
+ {
+ gridToolbox.SetGridFromPoints(gridFromPointsStart.Value, pos);
+ isPlacingGridFromPoints = false;
+ gridFromPointsStart = null;
+ }
+
+ return true;
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs b/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
index 76e735449a..05b90696a0 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
@@ -1,6 +1,7 @@
// 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.Framework.Allocation;
using osu.Framework.Bindables;
@@ -12,6 +13,7 @@ using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
+using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Input.Bindings;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.UI;
@@ -90,6 +92,8 @@ namespace osu.Game.Rulesets.Osu.Edit
private ExpandableSlider gridLinesRotationSlider = null!;
private EditorRadioButtonCollection gridTypeButtons = null!;
+ public event Action? GridFromPointsClicked;
+
public OsuGridToolboxGroup()
: base("grid")
{
@@ -97,6 +101,17 @@ namespace osu.Game.Rulesets.Osu.Edit
private const float max_automatic_spacing = 64;
+ public void SetGridFromPoints(Vector2 point1, Vector2 point2)
+ {
+ StartPositionX.Value = point1.X;
+ StartPositionY.Value = point1.Y;
+ GridLinesRotation.Value = (MathHelper.RadiansToDegrees(MathF.Atan2(point2.Y - point1.Y, point2.X - point1.X)) + 405) % 90 - 45;
+ float dist = Vector2.Distance(point1, point2);
+ while (dist > Spacing.MaxValue)
+ dist /= 2;
+ Spacing.Value = dist;
+ }
+
[BackgroundDependencyLoader]
private void load()
{
@@ -129,6 +144,12 @@ namespace osu.Game.Rulesets.Osu.Edit
Spacing = new Vector2(0f, 10f),
Children = new Drawable[]
{
+ new RoundedButton
+ {
+ Action = () => GridFromPointsClicked?.Invoke(),
+ RelativeSizeAxes = Axes.X,
+ Text = "Grid from points",
+ },
gridTypeButtons = new EditorRadioButtonCollection
{
RelativeSizeAxes = Axes.X,
From fea0ceb49899643371e5f81bf32ccf74fac560df Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Sat, 30 Dec 2023 20:22:14 +0100
Subject: [PATCH 0051/1255] improve the grid from points
---
.../Edit/OsuBlueprintContainer.cs | 26 ++++++++++++++-
.../Edit/OsuGridToolboxGroup.cs | 32 ++++++++++++-------
2 files changed, 45 insertions(+), 13 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
index 8b391cd06e..79b6fafcab 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
@@ -58,12 +58,17 @@ namespace osu.Game.Rulesets.Osu.Edit
gridFromPointsStart = null;
}
+ protected override bool OnMouseDown(MouseDownEvent e)
+ {
+ return isPlacingGridFromPoints || base.OnMouseDown(e);
+ }
+
protected override bool OnClick(ClickEvent e)
{
if (!isPlacingGridFromPoints)
return base.OnClick(e);
- var pos = ToLocalSpace(Composer.FindSnappedPositionAndTime(e.ScreenSpaceMousePosition).ScreenSpacePosition);
+ var pos = ToLocalSpace(Composer.FindSnappedPositionAndTime(e.ScreenSpaceMousePosition, ~SnapType.GlobalGrids).ScreenSpacePosition);
if (!gridFromPointsStart.HasValue)
{
@@ -78,5 +83,24 @@ namespace osu.Game.Rulesets.Osu.Edit
return true;
}
+
+ protected override bool OnMouseMove(MouseMoveEvent e)
+ {
+ if (!isPlacingGridFromPoints)
+ return base.OnMouseMove(e);
+
+ var pos = ToLocalSpace(Composer.FindSnappedPositionAndTime(e.ScreenSpaceMousePosition, ~SnapType.GlobalGrids).ScreenSpacePosition);
+
+ if (!gridFromPointsStart.HasValue)
+ {
+ gridToolbox.StartPosition.Value = pos;
+ }
+ else
+ {
+ gridToolbox.SetGridFromPoints(gridFromPointsStart.Value, pos);
+ }
+
+ return true;
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs b/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
index 05b90696a0..b07d8dcd56 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
@@ -39,7 +39,6 @@ namespace osu.Game.Rulesets.Osu.Edit
{
MinValue = 0f,
MaxValue = OsuPlayfield.BASE_SIZE.X,
- Precision = 1f
};
///
@@ -49,7 +48,6 @@ namespace osu.Game.Rulesets.Osu.Edit
{
MinValue = 0f,
MaxValue = OsuPlayfield.BASE_SIZE.Y,
- Precision = 1f
};
///
@@ -59,7 +57,6 @@ namespace osu.Game.Rulesets.Osu.Edit
{
MinValue = 4f,
MaxValue = 128f,
- Precision = 1f
};
///
@@ -69,7 +66,6 @@ namespace osu.Game.Rulesets.Osu.Edit
{
MinValue = -45f,
MaxValue = 45f,
- Precision = 1f
};
///
@@ -105,9 +101,15 @@ namespace osu.Game.Rulesets.Osu.Edit
{
StartPositionX.Value = point1.X;
StartPositionY.Value = point1.Y;
- GridLinesRotation.Value = (MathHelper.RadiansToDegrees(MathF.Atan2(point2.Y - point1.Y, point2.X - point1.X)) + 405) % 90 - 45;
+
+ // Get the angle between the two points and normalize to the valid range.
+ float period = GridType.Value == PositionSnapGridType.Triangle ? 60 : 90;
+ GridLinesRotation.Value = (MathHelper.RadiansToDegrees(MathF.Atan2(point2.Y - point1.Y, point2.X - point1.X))
+ + 360 + period / 2) % period - period / 2;
+
+ // Divide the distance so that there is a good density of grid lines.
float dist = Vector2.Distance(point1, point2);
- while (dist > Spacing.MaxValue)
+ while (dist > 32)
dist /= 2;
Spacing.Value = dist;
}
@@ -181,22 +183,28 @@ namespace osu.Game.Rulesets.Osu.Edit
StartPositionX.BindValueChanged(x =>
{
- startPositionXSlider.ContractedLabelText = $"X: {x.NewValue:N0}";
- startPositionXSlider.ExpandedLabelText = $"X Offset: {x.NewValue:N0}";
+ startPositionXSlider.ContractedLabelText = $"X: {x.NewValue:#,0.##}";
+ startPositionXSlider.ExpandedLabelText = $"X Offset: {x.NewValue:#,0.##}";
StartPosition.Value = new Vector2(x.NewValue, StartPosition.Value.Y);
}, true);
StartPositionY.BindValueChanged(y =>
{
- startPositionYSlider.ContractedLabelText = $"Y: {y.NewValue:N0}";
- startPositionYSlider.ExpandedLabelText = $"Y Offset: {y.NewValue:N0}";
+ startPositionYSlider.ContractedLabelText = $"Y: {y.NewValue:#,0.##}";
+ startPositionYSlider.ExpandedLabelText = $"Y Offset: {y.NewValue:#,0.##}";
StartPosition.Value = new Vector2(StartPosition.Value.X, y.NewValue);
}, true);
+ StartPosition.BindValueChanged(pos =>
+ {
+ StartPositionX.Value = pos.NewValue.X;
+ StartPositionY.Value = pos.NewValue.Y;
+ });
+
Spacing.BindValueChanged(spacing =>
{
- spacingSlider.ContractedLabelText = $"S: {spacing.NewValue:N0}";
- spacingSlider.ExpandedLabelText = $"Spacing: {spacing.NewValue:N0}";
+ spacingSlider.ContractedLabelText = $"S: {spacing.NewValue:#,0.##}";
+ spacingSlider.ExpandedLabelText = $"Spacing: {spacing.NewValue:#,0.##}";
SpacingVector.Value = new Vector2(spacing.NewValue);
editorBeatmap.BeatmapInfo.GridSize = (int)spacing.NewValue;
}, true);
From c2ea1848ca96c580f36cef4393c2dd5c145f03c1 Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Sat, 30 Dec 2023 20:58:21 +0100
Subject: [PATCH 0052/1255] fix period
---
osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs b/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
index b07d8dcd56..31148f10a2 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
@@ -64,8 +64,8 @@ namespace osu.Game.Rulesets.Osu.Edit
///
public BindableFloat GridLinesRotation { get; } = new BindableFloat(0f)
{
- MinValue = -45f,
- MaxValue = 45f,
+ MinValue = -90f,
+ MaxValue = 90f,
};
///
@@ -103,9 +103,9 @@ namespace osu.Game.Rulesets.Osu.Edit
StartPositionY.Value = point1.Y;
// Get the angle between the two points and normalize to the valid range.
- float period = GridType.Value == PositionSnapGridType.Triangle ? 60 : 90;
+ const float period = 180;
GridLinesRotation.Value = (MathHelper.RadiansToDegrees(MathF.Atan2(point2.Y - point1.Y, point2.X - point1.X))
- + 360 + period / 2) % period - period / 2;
+ + period * 1.5f) % period - period * 0.5f;
// Divide the distance so that there is a good density of grid lines.
float dist = Vector2.Distance(point1, point2);
From c952e2f6209ff0cffa7b7db01266baf2cdab73d3 Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Sun, 31 Dec 2023 02:12:50 +0100
Subject: [PATCH 0053/1255] deselect stuff stuff when grid from points
---
osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs | 3 +++
1 file changed, 3 insertions(+)
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
index 79b6fafcab..a5f7185a7d 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
@@ -56,6 +56,9 @@ namespace osu.Game.Rulesets.Osu.Edit
{
isPlacingGridFromPoints = true;
gridFromPointsStart = null;
+
+ // Deselect all objects because we cant snap to objects which are selected.
+ DeselectAll();
}
protected override bool OnMouseDown(MouseDownEvent e)
From 7a0535a2ebe1e612691f16ced09137feb1aa9da3 Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Sun, 31 Dec 2023 02:26:14 +0100
Subject: [PATCH 0054/1255] add right click for abort
---
.../Edit/OsuBlueprintContainer.cs | 24 +++++++------------
1 file changed, 9 insertions(+), 15 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
index a5f7185a7d..45cf754c57 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
@@ -11,6 +11,7 @@ using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit.Compose.Components;
using osuTK;
+using osuTK.Input;
namespace osu.Game.Rulesets.Osu.Edit
{
@@ -62,28 +63,23 @@ namespace osu.Game.Rulesets.Osu.Edit
}
protected override bool OnMouseDown(MouseDownEvent e)
- {
- return isPlacingGridFromPoints || base.OnMouseDown(e);
- }
-
- protected override bool OnClick(ClickEvent e)
{
if (!isPlacingGridFromPoints)
- return base.OnClick(e);
+ return base.OnMouseDown(e);
- var pos = ToLocalSpace(Composer.FindSnappedPositionAndTime(e.ScreenSpaceMousePosition, ~SnapType.GlobalGrids).ScreenSpacePosition);
+ var pos = snappedLocalPosition(e);
if (!gridFromPointsStart.HasValue)
- {
gridFromPointsStart = pos;
- }
else
{
gridToolbox.SetGridFromPoints(gridFromPointsStart.Value, pos);
isPlacingGridFromPoints = false;
- gridFromPointsStart = null;
}
+ if (e.Button == MouseButton.Right)
+ isPlacingGridFromPoints = false;
+
return true;
}
@@ -92,18 +88,16 @@ namespace osu.Game.Rulesets.Osu.Edit
if (!isPlacingGridFromPoints)
return base.OnMouseMove(e);
- var pos = ToLocalSpace(Composer.FindSnappedPositionAndTime(e.ScreenSpaceMousePosition, ~SnapType.GlobalGrids).ScreenSpacePosition);
+ var pos = snappedLocalPosition(e);
if (!gridFromPointsStart.HasValue)
- {
gridToolbox.StartPosition.Value = pos;
- }
else
- {
gridToolbox.SetGridFromPoints(gridFromPointsStart.Value, pos);
- }
return true;
}
+
+ private Vector2 snappedLocalPosition(UIEvent e) => ToLocalSpace(Composer.FindSnappedPositionAndTime(e.ScreenSpaceMousePosition, ~SnapType.GlobalGrids).ScreenSpacePosition);
}
}
From 85bfd6137a5f286341a14615a9ac0ea1fee8137f Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Sun, 31 Dec 2023 03:53:42 +0100
Subject: [PATCH 0055/1255] improve UI
From 4e3fe5112d9d02171fb19e23bd26520f34590c87 Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Mon, 1 Jan 2024 16:09:18 +0100
Subject: [PATCH 0056/1255] merge conflict fix
---
osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs b/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
index 31148f10a2..c318bac64d 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
@@ -86,6 +86,7 @@ namespace osu.Game.Rulesets.Osu.Edit
private ExpandableSlider startPositionYSlider = null!;
private ExpandableSlider spacingSlider = null!;
private ExpandableSlider gridLinesRotationSlider = null!;
+ private RoundedButton gridFromPointsButton = null!;
private EditorRadioButtonCollection gridTypeButtons = null!;
public event Action? GridFromPointsClicked;
@@ -146,7 +147,7 @@ namespace osu.Game.Rulesets.Osu.Edit
Spacing = new Vector2(0f, 10f),
Children = new Drawable[]
{
- new RoundedButton
+ gridFromPointsButton = new RoundedButton
{
Action = () => GridFromPointsClicked?.Invoke(),
RelativeSizeAxes = Axes.X,
@@ -217,6 +218,8 @@ namespace osu.Game.Rulesets.Osu.Edit
expandingContainer?.Expanded.BindValueChanged(v =>
{
+ gridFromPointsButton.FadeTo(v.NewValue ? 1f : 0f, 500, Easing.OutQuint);
+ gridFromPointsButton.BypassAutoSizeAxes = !v.NewValue ? Axes.Y : Axes.None;
gridTypeButtons.FadeTo(v.NewValue ? 1f : 0f, 500, Easing.OutQuint);
gridTypeButtons.BypassAutoSizeAxes = !v.NewValue ? Axes.Y : Axes.None;
}, true);
From 98505d0bba18fe4b08c06bf44d77298040c1c5e8 Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Sun, 31 Dec 2023 18:58:29 +0100
Subject: [PATCH 0057/1255] improve grid from points tool code
---
.../Edit/GridFromPointsTool.cs | 72 +++++++++++++++++++
.../Edit/OsuBlueprintContainer.cs | 63 ----------------
.../Edit/OsuHitObjectComposer.cs | 18 +++--
osu.Game/Rulesets/Edit/HitObjectComposer.cs | 5 +-
4 files changed, 90 insertions(+), 68 deletions(-)
create mode 100644 osu.Game.Rulesets.Osu/Edit/GridFromPointsTool.cs
diff --git a/osu.Game.Rulesets.Osu/Edit/GridFromPointsTool.cs b/osu.Game.Rulesets.Osu/Edit/GridFromPointsTool.cs
new file mode 100644
index 0000000000..722d9d1303
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/GridFromPointsTool.cs
@@ -0,0 +1,72 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Input.Events;
+using osu.Game.Rulesets.Edit;
+using osuTK;
+using osuTK.Input;
+
+namespace osu.Game.Rulesets.Osu.Edit
+{
+ public partial class GridFromPointsTool : Drawable
+ {
+ [Resolved]
+ private OsuGridToolboxGroup gridToolboxGroup { get; set; } = null!;
+
+ [Resolved(canBeNull: true)]
+ private IPositionSnapProvider? snapProvider { get; set; }
+
+ public bool IsPlacing { get; private set; }
+
+ private Vector2? startPosition;
+
+ public void BeginPlacement()
+ {
+ IsPlacing = true;
+ startPosition = null;
+ }
+
+ protected override bool OnMouseDown(MouseDownEvent e)
+ {
+ if (!IsPlacing)
+ return base.OnMouseDown(e);
+
+ var pos = snappedLocalPosition(e);
+
+ if (!startPosition.HasValue)
+ startPosition = pos;
+ else
+ {
+ gridToolboxGroup.SetGridFromPoints(startPosition.Value, pos);
+ IsPlacing = false;
+ }
+
+ if (e.Button == MouseButton.Right)
+ IsPlacing = false;
+
+ return true;
+ }
+
+ protected override bool OnMouseMove(MouseMoveEvent e)
+ {
+ if (!IsPlacing)
+ return base.OnMouseMove(e);
+
+ var pos = snappedLocalPosition(e);
+
+ if (!startPosition.HasValue)
+ gridToolboxGroup.StartPosition.Value = pos;
+ else
+ gridToolboxGroup.SetGridFromPoints(startPosition.Value, pos);
+
+ return true;
+ }
+
+ private Vector2 snappedLocalPosition(UIEvent e)
+ {
+ return ToLocalSpace(snapProvider?.FindSnappedPositionAndTime(e.ScreenSpaceMousePosition, ~SnapType.GlobalGrids).ScreenSpacePosition ?? e.ScreenSpaceMousePosition);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
index 45cf754c57..54c54fca17 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Framework.Allocation;
-using osu.Framework.Input.Events;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
@@ -10,27 +8,16 @@ using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit.Compose.Components;
-using osuTK;
-using osuTK.Input;
namespace osu.Game.Rulesets.Osu.Edit
{
public partial class OsuBlueprintContainer : ComposeBlueprintContainer
{
- private OsuGridToolboxGroup gridToolbox = null!;
-
public OsuBlueprintContainer(HitObjectComposer composer)
: base(composer)
{
}
- [BackgroundDependencyLoader]
- private void load(OsuGridToolboxGroup gridToolbox)
- {
- this.gridToolbox = gridToolbox;
- gridToolbox.GridFromPointsClicked += OnGridFromPointsClicked;
- }
-
protected override SelectionHandler CreateSelectionHandler() => new OsuSelectionHandler();
public override HitObjectSelectionBlueprint? CreateHitObjectBlueprintFor(HitObject hitObject)
@@ -49,55 +36,5 @@ namespace osu.Game.Rulesets.Osu.Edit
return base.CreateHitObjectBlueprintFor(hitObject);
}
-
- private bool isPlacingGridFromPoints;
- private Vector2? gridFromPointsStart;
-
- private void OnGridFromPointsClicked()
- {
- isPlacingGridFromPoints = true;
- gridFromPointsStart = null;
-
- // Deselect all objects because we cant snap to objects which are selected.
- DeselectAll();
- }
-
- protected override bool OnMouseDown(MouseDownEvent e)
- {
- if (!isPlacingGridFromPoints)
- return base.OnMouseDown(e);
-
- var pos = snappedLocalPosition(e);
-
- if (!gridFromPointsStart.HasValue)
- gridFromPointsStart = pos;
- else
- {
- gridToolbox.SetGridFromPoints(gridFromPointsStart.Value, pos);
- isPlacingGridFromPoints = false;
- }
-
- if (e.Button == MouseButton.Right)
- isPlacingGridFromPoints = false;
-
- return true;
- }
-
- protected override bool OnMouseMove(MouseMoveEvent e)
- {
- if (!isPlacingGridFromPoints)
- return base.OnMouseMove(e);
-
- var pos = snappedLocalPosition(e);
-
- if (!gridFromPointsStart.HasValue)
- gridToolbox.StartPosition.Value = pos;
- else
- gridToolbox.SetGridFromPoints(gridFromPointsStart.Value, pos);
-
- return true;
- }
-
- private Vector2 snappedLocalPosition(UIEvent e) => ToLocalSpace(Composer.FindSnappedPositionAndTime(e.ScreenSpaceMousePosition, ~SnapType.GlobalGrids).ScreenSpacePosition);
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
index 02e98d75a7..e035317194 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
@@ -81,13 +81,12 @@ namespace osu.Game.Rulesets.Osu.Edit
// Give a bit of breathing room around the playfield content.
PlayfieldContentContainer.Padding = new MarginPadding(10);
- LayerBelowRuleset.AddRange(new Drawable[]
- {
+ LayerBelowRuleset.Add(
distanceSnapGridContainer = new Container
{
RelativeSizeAxes = Axes.Both
}
- });
+ );
selectedHitObjects = EditorBeatmap.SelectedHitObjects.GetBoundCopy();
selectedHitObjects.CollectionChanged += (_, _) => updateDistanceSnapGrid();
@@ -100,6 +99,15 @@ namespace osu.Game.Rulesets.Osu.Edit
updateDistanceSnapGrid();
OsuGridToolboxGroup.GridType.BindValueChanged(updatePositionSnapGrid, true);
+ OsuGridToolboxGroup.GridFromPointsClicked += () => gridFromPointsTool.BeginPlacement();
+
+ LayerAboveRuleset.Add(
+ // Place it above the playfield and blueprints, so it takes priority when handling input.
+ gridFromPointsTool = new GridFromPointsTool
+ {
+ RelativeSizeAxes = Axes.Both,
+ }
+ );
RightToolbox.AddRange(new EditorToolboxGroup[]
{
@@ -110,6 +118,8 @@ namespace osu.Game.Rulesets.Osu.Edit
);
}
+ private GridFromPointsTool gridFromPointsTool;
+
private void updatePositionSnapGrid(ValueChangedEvent obj)
{
if (positionSnapGrid != null)
@@ -278,7 +288,7 @@ namespace osu.Game.Rulesets.Osu.Edit
foreach (var b in blueprints)
{
- if (b.IsSelected)
+ if (b.IsSelected && !gridFromPointsTool.IsPlacing)
continue;
var snapPositions = b.ScreenSpaceSnapPoints;
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index 50e6393895..4f8329cc82 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -76,6 +76,8 @@ namespace osu.Game.Rulesets.Edit
protected readonly Container LayerBelowRuleset = new Container { RelativeSizeAxes = Axes.Both };
+ protected readonly Container LayerAboveRuleset = new Container { RelativeSizeAxes = Axes.Both };
+
protected InputManager InputManager { get; private set; }
private EditorRadioButtonCollection toolboxCollection;
@@ -137,7 +139,8 @@ namespace osu.Game.Rulesets.Edit
drawableRulesetWrapper,
// layers above playfield
drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer()
- .WithChild(BlueprintContainer = CreateBlueprintContainer())
+ .WithChild(BlueprintContainer = CreateBlueprintContainer()),
+ drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer().WithChild(LayerAboveRuleset),
}
},
new Container
From b54c9a36fea93e5bcf2ec3b05ac931eba5090c96 Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Sun, 31 Dec 2023 19:08:27 +0100
Subject: [PATCH 0058/1255] move GridFromPointsClicked handler creation code
---
osu.Game.Rulesets.Osu/Edit/GridFromPointsTool.cs | 7 +++++++
osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 5 ++---
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Edit/GridFromPointsTool.cs b/osu.Game.Rulesets.Osu/Edit/GridFromPointsTool.cs
index 722d9d1303..9bbeeaafc3 100644
--- a/osu.Game.Rulesets.Osu/Edit/GridFromPointsTool.cs
+++ b/osu.Game.Rulesets.Osu/Edit/GridFromPointsTool.cs
@@ -22,6 +22,13 @@ namespace osu.Game.Rulesets.Osu.Edit
private Vector2? startPosition;
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ gridToolboxGroup.GridFromPointsClicked += BeginPlacement;
+ }
+
public void BeginPlacement()
{
IsPlacing = true;
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
index e035317194..cd5c4f4ba9 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
@@ -63,6 +63,8 @@ namespace osu.Game.Rulesets.Osu.Edit
private Bindable placementObject;
+ private GridFromPointsTool gridFromPointsTool;
+
[Cached(typeof(IDistanceSnapProvider))]
protected readonly OsuDistanceSnapProvider DistanceSnapProvider = new OsuDistanceSnapProvider();
@@ -99,7 +101,6 @@ namespace osu.Game.Rulesets.Osu.Edit
updateDistanceSnapGrid();
OsuGridToolboxGroup.GridType.BindValueChanged(updatePositionSnapGrid, true);
- OsuGridToolboxGroup.GridFromPointsClicked += () => gridFromPointsTool.BeginPlacement();
LayerAboveRuleset.Add(
// Place it above the playfield and blueprints, so it takes priority when handling input.
@@ -118,8 +119,6 @@ namespace osu.Game.Rulesets.Osu.Edit
);
}
- private GridFromPointsTool gridFromPointsTool;
-
private void updatePositionSnapGrid(ValueChangedEvent obj)
{
if (positionSnapGrid != null)
From f8979cff3aaf2c184f08d66fd6cc6f4aeb397f41 Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Mon, 1 Jan 2024 16:14:20 +0100
Subject: [PATCH 0059/1255] fix distance
---
osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs b/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
index c318bac64d..b73eaecc66 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
@@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Edit
// Divide the distance so that there is a good density of grid lines.
float dist = Vector2.Distance(point1, point2);
- while (dist > 32)
+ while (dist >= max_automatic_spacing)
dist /= 2;
Spacing.Value = dist;
}
From 2918ecf46c3516039a28f58d2dd71042d505f574 Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Thu, 1 Feb 2024 16:56:57 +0100
Subject: [PATCH 0060/1255] Remove Masking from PositionSnapGrid
This caused issues in rendering the outline of the grid because the outline was getting masked at some resolutions.
---
.../Components/LinedPositionSnapGrid.cs | 128 +++++++++++++++---
.../Compose/Components/PositionSnapGrid.cs | 2 -
2 files changed, 106 insertions(+), 24 deletions(-)
diff --git a/osu.Game/Screens/Edit/Compose/Components/LinedPositionSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/LinedPositionSnapGrid.cs
index ebdd76a4e2..8a7f6b5344 100644
--- a/osu.Game/Screens/Edit/Compose/Components/LinedPositionSnapGrid.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/LinedPositionSnapGrid.cs
@@ -15,18 +15,29 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
protected void GenerateGridLines(Vector2 step, Vector2 drawSize)
{
+ if (Precision.AlmostEquals(step, Vector2.Zero))
+ return;
+
int index = 0;
- var currentPosition = StartPosition.Value;
// Make lines the same width independent of display resolution.
float lineWidth = DrawWidth / ScreenSpaceDrawQuad.Width;
- float lineLength = drawSize.Length * 2;
+ float rotation = MathHelper.RadiansToDegrees(MathF.Atan2(step.Y, step.X));
List generatedLines = new List();
- while (lineDefinitelyIntersectsBox(currentPosition, step.PerpendicularLeft, drawSize) ||
- isMovingTowardsBox(currentPosition, step, drawSize))
+ while (true)
{
+ Vector2 currentPosition = StartPosition.Value + index++ * step;
+
+ if (!lineDefinitelyIntersectsBox(currentPosition, step.PerpendicularLeft, drawSize, out var p1, out var p2))
+ {
+ if (!isMovingTowardsBox(currentPosition, step, drawSize))
+ break;
+
+ continue;
+ }
+
var gridLine = new Box
{
Colour = Colour4.White,
@@ -34,15 +45,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.None,
Width = lineWidth,
- Height = lineLength,
- Position = currentPosition,
- Rotation = MathHelper.RadiansToDegrees(MathF.Atan2(step.Y, step.X)),
+ Height = Vector2.Distance(p1, p2),
+ Position = (p1 + p2) / 2,
+ Rotation = rotation,
};
generatedLines.Add(gridLine);
-
- index += 1;
- currentPosition = StartPosition.Value + index * step;
}
if (generatedLines.Count == 0)
@@ -59,23 +67,99 @@ namespace osu.Game.Screens.Edit.Compose.Components
(currentPosition + step - box).LengthSquared < (currentPosition - box).LengthSquared;
}
- private bool lineDefinitelyIntersectsBox(Vector2 lineStart, Vector2 lineDir, Vector2 box)
+ ///
+ /// Determines if the line starting at and going in the direction of
+ /// definitely intersects the box on (0, 0) with the given width and height and returns the intersection points if it does.
+ ///
+ /// The start point of the line.
+ /// The direction of the line.
+ /// The width and height of the box.
+ /// The first intersection point.
+ /// The second intersection point.
+ /// Whether the line definitely intersects the box.
+ private bool lineDefinitelyIntersectsBox(Vector2 lineStart, Vector2 lineDir, Vector2 box, out Vector2 p1, out Vector2 p2)
{
- var p2 = lineStart + lineDir;
+ p1 = Vector2.Zero;
+ p2 = Vector2.Zero;
- double d1 = det(Vector2.Zero);
- double d2 = det(new Vector2(box.X, 0));
- double d3 = det(new Vector2(0, box.Y));
- double d4 = det(box);
+ if (Precision.AlmostEquals(lineDir.X, 0))
+ {
+ // If the line is vertical, we only need to check if the X coordinate of the line is within the box.
+ if (!Precision.DefinitelyBigger(lineStart.X, 0) || !Precision.DefinitelyBigger(box.X, lineStart.X))
+ return false;
- return definitelyDifferentSign(d1, d2) || definitelyDifferentSign(d3, d4) ||
- definitelyDifferentSign(d1, d3) || definitelyDifferentSign(d2, d4);
+ p1 = new Vector2(lineStart.X, 0);
+ p2 = new Vector2(lineStart.X, box.Y);
+ return true;
+ }
- double det(Vector2 p) => (p.X - lineStart.X) * (p2.Y - lineStart.Y) - (p.Y - lineStart.Y) * (p2.X - lineStart.X);
+ if (Precision.AlmostEquals(lineDir.Y, 0))
+ {
+ // If the line is horizontal, we only need to check if the Y coordinate of the line is within the box.
+ if (!Precision.DefinitelyBigger(lineStart.Y, 0) || !Precision.DefinitelyBigger(box.Y, lineStart.Y))
+ return false;
- bool definitelyDifferentSign(double a, double b) => !Precision.AlmostEquals(a, 0) &&
- !Precision.AlmostEquals(b, 0) &&
- Math.Sign(a) != Math.Sign(b);
+ p1 = new Vector2(0, lineStart.Y);
+ p2 = new Vector2(box.X, lineStart.Y);
+ return true;
+ }
+
+ float m = lineDir.Y / lineDir.X;
+ float mInv = lineDir.X / lineDir.Y; // Use this to improve numerical stability if X is close to zero.
+ float b = lineStart.Y - m * lineStart.X;
+
+ // Calculate intersection points with the sides of the box.
+ var p = new List(4);
+
+ if (0 <= b && b <= box.Y)
+ p.Add(new Vector2(0, b));
+ if (0 <= (box.Y - b) * mInv && (box.Y - b) * mInv <= box.X)
+ p.Add(new Vector2((box.Y - b) * mInv, box.Y));
+ if (0 <= m * box.X + b && m * box.X + b <= box.Y)
+ p.Add(new Vector2(box.X, m * box.X + b));
+ if (0 <= -b * mInv && -b * mInv <= box.X)
+ p.Add(new Vector2(-b * mInv, 0));
+
+ switch (p.Count)
+ {
+ case 4:
+ // If there are 4 intersection points, the line is a diagonal of the box.
+ if (m > 0)
+ {
+ p1 = Vector2.Zero;
+ p2 = box;
+ }
+ else
+ {
+ p1 = new Vector2(0, box.Y);
+ p2 = new Vector2(box.X, 0);
+ }
+
+ break;
+
+ case 3:
+ // If there are 3 intersection points, the line goes through a corner of the box.
+ if (p[0] == p[1])
+ {
+ p1 = p[0];
+ p2 = p[2];
+ }
+ else
+ {
+ p1 = p[0];
+ p2 = p[1];
+ }
+
+ break;
+
+ case 2:
+ p1 = p[0];
+ p2 = p[1];
+
+ break;
+ }
+
+ return !Precision.AlmostEquals(p1, p2);
}
}
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/PositionSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/PositionSnapGrid.cs
index 36687ef73a..e576ac1e49 100644
--- a/osu.Game/Screens/Edit/Compose/Components/PositionSnapGrid.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/PositionSnapGrid.cs
@@ -21,8 +21,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
protected PositionSnapGrid()
{
- Masking = true;
-
StartPosition.BindValueChanged(_ => GridCache.Invalidate());
AddLayout(GridCache);
From 93dbd7507fd61086a8c9d3422e6a02560254f4d4 Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Thu, 1 Feb 2024 17:07:03 +0100
Subject: [PATCH 0061/1255] Fix masking in circular snap grid
---
.../Edit/Compose/Components/CircularPositionSnapGrid.cs | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularPositionSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularPositionSnapGrid.cs
index 403a270359..791cb33439 100644
--- a/osu.Game/Screens/Edit/Compose/Components/CircularPositionSnapGrid.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/CircularPositionSnapGrid.cs
@@ -82,7 +82,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
generatedCircles.First().Alpha = 0.8f;
- AddRangeInternal(generatedCircles);
+ AddInternal(new Container
+ {
+ Masking = true,
+ RelativeSizeAxes = Axes.Both,
+ Children = generatedCircles,
+ });
}
public override Vector2 GetSnappedPosition(Vector2 original)
From 8ccb14f19f1363c42eaf80718ed50c1a68338185 Mon Sep 17 00:00:00 2001
From: tsunyoku
Date: Tue, 6 Feb 2024 13:08:17 +0000
Subject: [PATCH 0062/1255] include slider count in accuracy pp if slider head
accuracy is in use
---
osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index b31f4ff519..7f4c5f6c46 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -192,6 +192,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
// 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;
+ if (!score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value))
+ amountHitObjectsWithAccuracy += attributes.SliderCount;
if (amountHitObjectsWithAccuracy > 0)
betterAccuracyPercentage = ((countGreat - (totalHits - amountHitObjectsWithAccuracy)) * 6 + countOk * 2 + countMeh) / (double)(amountHitObjectsWithAccuracy * 6);
From 91fb59ee15caf75b08a43bd6508a4edf3f49e8f5 Mon Sep 17 00:00:00 2001
From: Salman Ahmed
Date: Sun, 11 Feb 2024 07:37:13 +0300
Subject: [PATCH 0063/1255] Introduce `LocalUserStatisticsProvider` component
---
.../TestSceneLocalUserStatisticsProvider.cs | 141 ++++++++++++++++++
.../Online/LocalUserStatisticsProvider.cs | 94 ++++++++++++
osu.Game/OsuGameBase.cs | 6 +-
3 files changed, 240 insertions(+), 1 deletion(-)
create mode 100644 osu.Game.Tests/Visual/Online/TestSceneLocalUserStatisticsProvider.cs
create mode 100644 osu.Game/Online/LocalUserStatisticsProvider.cs
diff --git a/osu.Game.Tests/Visual/Online/TestSceneLocalUserStatisticsProvider.cs b/osu.Game.Tests/Visual/Online/TestSceneLocalUserStatisticsProvider.cs
new file mode 100644
index 0000000000..1a27fd1de5
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneLocalUserStatisticsProvider.cs
@@ -0,0 +1,141 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using NUnit.Framework;
+using osu.Framework.Extensions.ObjectExtensions;
+using osu.Framework.Graphics;
+using osu.Framework.Testing;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Online;
+using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
+using osu.Game.Online.API.Requests.Responses;
+using osu.Game.Rulesets.Catch;
+using osu.Game.Rulesets.Mania;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Taiko;
+using osu.Game.Users;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public partial class TestSceneLocalUserStatisticsProvider : OsuTestScene
+ {
+ private LocalUserStatisticsProvider statisticsProvider = null!;
+
+ private readonly Dictionary<(int userId, string rulesetName), UserStatistics> serverSideStatistics = new Dictionary<(int userId, string rulesetName), UserStatistics>();
+
+ [SetUpSteps]
+ public void SetUpSteps()
+ {
+ AddStep("clear statistics", () => serverSideStatistics.Clear());
+
+ setUser(1000);
+
+ AddStep("setup provider", () =>
+ {
+ OsuSpriteText text;
+
+ ((DummyAPIAccess)API).HandleRequest = r =>
+ {
+ switch (r)
+ {
+ case GetUserRequest userRequest:
+ int userId = int.Parse(userRequest.Lookup);
+ string rulesetName = userRequest.Ruleset!.ShortName;
+ var response = new APIUser
+ {
+ Id = userId,
+ Statistics = tryGetStatistics(userId, rulesetName)
+ };
+
+ userRequest.TriggerSuccess(response);
+ return true;
+
+ default:
+ return false;
+ }
+ };
+
+ Clear();
+ Add(statisticsProvider = new LocalUserStatisticsProvider());
+ Add(text = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ });
+
+ statisticsProvider.Statistics.BindValueChanged(s =>
+ {
+ text.Text = s.NewValue == null
+ ? "Statistics: (null)"
+ : $"Statistics: (total score: {s.NewValue.TotalScore:N0})";
+ });
+
+ Ruleset.Value = new OsuRuleset().RulesetInfo;
+ });
+ }
+
+ [Test]
+ public void TestInitialStatistics()
+ {
+ AddAssert("initial statistics populated", () => statisticsProvider.Statistics.Value.AsNonNull().TotalScore, () => Is.EqualTo(4_000_000));
+ }
+
+ [Test]
+ public void TestRulesetChanges()
+ {
+ AddAssert("statistics from osu", () => statisticsProvider.Statistics.Value.AsNonNull().TotalScore, () => Is.EqualTo(4_000_000));
+ AddStep("change ruleset to taiko", () => Ruleset.Value = new TaikoRuleset().RulesetInfo);
+ AddAssert("statistics from taiko", () => statisticsProvider.Statistics.Value.AsNonNull().TotalScore, () => Is.EqualTo(3_000_000));
+ AddStep("change ruleset to catch", () => Ruleset.Value = new CatchRuleset().RulesetInfo);
+ AddAssert("statistics from catch", () => statisticsProvider.Statistics.Value.AsNonNull().TotalScore, () => Is.EqualTo(2_000_000));
+ AddStep("change ruleset to mania", () => Ruleset.Value = new ManiaRuleset().RulesetInfo);
+ AddAssert("statistics from mania", () => statisticsProvider.Statistics.Value.AsNonNull().TotalScore, () => Is.EqualTo(1_000_000));
+ }
+
+ [Test]
+ public void TestUserChanges()
+ {
+ setUser(1001);
+
+ AddStep("update statistics for user 1000", () =>
+ {
+ serverSideStatistics[(1000, "osu")] = new UserStatistics { TotalScore = 5_000_000 };
+ serverSideStatistics[(1000, "taiko")] = new UserStatistics { TotalScore = 6_000_000 };
+ });
+
+ AddAssert("statistics matches user 1001 from osu", () => statisticsProvider.Statistics.Value.AsNonNull().TotalScore, () => Is.EqualTo(4_000_000));
+
+ AddStep("change ruleset to taiko", () => Ruleset.Value = new TaikoRuleset().RulesetInfo);
+ AddAssert("statistics matches user 1001 from taiko", () => statisticsProvider.Statistics.Value.AsNonNull().TotalScore, () => Is.EqualTo(3_000_000));
+
+ AddStep("change ruleset to osu", () => Ruleset.Value = new OsuRuleset().RulesetInfo);
+ setUser(1000, false);
+
+ AddAssert("statistics matches user 1000 from osu", () => statisticsProvider.Statistics.Value.AsNonNull().TotalScore, () => Is.EqualTo(5_000_000));
+
+ AddStep("change ruleset to osu", () => Ruleset.Value = new TaikoRuleset().RulesetInfo);
+ AddAssert("statistics matches user 1000 from taiko", () => statisticsProvider.Statistics.Value.AsNonNull().TotalScore, () => Is.EqualTo(6_000_000));
+ }
+
+ private UserStatistics tryGetStatistics(int userId, string rulesetName)
+ => serverSideStatistics.TryGetValue((userId, rulesetName), out var stats) ? stats : new UserStatistics();
+
+ private void setUser(int userId, bool generateStatistics = true)
+ {
+ AddStep($"set local user to {userId}", () =>
+ {
+ if (generateStatistics)
+ {
+ serverSideStatistics[(userId, "osu")] = new UserStatistics { TotalScore = 4_000_000 };
+ serverSideStatistics[(userId, "taiko")] = new UserStatistics { TotalScore = 3_000_000 };
+ serverSideStatistics[(userId, "fruits")] = new UserStatistics { TotalScore = 2_000_000 };
+ serverSideStatistics[(userId, "mania")] = new UserStatistics { TotalScore = 1_000_000 };
+ }
+
+ ((DummyAPIAccess)API).LocalUser.Value = new APIUser { Id = userId };
+ });
+ }
+ }
+}
diff --git a/osu.Game/Online/LocalUserStatisticsProvider.cs b/osu.Game/Online/LocalUserStatisticsProvider.cs
new file mode 100644
index 0000000000..e2f016b336
--- /dev/null
+++ b/osu.Game/Online/LocalUserStatisticsProvider.cs
@@ -0,0 +1,94 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Game.Extensions;
+using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
+using osu.Game.Rulesets;
+using osu.Game.Users;
+
+namespace osu.Game.Online
+{
+ ///
+ /// A component that is responsible for providing the latest statistics of the logged-in user for the game-wide selected ruleset.
+ ///
+ public partial class LocalUserStatisticsProvider : Component
+ {
+ ///
+ /// The statistics of the logged-in user for the game-wide selected ruleset.
+ ///
+ public IBindable Statistics => statistics;
+
+ private readonly Bindable statistics = new Bindable();
+
+ [Resolved]
+ private IBindable ruleset { get; set; } = null!;
+
+ [Resolved]
+ private IAPIProvider api { get; set; } = null!;
+
+ private readonly Dictionary allStatistics = new Dictionary();
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ statistics.BindValueChanged(v =>
+ {
+ if (api.LocalUser.Value != null && v.NewValue != null)
+ api.LocalUser.Value.Statistics = v.NewValue;
+ });
+
+ ruleset.BindValueChanged(_ => updateStatisticsBindable());
+
+ api.LocalUser.BindValueChanged(_ =>
+ {
+ allStatistics.Clear();
+ updateStatisticsBindable();
+ }, true);
+ }
+
+ private GetUserRequest? currentRequest;
+
+ private void updateStatisticsBindable() => Schedule(() =>
+ {
+ statistics.Value = null;
+
+ if (api.LocalUser.Value == null || api.LocalUser.Value.OnlineID <= 1 || !ruleset.Value.IsLegacyRuleset())
+ {
+ statistics.Value = new UserStatistics();
+ return;
+ }
+
+ if (currentRequest?.CompletionState == APIRequestCompletionState.Waiting)
+ {
+ currentRequest.Cancel();
+ currentRequest = null;
+ }
+
+ if (allStatistics.TryGetValue(ruleset.Value.ShortName, out var existing))
+ statistics.Value = existing;
+ else
+ requestStatistics(ruleset.Value);
+ });
+
+ private void requestStatistics(RulesetInfo ruleset)
+ {
+ currentRequest = new GetUserRequest(api.LocalUser.Value.OnlineID, ruleset);
+ currentRequest.Success += u => statistics.Value = allStatistics[ruleset.ShortName] = u.Statistics;
+ api.Queue(currentRequest);
+ }
+
+ internal void UpdateStatistics(UserStatistics statistics, RulesetInfo statisticsRuleset)
+ {
+ allStatistics[statisticsRuleset.ShortName] = statistics;
+
+ if (statisticsRuleset.ShortName == ruleset.Value.ShortName)
+ updateStatisticsBindable();
+ }
+ }
+}
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index a2a6322665..f574885757 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -208,6 +208,7 @@ namespace osu.Game
private MetadataClient metadataClient;
private SoloStatisticsWatcher soloStatisticsWatcher;
+ private LocalUserStatisticsProvider localUserStatisticsProvider;
private RealmAccess realm;
@@ -328,7 +329,9 @@ namespace osu.Game
dependencies.CacheAs(SpectatorClient = new OnlineSpectatorClient(endpoints));
dependencies.CacheAs(MultiplayerClient = new OnlineMultiplayerClient(endpoints));
dependencies.CacheAs(metadataClient = new OnlineMetadataClient(endpoints));
- dependencies.CacheAs(soloStatisticsWatcher = new SoloStatisticsWatcher());
+
+ dependencies.CacheAs(localUserStatisticsProvider = new LocalUserStatisticsProvider());
+ dependencies.CacheAs(soloStatisticsWatcher = new SoloStatisticsWatcher(localUserStatisticsProvider));
base.Content.Add(new BeatmapOnlineChangeIngest(beatmapUpdater, realm, metadataClient));
@@ -371,6 +374,7 @@ namespace osu.Game
base.Content.Add(SpectatorClient);
base.Content.Add(MultiplayerClient);
base.Content.Add(metadataClient);
+ base.Content.Add(localUserStatisticsProvider);
base.Content.Add(soloStatisticsWatcher);
base.Content.Add(rulesetConfigCache);
From 3ab60b76df0ea520f53a0c2bd68d7821e11e45f7 Mon Sep 17 00:00:00 2001
From: Salman Ahmed
Date: Sun, 11 Feb 2024 07:38:33 +0300
Subject: [PATCH 0064/1255] Remove `IAPIProvider.Statistics` in favour of the
new component
---
.../Online/TestSceneSoloStatisticsWatcher.cs | 8 ++++++--
osu.Game/Online/API/APIAccess.cs | 17 +----------------
osu.Game/Online/API/DummyAPIAccess.cs | 16 ----------------
osu.Game/Online/API/IAPIProvider.cs | 10 ----------
osu.Game/Online/Solo/SoloStatisticsWatcher.cs | 9 ++++++++-
5 files changed, 15 insertions(+), 45 deletions(-)
diff --git a/osu.Game.Tests/Visual/Online/TestSceneSoloStatisticsWatcher.cs b/osu.Game.Tests/Visual/Online/TestSceneSoloStatisticsWatcher.cs
index 3607b37c7e..0e762966d6 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneSoloStatisticsWatcher.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneSoloStatisticsWatcher.cs
@@ -8,6 +8,7 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Testing;
using osu.Game.Models;
+using osu.Game.Online;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
@@ -25,6 +26,7 @@ namespace osu.Game.Tests.Visual.Online
{
protected override bool UseOnlineAPI => false;
+ private LocalUserStatisticsProvider statisticsProvider = null!;
private SoloStatisticsWatcher watcher = null!;
[Resolved]
@@ -109,7 +111,9 @@ namespace osu.Game.Tests.Visual.Online
AddStep("create watcher", () =>
{
- Child = watcher = new SoloStatisticsWatcher();
+ Clear();
+ Add(statisticsProvider = new LocalUserStatisticsProvider());
+ Add(watcher = new SoloStatisticsWatcher(statisticsProvider));
});
}
@@ -289,7 +293,7 @@ namespace osu.Game.Tests.Visual.Online
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, scoreId));
AddUntilStep("update received", () => update != null);
AddAssert("local user values are correct", () => dummyAPI.LocalUser.Value.Statistics.TotalScore, () => Is.EqualTo(5_000_000));
- AddAssert("statistics values are correct", () => dummyAPI.Statistics.Value!.TotalScore, () => Is.EqualTo(5_000_000));
+ AddAssert("statistics values are correct", () => statisticsProvider.Statistics.Value!.TotalScore, () => Is.EqualTo(5_000_000));
}
private int nextUserId = 2000;
diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs
index d3707fe74d..5c3a8e7e92 100644
--- a/osu.Game/Online/API/APIAccess.cs
+++ b/osu.Game/Online/API/APIAccess.cs
@@ -55,7 +55,6 @@ namespace osu.Game.Online.API
public IBindable LocalUser => localUser;
public IBindableList Friends => friends;
public IBindable Activity => activity;
- public IBindable Statistics => statistics;
public INotificationsClient NotificationsClient { get; }
@@ -70,8 +69,6 @@ namespace osu.Game.Online.API
private Bindable configStatus { get; } = new Bindable();
private Bindable localUserStatus { get; } = new Bindable();
- private Bindable statistics { get; } = new Bindable();
-
protected bool HasLogin => authentication.Token.Value != null || (!string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password));
private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource();
@@ -595,21 +592,9 @@ namespace osu.Game.Online.API
flushQueue();
}
- public void UpdateStatistics(UserStatistics newStatistics)
- {
- statistics.Value = newStatistics;
-
- if (IsLoggedIn)
- localUser.Value.Statistics = newStatistics;
- }
-
private static APIUser createGuestUser() => new GuestUser();
- private void setLocalUser(APIUser user) => Scheduler.Add(() =>
- {
- localUser.Value = user;
- statistics.Value = user.Statistics;
- }, false);
+ private void setLocalUser(APIUser user) => Scheduler.Add(() => localUser.Value = user, false);
protected override void Dispose(bool isDisposing)
{
diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs
index 4962838bd9..ca21b15b1f 100644
--- a/osu.Game/Online/API/DummyAPIAccess.cs
+++ b/osu.Game/Online/API/DummyAPIAccess.cs
@@ -30,8 +30,6 @@ namespace osu.Game.Online.API
public Bindable Activity { get; } = new Bindable();
- public Bindable Statistics { get; } = new Bindable();
-
public DummyNotificationsClient NotificationsClient { get; } = new DummyNotificationsClient();
INotificationsClient IAPIProvider.NotificationsClient => NotificationsClient;
@@ -158,11 +156,6 @@ namespace osu.Game.Online.API
private void onSuccessfulLogin()
{
state.Value = APIState.Online;
- Statistics.Value = new UserStatistics
- {
- GlobalRank = 1,
- CountryRank = 1
- };
}
public void Logout()
@@ -173,14 +166,6 @@ namespace osu.Game.Online.API
LocalUser.Value = new GuestUser();
}
- public void UpdateStatistics(UserStatistics newStatistics)
- {
- Statistics.Value = newStatistics;
-
- if (IsLoggedIn)
- LocalUser.Value.Statistics = newStatistics;
- }
-
public IHubClientConnector? GetHubConnector(string clientName, string endpoint, bool preferMessagePack) => null;
public IChatClient GetChatClient() => new TestChatClientConnector(this);
@@ -196,7 +181,6 @@ namespace osu.Game.Online.API
IBindable IAPIProvider.LocalUser => LocalUser;
IBindableList IAPIProvider.Friends => Friends;
IBindable IAPIProvider.Activity => Activity;
- IBindable IAPIProvider.Statistics => Statistics;
///
/// Skip 2FA requirement for next login.
diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs
index 66f124f7c3..c1f2a52d24 100644
--- a/osu.Game/Online/API/IAPIProvider.cs
+++ b/osu.Game/Online/API/IAPIProvider.cs
@@ -29,11 +29,6 @@ namespace osu.Game.Online.API
///
IBindable Activity { get; }
- ///
- /// The current user's online statistics.
- ///
- IBindable Statistics { get; }
-
///
/// The language supplied by this provider to API requests.
///
@@ -123,11 +118,6 @@ namespace osu.Game.Online.API
///
void Logout();
- ///
- /// Sets Statistics bindable.
- ///
- void UpdateStatistics(UserStatistics newStatistics);
-
///
/// Constructs a new . May be null if not supported.
///
diff --git a/osu.Game/Online/Solo/SoloStatisticsWatcher.cs b/osu.Game/Online/Solo/SoloStatisticsWatcher.cs
index 55b27fb364..eb7c385fed 100644
--- a/osu.Game/Online/Solo/SoloStatisticsWatcher.cs
+++ b/osu.Game/Online/Solo/SoloStatisticsWatcher.cs
@@ -22,6 +22,8 @@ namespace osu.Game.Online.Solo
///
public partial class SoloStatisticsWatcher : Component
{
+ private readonly LocalUserStatisticsProvider? statisticsProvider;
+
[Resolved]
private SpectatorClient spectatorClient { get; set; } = null!;
@@ -33,6 +35,11 @@ namespace osu.Game.Online.Solo
private Dictionary? latestStatistics;
+ public SoloStatisticsWatcher(LocalUserStatisticsProvider? statisticsProvider = null)
+ {
+ this.statisticsProvider = statisticsProvider;
+ }
+
protected override void LoadComplete()
{
base.LoadComplete();
@@ -127,7 +134,7 @@ namespace osu.Game.Online.Solo
{
string rulesetName = callback.Score.Ruleset.ShortName;
- api.UpdateStatistics(updatedStatistics);
+ statisticsProvider?.UpdateStatistics(updatedStatistics, callback.Score.Ruleset);
if (latestStatistics == null)
return;
From 633d85431bb0b01cce1d58ad4ac461eb5a9a51fc Mon Sep 17 00:00:00 2001
From: Salman Ahmed
Date: Sun, 11 Feb 2024 08:22:20 +0300
Subject: [PATCH 0065/1255] Update `UserRankPanel` implementation to use new
component
---
.../Visual/Online/TestSceneUserPanel.cs | 15 ++++++-------
osu.Game/Users/UserRankPanel.cs | 21 +++++++++++++------
2 files changed, 23 insertions(+), 13 deletions(-)
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
index 4df34e6244..bb7b83cb97 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
@@ -11,6 +11,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
+using osu.Game.Online;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
using osu.Game.Rulesets;
@@ -32,7 +33,10 @@ namespace osu.Game.Tests.Visual.Online
private TestUserListPanel boundPanel2;
[Cached]
- private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
+ private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
+
+ [Cached]
+ private readonly LocalUserStatisticsProvider statisticsProvider = new LocalUserStatisticsProvider();
[Resolved]
private IRulesetStore rulesetStore { get; set; }
@@ -163,16 +167,13 @@ namespace osu.Game.Tests.Visual.Online
{
AddStep("update statistics", () =>
{
- API.UpdateStatistics(new UserStatistics
+ statisticsProvider.UpdateStatistics(new UserStatistics
{
GlobalRank = RNG.Next(100000),
CountryRank = RNG.Next(100000)
- });
- });
- AddStep("set statistics to empty", () =>
- {
- API.UpdateStatistics(new UserStatistics());
+ }, Ruleset.Value);
});
+ AddStep("set statistics to empty", () => statisticsProvider.UpdateStatistics(new UserStatistics(), Ruleset.Value));
}
private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.InSoloGame(new BeatmapInfo(), rulesetStore.GetRuleset(rulesetId)!);
diff --git a/osu.Game/Users/UserRankPanel.cs b/osu.Game/Users/UserRankPanel.cs
index 84ff3114fc..167c34e4b8 100644
--- a/osu.Game/Users/UserRankPanel.cs
+++ b/osu.Game/Users/UserRankPanel.cs
@@ -7,7 +7,8 @@ using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
-using osu.Game.Online.API;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Online;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.Profile.Header.Components;
using osu.Game.Resources.Localisation.Web;
@@ -24,11 +25,9 @@ namespace osu.Game.Users
private const int padding = 10;
private const int main_content_height = 80;
- [Resolved]
- private IAPIProvider api { get; set; } = null!;
-
private ProfileValueDisplay globalRankDisplay = null!;
private ProfileValueDisplay countryRankDisplay = null!;
+ private LoadingLayer loadingLayer = null!;
private readonly IBindable statistics = new Bindable();
@@ -43,10 +42,19 @@ namespace osu.Game.Users
private void load()
{
BorderColour = ColourProvider?.Light1 ?? Colours.GreyVioletLighter;
+ }
- statistics.BindTo(api.Statistics);
+ [Resolved]
+ private LocalUserStatisticsProvider statisticsProvider { get; set; } = null!;
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ statistics.BindTo(statisticsProvider.Statistics);
statistics.BindValueChanged(stats =>
{
+ loadingLayer.State.Value = stats.NewValue == null ? Visibility.Visible : Visibility.Hidden;
globalRankDisplay.Content = stats.NewValue?.GlobalRank?.ToLocalisableString("\\##,##0") ?? "-";
countryRankDisplay.Content = stats.NewValue?.CountryRank?.ToLocalisableString("\\##,##0") ?? "-";
}, true);
@@ -173,7 +181,8 @@ namespace osu.Game.Users
}
}
}
- }
+ },
+ loadingLayer = new LoadingLayer(true),
}
};
From bc2b7050635a62524ca37a3463a9097739a649c2 Mon Sep 17 00:00:00 2001
From: Salman Ahmed
Date: Sun, 11 Feb 2024 11:16:54 +0300
Subject: [PATCH 0066/1255] Fix `ImportTest.TestOsuGameBase` having null
ruleset
---
osu.Game.Tests/ImportTest.cs | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/osu.Game.Tests/ImportTest.cs b/osu.Game.Tests/ImportTest.cs
index 27b8d3f21e..b1e2730703 100644
--- a/osu.Game.Tests/ImportTest.cs
+++ b/osu.Game.Tests/ImportTest.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
@@ -64,6 +65,10 @@ namespace osu.Game.Tests
// Beatmap must be imported before the collection manager is loaded.
if (withBeatmap)
BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely();
+
+ // the logic for setting the initial ruleset exists in OsuGame rather than OsuGameBase.
+ // the ruleset bindable is not meant to be nullable, so assign any ruleset in here.
+ Ruleset.Value = RulesetStore.AvailableRulesets.First();
}
}
}
From 11b3fa8691d289d83cd8fcbd2940e7968c9ee2a4 Mon Sep 17 00:00:00 2001
From: Salman Ahmed
Date: Sun, 11 Feb 2024 11:39:12 +0300
Subject: [PATCH 0067/1255] Fix `TestSceneUserPanel` tests failing
---
osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
index bb7b83cb97..00072d52c1 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
@@ -47,7 +47,11 @@ namespace osu.Game.Tests.Visual.Online
activity.Value = null;
status.Value = null;
- Child = new FillFlowContainer
+ Remove(statisticsProvider, false);
+ Clear();
+ Add(statisticsProvider);
+
+ Add(new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -113,7 +117,7 @@ namespace osu.Game.Tests.Visual.Online
Statistics = new UserStatistics { GlobalRank = null, CountryRank = null }
}) { Width = 300 }
}
- };
+ });
boundPanel1.Status.BindTo(status);
boundPanel1.Activity.BindTo(activity);
From 7d34542c12a8e1db707d101fc7988e32e9da74d8 Mon Sep 17 00:00:00 2001
From: TextAdventurer12
Date: Thu, 22 Feb 2024 15:14:56 +1300
Subject: [PATCH 0068/1255] use difficulty instead of topstrain
---
osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
index 918e702b45..13d89cf2b8 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
@@ -37,6 +37,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
protected virtual double DifficultyMultiplier => DEFAULT_DIFFICULTY_MULTIPLIER;
protected List objectStrains = new List();
+ protected double difficulty;
protected OsuStrainSkill(Mod[] mods)
: base(mods)
@@ -45,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
public override double DifficultyValue()
{
- double difficulty = 0;
+ difficulty = 0;
double weight = 1;
// Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871).
@@ -78,9 +79,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
///
public double CountDifficultStrains()
{
- double topStrain = objectStrains.Max();
+ double consistentTopStrain = difficulty / 10; // What would the top strain be if all strain values were identical
- return objectStrains.Sum(s => Math.Pow(s / topStrain, 4));
+ return objectStrains.Sum(s => Math.Pow(1, s / consistentTopStrain, 5));
}
}
}
From 0db910deb90b792f41203ee9410ef6dc05231308 Mon Sep 17 00:00:00 2001
From: TextAdventurer12
Date: Thu, 22 Feb 2024 15:20:32 +1300
Subject: [PATCH 0069/1255] cap each note at adding 1 difficult strain count
---
osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
index 13d89cf2b8..84bf8e3bf6 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
@@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
{
double consistentTopStrain = difficulty / 10; // What would the top strain be if all strain values were identical
- return objectStrains.Sum(s => Math.Pow(1, s / consistentTopStrain, 5));
+ return objectStrains.Sum(s => Math.Pow(Math.Min(1, s / consistentTopStrain), 5));
}
}
}
From caba0510db86b66f9e238c640a7a09e272d67fdb Mon Sep 17 00:00:00 2001
From: nathen
Date: Sat, 9 Mar 2024 23:10:53 -0500
Subject: [PATCH 0070/1255] Compute the upper bound on deviation with a 99%
confidence interval
---
.../Difficulty/TaikoPerformanceCalculator.cs | 81 +++++++++----------
1 file changed, 40 insertions(+), 41 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 723a6612bc..322e3874ba 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
countOk = score.Statistics.GetValueOrDefault(HitResult.Ok);
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
- estimatedUr = computeEstimatedUr(score, taikoAttributes);
+ estimatedUr = computeDeviationUpperBound(taikoAttributes) * 10;
// The effectiveMissCount is calculated by gaining a ratio for totalSuccessfulHits and increasing the miss penalty for shorter object counts lower than 1000.
if (totalSuccessfulHits > 0)
@@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (attributes.GreatHitWindow <= 0 || estimatedUr == null)
return 0;
- double accuracyValue = Math.Pow(65 / estimatedUr.Value, 1.1) * Math.Pow(attributes.StarRating, 0.4) * 100.0;
+ double accuracyValue = Math.Pow(70 / estimatedUr.Value, 1.1) * Math.Pow(attributes.StarRating, 0.4) * 100.0;
double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
@@ -115,10 +115,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
}
///
- /// Calculates the tap deviation for a player using the OD, object count, and scores of 300s, 100s, and misses, with an assumed mean hit error of 0.
- /// Consistency is ensured as identical SS scores on the same map and settings yield the same deviation.
+ /// Computes an upper bound on the player's tap deviation based on the OD, number of circles and sliders,
+ /// and the hit judgements, assuming the player's mean hit error is 0. The estimation is consistent in that
+ /// two SS scores on the same map with the same settings will always return the same deviation.
///
- private double? computeEstimatedUr(ScoreInfo score, TaikoDifficultyAttributes attributes)
+ private double? computeDeviationUpperBound(TaikoDifficultyAttributes attributes)
{
if (totalSuccessfulHits == 0 || attributes.GreatHitWindow <= 0)
return null;
@@ -126,53 +127,51 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
double h300 = attributes.GreatHitWindow;
double h100 = attributes.OkHitWindow;
- // Determines the probability of a deviation leading to the score's hit evaluations. The curve's apex represents the most probable deviation.
- double likelihoodGradient(double d)
+ const double z = 2.32634787404; // 99% critical value for the normal distribution (one-tailed).
+
+ // The upper bound on deviation, calculated with the ratio of 300s to 100s, and the great hit window.
+ double? calcDeviationGreatWindow()
{
- if (d <= 0)
- return 0;
+ if (countGreat == 0) return null;
- double p300 = logDiff(0, logPcHit(h300, d));
- double p100 = logDiff(logPcHit(h300, d), logPcHit(h100, d));
- double p0 = logPcHit(h100, d);
+ double n = totalSuccessfulHits;
- double gradient = Math.Exp(
- (countGreat * p300
- + (countOk + 0.5) * p100
- + countMiss * p0) / totalHits
- );
+ // Proportion of greats hit, ignoring misses.
+ double p = countGreat / n;
- return -gradient;
+ // We can be 99% confident that p is at least this value.
+ double pLowerBound = (n * p + z * z / 2) / (n + z * z) - z / (n + z * z) * Math.Sqrt(n * p * (1 - p) + z * z / 4);
+
+ // We can be 99% confident that the deviation is not higher than:
+ return h300 / (Math.Sqrt(2) * SpecialFunctions.ErfInv(pLowerBound));
}
- double deviation = FindMinimum.OfScalarFunction(likelihoodGradient, 30);
+ // The upper bound on deviation, calculated with the ratio of 300s + 100s to misses, and the good hit window.
+ // This will return a lower value than the first method when the number of 100s is high, but the miss count is low.
+ double? calcDeviationGoodWindow()
+ {
+ if (totalSuccessfulHits == 0) return null;
- return deviation * 10;
+ double n = totalHits;
+
+ // Proportion of greats + goods hit.
+ double p = totalSuccessfulHits / n;
+
+ // We can be 99% confident that p is at least this value.
+ double pLowerBound = (n * p + z * z / 2) / (n + z * z) - z / (n + z * z) * Math.Sqrt(n * p * (1 - p) + z * z / 4);
+
+ // We can be 99% confident that the deviation is not higher than:
+ return h100 / (Math.Sqrt(2) * SpecialFunctions.ErfInv(pLowerBound));
+ }
+
+ if (calcDeviationGreatWindow() is null)
+ return calcDeviationGoodWindow();
+
+ return Math.Min(calcDeviationGreatWindow()!.Value, calcDeviationGoodWindow()!.Value);
}
private int totalHits => countGreat + countOk + countMeh + countMiss;
private int totalSuccessfulHits => countGreat + countOk + countMeh;
-
- private double logPcHit(double x, double deviation) => logErfcApprox(x / (deviation * Math.Sqrt(2)));
-
- // Utilises a numerical approximation to extend the computation range of ln(erfc(x)).
- private double logErfcApprox(double x) => x <= 5
- ? Math.Log(SpecialFunctions.Erfc(x))
- : -Math.Pow(x, 2) - Math.Log(x * Math.Sqrt(Math.PI)); // https://www.desmos.com/calculator/kdbxwxgf01
-
- // Subtracts the base value of two logs, circumventing log rules that typically complicate subtraction of non-logarithmic values.
- private double logDiff(double firstLog, double secondLog)
- {
- double maxVal = Math.Max(firstLog, secondLog);
-
- // To avoid a NaN result, a check is performed to prevent subtraction of two negative infinity values.
- if (double.IsNegativeInfinity(maxVal))
- {
- return maxVal;
- }
-
- return firstLog + SpecialFunctions.Log1p(-Math.Exp(-(firstLog - secondLog)));
- }
}
}
From 6ddb2b7f8b1a40f3fc86892b00c67486884ac025 Mon Sep 17 00:00:00 2001
From: nathen
Date: Sun, 10 Mar 2024 00:19:04 -0500
Subject: [PATCH 0071/1255] Include misses in the great window deviation calc
---
.../Difficulty/TaikoPerformanceCalculator.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 322e3874ba..8998b5cfa3 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -129,12 +129,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
const double z = 2.32634787404; // 99% critical value for the normal distribution (one-tailed).
- // The upper bound on deviation, calculated with the ratio of 300s to 100s, and the great hit window.
+ // The upper bound on deviation, calculated with the ratio of 300s to objects, and the great hit window.
double? calcDeviationGreatWindow()
{
if (countGreat == 0) return null;
- double n = totalSuccessfulHits;
+ double n = totalHits;
// Proportion of greats hit, ignoring misses.
double p = countGreat / n;
@@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
return h300 / (Math.Sqrt(2) * SpecialFunctions.ErfInv(pLowerBound));
}
- // The upper bound on deviation, calculated with the ratio of 300s + 100s to misses, and the good hit window.
+ // The upper bound on deviation, calculated with the ratio of 300s + 100s to objects, and the good hit window.
// This will return a lower value than the first method when the number of 100s is high, but the miss count is low.
double? calcDeviationGoodWindow()
{
From 537059504aa12c4dbc4a790c8dc24603ccdd94f6 Mon Sep 17 00:00:00 2001
From: nathen
Date: Sun, 10 Mar 2024 00:20:06 -0500
Subject: [PATCH 0072/1255] Fix comment
---
.../Difficulty/TaikoPerformanceCalculator.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 8998b5cfa3..c532d24f4d 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
double n = totalHits;
- // Proportion of greats hit, ignoring misses.
+ // Proportion of greats hit.
double p = countGreat / n;
// We can be 99% confident that p is at least this value.
From a9b3416a3fe2e77f66c249c85636ca476ac3027c Mon Sep 17 00:00:00 2001
From: nathen
Date: Sun, 10 Mar 2024 00:37:28 -0500
Subject: [PATCH 0073/1255] Remove MathNet.Numerics dependency
---
.../Difficulty/TaikoPerformanceCalculator.cs | 2 +-
.../osu.Game.Rulesets.Taiko.csproj | 4 -
osu.Game/Utils/SpecialFunctions.cs | 694 ++++++++++++++++++
3 files changed, 695 insertions(+), 5 deletions(-)
create mode 100644 osu.Game/Utils/SpecialFunctions.cs
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index c532d24f4d..ca11397801 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -9,7 +9,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Scoring;
-using MathNet.Numerics;
+using osu.Game.Utils;
namespace osu.Game.Rulesets.Taiko.Difficulty
{
diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
index 6ad5425d5b..cacba55c2a 100644
--- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
+++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
@@ -15,8 +15,4 @@
-
-
-
-
diff --git a/osu.Game/Utils/SpecialFunctions.cs b/osu.Game/Utils/SpecialFunctions.cs
new file mode 100644
index 0000000000..0b0f0598bb
--- /dev/null
+++ b/osu.Game/Utils/SpecialFunctions.cs
@@ -0,0 +1,694 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+// All code is referenced from the following:
+// https://github.com/mathnet/mathnet-numerics/blob/master/src/Numerics/SpecialFunctions/Erf.cs
+// https://github.com/mathnet/mathnet-numerics/blob/master/src/Numerics/Optimization/NelderMeadSimplex.cs
+
+/*
+ Copyright (c) 2002-2022 Math.NET
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+using System;
+
+namespace osu.Game.Utils
+{
+ public class SpecialFunctions
+ {
+ private const double sqrt2_pi = 2.5066282746310005024157652848110452530069867406099d;
+
+ ///
+ /// **************************************
+ /// COEFFICIENTS FOR METHOD ErfImp *
+ /// **************************************
+ ///
+ /// Polynomial coefficients for a numerator of ErfImp
+ /// calculation for Erf(x) in the interval [1e-10, 0.5].
+ ///
+ private static readonly double[] erf_imp_an = { 0.00337916709551257388990745, -0.00073695653048167948530905, -0.374732337392919607868241, 0.0817442448733587196071743, -0.0421089319936548595203468, 0.0070165709512095756344528, -0.00495091255982435110337458, 0.000871646599037922480317225 };
+
+ /// Polynomial coefficients for a denominator of ErfImp
+ /// calculation for Erf(x) in the interval [1e-10, 0.5].
+ ///
+ private static readonly double[] erf_imp_ad = { 1, -0.218088218087924645390535, 0.412542972725442099083918, -0.0841891147873106755410271, 0.0655338856400241519690695, -0.0120019604454941768171266, 0.00408165558926174048329689, -0.000615900721557769691924509 };
+
+ /// Polynomial coefficients for a numerator in ErfImp
+ /// calculation for Erfc(x) in the interval [0.5, 0.75].
+ ///
+ private static readonly double[] erf_imp_bn = { -0.0361790390718262471360258, 0.292251883444882683221149, 0.281447041797604512774415, 0.125610208862766947294894, 0.0274135028268930549240776, 0.00250839672168065762786937 };
+
+ /// Polynomial coefficients for a denominator in ErfImp
+ /// calculation for Erfc(x) in the interval [0.5, 0.75].
+ ///
+ private static readonly double[] erf_imp_bd = { 1, 1.8545005897903486499845, 1.43575803037831418074962, 0.582827658753036572454135, 0.124810476932949746447682, 0.0113724176546353285778481 };
+
+ /// Polynomial coefficients for a numerator in ErfImp
+ /// calculation for Erfc(x) in the interval [0.75, 1.25].
+ ///
+ private static readonly double[] erf_imp_cn = { -0.0397876892611136856954425, 0.153165212467878293257683, 0.191260295600936245503129, 0.10276327061989304213645, 0.029637090615738836726027, 0.0046093486780275489468812, 0.000307607820348680180548455 };
+
+ /// Polynomial coefficients for a denominator in ErfImp
+ /// calculation for Erfc(x) in the interval [0.75, 1.25].
+ ///
+ private static readonly double[] erf_imp_cd = { 1, 1.95520072987627704987886, 1.64762317199384860109595, 0.768238607022126250082483, 0.209793185936509782784315, 0.0319569316899913392596356, 0.00213363160895785378615014 };
+
+ /// Polynomial coefficients for a numerator in ErfImp
+ /// calculation for Erfc(x) in the interval [1.25, 2.25].
+ ///
+ private static readonly double[] erf_imp_dn = { -0.0300838560557949717328341, 0.0538578829844454508530552, 0.0726211541651914182692959, 0.0367628469888049348429018, 0.00964629015572527529605267, 0.00133453480075291076745275, 0.778087599782504251917881e-4 };
+
+ /// Polynomial coefficients for a denominator in ErfImp
+ /// calculation for Erfc(x) in the interval [1.25, 2.25].
+ ///
+ private static readonly double[] erf_imp_dd = { 1, 1.75967098147167528287343, 1.32883571437961120556307, 0.552528596508757581287907, 0.133793056941332861912279, 0.0179509645176280768640766, 0.00104712440019937356634038, -0.106640381820357337177643e-7 };
+
+ /// Polynomial coefficients for a numerator in ErfImp
+ /// calculation for Erfc(x) in the interval [2.25, 3.5].
+ ///
+ private static readonly double[] erf_imp_en = { -0.0117907570137227847827732, 0.014262132090538809896674, 0.0202234435902960820020765, 0.00930668299990432009042239, 0.00213357802422065994322516, 0.00025022987386460102395382, 0.120534912219588189822126e-4 };
+
+ /// Polynomial coefficients for a denominator in ErfImp
+ /// calculation for Erfc(x) in the interval [2.25, 3.5].
+ ///
+ private static readonly double[] erf_imp_ed = { 1, 1.50376225203620482047419, 0.965397786204462896346934, 0.339265230476796681555511, 0.0689740649541569716897427, 0.00771060262491768307365526, 0.000371421101531069302990367 };
+
+ /// Polynomial coefficients for a numerator in ErfImp
+ /// calculation for Erfc(x) in the interval [3.5, 5.25].
+ ///
+ private static readonly double[] erf_imp_fn = { -0.00546954795538729307482955, 0.00404190278731707110245394, 0.0054963369553161170521356, 0.00212616472603945399437862, 0.000394984014495083900689956, 0.365565477064442377259271e-4, 0.135485897109932323253786e-5 };
+
+ /// Polynomial coefficients for a denominator in ErfImp
+ /// calculation for Erfc(x) in the interval [3.5, 5.25].
+ ///
+ private static readonly double[] erf_imp_fd = { 1, 1.21019697773630784832251, 0.620914668221143886601045, 0.173038430661142762569515, 0.0276550813773432047594539, 0.00240625974424309709745382, 0.891811817251336577241006e-4, -0.465528836283382684461025e-11 };
+
+ /// Polynomial coefficients for a numerator in ErfImp
+ /// calculation for Erfc(x) in the interval [5.25, 8].
+ ///
+ private static readonly double[] erf_imp_gn = { -0.00270722535905778347999196, 0.0013187563425029400461378, 0.00119925933261002333923989, 0.00027849619811344664248235, 0.267822988218331849989363e-4, 0.923043672315028197865066e-6 };
+
+ /// Polynomial coefficients for a denominator in ErfImp
+ /// calculation for Erfc(x) in the interval [5.25, 8].
+ ///
+ private static readonly double[] erf_imp_gd = { 1, 0.814632808543141591118279, 0.268901665856299542168425, 0.0449877216103041118694989, 0.00381759663320248459168994, 0.000131571897888596914350697, 0.404815359675764138445257e-11 };
+
+ /// Polynomial coefficients for a numerator in ErfImp
+ /// calculation for Erfc(x) in the interval [8, 11.5].
+ ///
+ private static readonly double[] erf_imp_hn = { -0.00109946720691742196814323, 0.000406425442750422675169153, 0.000274499489416900707787024, 0.465293770646659383436343e-4, 0.320955425395767463401993e-5, 0.778286018145020892261936e-7 };
+
+ /// Polynomial coefficients for a denominator in ErfImp
+ /// calculation for Erfc(x) in the interval [8, 11.5].
+ ///
+ private static readonly double[] erf_imp_hd = { 1, 0.588173710611846046373373, 0.139363331289409746077541, 0.0166329340417083678763028, 0.00100023921310234908642639, 0.24254837521587225125068e-4 };
+
+ /// Polynomial coefficients for a numerator in ErfImp
+ /// calculation for Erfc(x) in the interval [11.5, 17].
+ ///
+ private static readonly double[] erf_imp_in = { -0.00056907993601094962855594, 0.000169498540373762264416984, 0.518472354581100890120501e-4, 0.382819312231928859704678e-5, 0.824989931281894431781794e-7 };
+
+ /// Polynomial coefficients for a denominator in ErfImp
+ /// calculation for Erfc(x) in the interval [11.5, 17].
+ ///
+ private static readonly double[] erf_imp_id = { 1, 0.339637250051139347430323, 0.043472647870310663055044, 0.00248549335224637114641629, 0.535633305337152900549536e-4, -0.117490944405459578783846e-12 };
+
+ /// Polynomial coefficients for a numerator in ErfImp
+ /// calculation for Erfc(x) in the interval [17, 24].
+ ///
+ private static readonly double[] erf_imp_jn = { -0.000241313599483991337479091, 0.574224975202501512365975e-4, 0.115998962927383778460557e-4, 0.581762134402593739370875e-6, 0.853971555085673614607418e-8 };
+
+ /// Polynomial coefficients for a denominator in ErfImp
+ /// calculation for Erfc(x) in the interval [17, 24].
+ ///
+ private static readonly double[] erf_imp_jd = { 1, 0.233044138299687841018015, 0.0204186940546440312625597, 0.000797185647564398289151125, 0.117019281670172327758019e-4 };
+
+ /// Polynomial coefficients for a numerator in ErfImp
+ /// calculation for Erfc(x) in the interval [24, 38].
+ ///
+ private static readonly double[] erf_imp_kn = { -0.000146674699277760365803642, 0.162666552112280519955647e-4, 0.269116248509165239294897e-5, 0.979584479468091935086972e-7, 0.101994647625723465722285e-8 };
+
+ /// Polynomial coefficients for a denominator in ErfImp
+ /// calculation for Erfc(x) in the interval [24, 38].
+ ///
+ private static readonly double[] erf_imp_kd = { 1, 0.165907812944847226546036, 0.0103361716191505884359634, 0.000286593026373868366935721, 0.298401570840900340874568e-5 };
+
+ /// Polynomial coefficients for a numerator in ErfImp
+ /// calculation for Erfc(x) in the interval [38, 60].
+ ///
+ private static readonly double[] erf_imp_ln = { -0.583905797629771786720406e-4, 0.412510325105496173512992e-5, 0.431790922420250949096906e-6, 0.993365155590013193345569e-8, 0.653480510020104699270084e-10 };
+
+ /// Polynomial coefficients for a denominator in ErfImp
+ /// calculation for Erfc(x) in the interval [38, 60].
+ ///
+ private static readonly double[] erf_imp_ld = { 1, 0.105077086072039915406159, 0.00414278428675475620830226, 0.726338754644523769144108e-4, 0.477818471047398785369849e-6 };
+
+ /// Polynomial coefficients for a numerator in ErfImp
+ /// calculation for Erfc(x) in the interval [60, 85].
+ ///
+ private static readonly double[] erf_imp_mn = { -0.196457797609229579459841e-4, 0.157243887666800692441195e-5, 0.543902511192700878690335e-7, 0.317472492369117710852685e-9 };
+
+ /// Polynomial coefficients for a denominator in ErfImp
+ /// calculation for Erfc(x) in the interval [60, 85].
+ ///
+ private static readonly double[] erf_imp_md = { 1, 0.052803989240957632204885, 0.000926876069151753290378112, 0.541011723226630257077328e-5, 0.535093845803642394908747e-15 };
+
+ /// Polynomial coefficients for a numerator in ErfImp
+ /// calculation for Erfc(x) in the interval [85, 110].
+ ///
+ private static readonly double[] erf_imp_nn = { -0.789224703978722689089794e-5, 0.622088451660986955124162e-6, 0.145728445676882396797184e-7, 0.603715505542715364529243e-10 };
+
+ /// Polynomial coefficients for a denominator in ErfImp
+ /// calculation for Erfc(x) in the interval [85, 110].
+ ///
+ private static readonly double[] erf_imp_nd = { 1, 0.0375328846356293715248719, 0.000467919535974625308126054, 0.193847039275845656900547e-5 };
+
+ ///
+ /// **************************************
+ /// COEFFICIENTS FOR METHOD ErfInvImp *
+ /// **************************************
+ ///
+ /// Polynomial coefficients for a numerator of ErfInvImp
+ /// calculation for Erf^-1(z) in the interval [0, 0.5].
+ ///
+ private static readonly double[] erv_inv_imp_an = { -0.000508781949658280665617, -0.00836874819741736770379, 0.0334806625409744615033, -0.0126926147662974029034, -0.0365637971411762664006, 0.0219878681111168899165, 0.00822687874676915743155, -0.00538772965071242932965 };
+
+ /// Polynomial coefficients for a denominator of ErfInvImp
+ /// calculation for Erf^-1(z) in the interval [0, 0.5].
+ ///
+ private static readonly double[] erv_inv_imp_ad = { 1, -0.970005043303290640362, -1.56574558234175846809, 1.56221558398423026363, 0.662328840472002992063, -0.71228902341542847553, -0.0527396382340099713954, 0.0795283687341571680018, -0.00233393759374190016776, 0.000886216390456424707504 };
+
+ /// Polynomial coefficients for a numerator of ErfInvImp
+ /// calculation for Erf^-1(z) in the interval [0.5, 0.75].
+ ///
+ private static readonly double[] erv_inv_imp_bn = { -0.202433508355938759655, 0.105264680699391713268, 8.37050328343119927838, 17.6447298408374015486, -18.8510648058714251895, -44.6382324441786960818, 17.445385985570866523, 21.1294655448340526258, -3.67192254707729348546 };
+
+ /// Polynomial coefficients for a denominator of ErfInvImp
+ /// calculation for Erf^-1(z) in the interval [0.5, 0.75].
+ ///
+ private static readonly double[] erv_inv_imp_bd = { 1, 6.24264124854247537712, 3.9713437953343869095, -28.6608180499800029974, -20.1432634680485188801, 48.5609213108739935468, 10.8268667355460159008, -22.6436933413139721736, 1.72114765761200282724 };
+
+ /// Polynomial coefficients for a numerator of ErfInvImp
+ /// calculation for Erf^-1(z) in the interval [0.75, 1] with x less than 3.
+ ///
+ private static readonly double[] erv_inv_imp_cn = { -0.131102781679951906451, -0.163794047193317060787, 0.117030156341995252019, 0.387079738972604337464, 0.337785538912035898924, 0.142869534408157156766, 0.0290157910005329060432, 0.00214558995388805277169, -0.679465575181126350155e-6, 0.285225331782217055858e-7, -0.681149956853776992068e-9 };
+
+ /// Polynomial coefficients for a denominator of ErfInvImp
+ /// calculation for Erf^-1(z) in the interval [0.75, 1] with x less than 3.
+ ///
+ private static readonly double[] erv_inv_imp_cd = { 1, 3.46625407242567245975, 5.38168345707006855425, 4.77846592945843778382, 2.59301921623620271374, 0.848854343457902036425, 0.152264338295331783612, 0.01105924229346489121 };
+
+ /// Polynomial coefficients for a numerator of ErfInvImp
+ /// calculation for Erf^-1(z) in the interval [0.75, 1] with x between 3 and 6.
+ ///
+ private static readonly double[] erv_inv_imp_dn = { -0.0350353787183177984712, -0.00222426529213447927281, 0.0185573306514231072324, 0.00950804701325919603619, 0.00187123492819559223345, 0.000157544617424960554631, 0.460469890584317994083e-5, -0.230404776911882601748e-9, 0.266339227425782031962e-11 };
+
+ /// Polynomial coefficients for a denominator of ErfInvImp
+ /// calculation for Erf^-1(z) in the interval [0.75, 1] with x between 3 and 6.
+ ///
+ private static readonly double[] erv_inv_imp_dd = { 1, 1.3653349817554063097, 0.762059164553623404043, 0.220091105764131249824, 0.0341589143670947727934, 0.00263861676657015992959, 0.764675292302794483503e-4 };
+
+ /// Polynomial coefficients for a numerator of ErfInvImp
+ /// calculation for Erf^-1(z) in the interval [0.75, 1] with x between 6 and 18.
+ ///
+ private static readonly double[] erv_inv_imp_en = { -0.0167431005076633737133, -0.00112951438745580278863, 0.00105628862152492910091, 0.000209386317487588078668, 0.149624783758342370182e-4, 0.449696789927706453732e-6, 0.462596163522878599135e-8, -0.281128735628831791805e-13, 0.99055709973310326855e-16 };
+
+ /// Polynomial coefficients for a denominator of ErfInvImp
+ /// calculation for Erf^-1(z) in the interval [0.75, 1] with x between 6 and 18.
+ ///
+ private static readonly double[] erv_inv_imp_ed = { 1, 0.591429344886417493481, 0.138151865749083321638, 0.0160746087093676504695, 0.000964011807005165528527, 0.275335474764726041141e-4, 0.282243172016108031869e-6 };
+
+ /// Polynomial coefficients for a numerator of ErfInvImp
+ /// calculation for Erf^-1(z) in the interval [0.75, 1] with x between 18 and 44.
+ ///
+ private static readonly double[] erv_inv_imp_fn = { -0.0024978212791898131227, -0.779190719229053954292e-5, 0.254723037413027451751e-4, 0.162397777342510920873e-5, 0.396341011304801168516e-7, 0.411632831190944208473e-9, 0.145596286718675035587e-11, -0.116765012397184275695e-17 };
+
+ /// Polynomial coefficients for a denominator of ErfInvImp
+ /// calculation for Erf^-1(z) in the interval [0.75, 1] with x between 18 and 44.
+ ///
+ private static readonly double[] erv_inv_imp_fd = { 1, 0.207123112214422517181, 0.0169410838120975906478, 0.000690538265622684595676, 0.145007359818232637924e-4, 0.144437756628144157666e-6, 0.509761276599778486139e-9 };
+
+ /// Polynomial coefficients for a numerator of ErfInvImp
+ /// calculation for Erf^-1(z) in the interval [0.75, 1] with x greater than 44.
+ ///
+ private static readonly double[] erv_inv_imp_gn = { -0.000539042911019078575891, -0.28398759004727721098e-6, 0.899465114892291446442e-6, 0.229345859265920864296e-7, 0.225561444863500149219e-9, 0.947846627503022684216e-12, 0.135880130108924861008e-14, -0.348890393399948882918e-21 };
+
+ /// Polynomial coefficients for a denominator of ErfInvImp
+ /// calculation for Erf^-1(z) in the interval [0.75, 1] with x greater than 44.
+ ///
+ private static readonly double[] erv_inv_imp_gd = { 1, 0.0845746234001899436914, 0.00282092984726264681981, 0.468292921940894236786e-4, 0.399968812193862100054e-6, 0.161809290887904476097e-8, 0.231558608310259605225e-11 };
+
+ /// Calculates the error function.
+ /// The value to evaluate.
+ /// the error function evaluated at given value.
+ ///
+ ///
+ /// - returns 1 if x == double.PositiveInfinity.
+ /// - returns -1 if x == double.NegativeInfinity.
+ ///
+ ///
+ public static double Erf(double x)
+ {
+ if (x == 0)
+ {
+ return 0;
+ }
+
+ if (double.IsPositiveInfinity(x))
+ {
+ return 1;
+ }
+
+ if (double.IsNegativeInfinity(x))
+ {
+ return -1;
+ }
+
+ if (double.IsNaN(x))
+ {
+ return double.NaN;
+ }
+
+ return erfImp(x, false);
+ }
+
+ /// Calculates the complementary error function.
+ /// The value to evaluate.
+ /// the complementary error function evaluated at given value.
+ ///
+ ///
+ /// - returns 0 if x == double.PositiveInfinity.
+ /// - returns 2 if x == double.NegativeInfinity.
+ ///
+ ///
+ public static double Erfc(double x)
+ {
+ if (x == 0)
+ {
+ return 1;
+ }
+
+ if (double.IsPositiveInfinity(x))
+ {
+ return 0;
+ }
+
+ if (double.IsNegativeInfinity(x))
+ {
+ return 2;
+ }
+
+ if (double.IsNaN(x))
+ {
+ return double.NaN;
+ }
+
+ return erfImp(x, true);
+ }
+
+ /// Calculates the inverse error function evaluated at z.
+ /// The inverse error function evaluated at given value.
+ ///
+ ///
+ /// - returns double.PositiveInfinity if z >= 1.0.
+ /// - returns double.NegativeInfinity if z <= -1.0.
+ ///
+ ///
+ /// Calculates the inverse error function evaluated at z.
+ /// value to evaluate.
+ /// the inverse error function evaluated at Z.
+ public static double ErfInv(double z)
+ {
+ if (z == 0.0)
+ {
+ return 0.0;
+ }
+
+ if (z >= 1.0)
+ {
+ return double.PositiveInfinity;
+ }
+
+ if (z <= -1.0)
+ {
+ return double.NegativeInfinity;
+ }
+
+ double p, q, s;
+
+ if (z < 0)
+ {
+ p = -z;
+ q = 1 - p;
+ s = -1;
+ }
+ else
+ {
+ p = z;
+ q = 1 - z;
+ s = 1;
+ }
+
+ return erfInvImpl(p, q, s);
+ }
+
+ ///
+ /// Implementation of the error function.
+ ///
+ /// Where to evaluate the error function.
+ /// Whether to compute 1 - the error function.
+ /// the error function.
+ private static double erfImp(double z, bool invert)
+ {
+ if (z < 0)
+ {
+ if (!invert)
+ {
+ return -erfImp(-z, false);
+ }
+
+ if (z < -0.5)
+ {
+ return 2 - erfImp(-z, true);
+ }
+
+ return 1 + erfImp(-z, false);
+ }
+
+ double result;
+
+ // Big bunch of selection statements now to pick which
+ // implementation to use, try to put most likely options
+ // first:
+ if (z < 0.5)
+ {
+ // We're going to calculate erf:
+ if (z < 1e-10)
+ {
+ result = (z * 1.125) + (z * 0.003379167095512573896158903121545171688);
+ }
+ else
+ {
+ // Worst case absolute error found: 6.688618532e-21
+ result = (z * 1.125) + (z * evaluatePolynomial(z, erf_imp_an) / evaluatePolynomial(z, erf_imp_ad));
+ }
+ }
+ else if (z < 110)
+ {
+ // We'll be calculating erfc:
+ invert = !invert;
+ double r, b;
+
+ if (z < 0.75)
+ {
+ // Worst case absolute error found: 5.582813374e-21
+ r = evaluatePolynomial(z - 0.5, erf_imp_bn) / evaluatePolynomial(z - 0.5, erf_imp_bd);
+ b = 0.3440242112F;
+ }
+ else if (z < 1.25)
+ {
+ // Worst case absolute error found: 4.01854729e-21
+ r = evaluatePolynomial(z - 0.75, erf_imp_cn) / evaluatePolynomial(z - 0.75, erf_imp_cd);
+ b = 0.419990927F;
+ }
+ else if (z < 2.25)
+ {
+ // Worst case absolute error found: 2.866005373e-21
+ r = evaluatePolynomial(z - 1.25, erf_imp_dn) / evaluatePolynomial(z - 1.25, erf_imp_dd);
+ b = 0.4898625016F;
+ }
+ else if (z < 3.5)
+ {
+ // Worst case absolute error found: 1.045355789e-21
+ r = evaluatePolynomial(z - 2.25, erf_imp_en) / evaluatePolynomial(z - 2.25, erf_imp_ed);
+ b = 0.5317370892F;
+ }
+ else if (z < 5.25)
+ {
+ // Worst case absolute error found: 8.300028706e-22
+ r = evaluatePolynomial(z - 3.5, erf_imp_fn) / evaluatePolynomial(z - 3.5, erf_imp_fd);
+ b = 0.5489973426F;
+ }
+ else if (z < 8)
+ {
+ // Worst case absolute error found: 1.700157534e-21
+ r = evaluatePolynomial(z - 5.25, erf_imp_gn) / evaluatePolynomial(z - 5.25, erf_imp_gd);
+ b = 0.5571740866F;
+ }
+ else if (z < 11.5)
+ {
+ // Worst case absolute error found: 3.002278011e-22
+ r = evaluatePolynomial(z - 8, erf_imp_hn) / evaluatePolynomial(z - 8, erf_imp_hd);
+ b = 0.5609807968F;
+ }
+ else if (z < 17)
+ {
+ // Worst case absolute error found: 6.741114695e-21
+ r = evaluatePolynomial(z - 11.5, erf_imp_in) / evaluatePolynomial(z - 11.5, erf_imp_id);
+ b = 0.5626493692F;
+ }
+ else if (z < 24)
+ {
+ // Worst case absolute error found: 7.802346984e-22
+ r = evaluatePolynomial(z - 17, erf_imp_jn) / evaluatePolynomial(z - 17, erf_imp_jd);
+ b = 0.5634598136F;
+ }
+ else if (z < 38)
+ {
+ // Worst case absolute error found: 2.414228989e-22
+ r = evaluatePolynomial(z - 24, erf_imp_kn) / evaluatePolynomial(z - 24, erf_imp_kd);
+ b = 0.5638477802F;
+ }
+ else if (z < 60)
+ {
+ // Worst case absolute error found: 5.896543869e-24
+ r = evaluatePolynomial(z - 38, erf_imp_ln) / evaluatePolynomial(z - 38, erf_imp_ld);
+ b = 0.5640528202F;
+ }
+ else if (z < 85)
+ {
+ // Worst case absolute error found: 3.080612264e-21
+ r = evaluatePolynomial(z - 60, erf_imp_mn) / evaluatePolynomial(z - 60, erf_imp_md);
+ b = 0.5641309023F;
+ }
+ else
+ {
+ // Worst case absolute error found: 8.094633491e-22
+ r = evaluatePolynomial(z - 85, erf_imp_nn) / evaluatePolynomial(z - 85, erf_imp_nd);
+ b = 0.5641584396F;
+ }
+
+ double g = Math.Exp(-z * z) / z;
+ result = (g * b) + (g * r);
+ }
+ else
+ {
+ // Any value of z larger than 28 will underflow to zero:
+ result = 0;
+ invert = !invert;
+ }
+
+ if (invert)
+ {
+ result = 1 - result;
+ }
+
+ return result;
+ }
+
+ /// Calculates the complementary inverse error function evaluated at z.
+ /// The complementary inverse error function evaluated at given value.
+ /// We have tested this implementation against the arbitrary precision mpmath library
+ /// and found cases where we can only guarantee 9 significant figures correct.
+ ///
+ /// - returns double.PositiveInfinity if z <= 0.0.
+ /// - returns double.NegativeInfinity if z >= 2.0.
+ ///
+ ///
+ /// calculates the complementary inverse error function evaluated at z.
+ /// value to evaluate.
+ /// the complementary inverse error function evaluated at Z.
+ public static double ErfcInv(double z)
+ {
+ if (z <= 0.0)
+ {
+ return double.PositiveInfinity;
+ }
+
+ if (z >= 2.0)
+ {
+ return double.NegativeInfinity;
+ }
+
+ double p, q, s;
+
+ if (z > 1)
+ {
+ q = 2 - z;
+ p = 1 - q;
+ s = -1;
+ }
+ else
+ {
+ p = 1 - z;
+ q = z;
+ s = 1;
+ }
+
+ return erfInvImpl(p, q, s);
+ }
+
+ ///
+ /// The implementation of the inverse error function.
+ ///
+ /// First intermediate parameter.
+ /// Second intermediate parameter.
+ /// Third intermediate parameter.
+ /// the inverse error function.
+ private static double erfInvImpl(double p, double q, double s)
+ {
+ double result;
+
+ if (p <= 0.5)
+ {
+ // Evaluate inverse erf using the rational approximation:
+ //
+ // x = p(p+10)(Y+R(p))
+ //
+ // Where Y is a constant, and R(p) is optimized for a low
+ // absolute error compared to |Y|.
+ //
+ // double: Max error found: 2.001849e-18
+ // long double: Max error found: 1.017064e-20
+ // Maximum Deviation Found (actual error term at infinite precision) 8.030e-21
+ const float y = 0.0891314744949340820313f;
+ double g = p * (p + 10);
+ double r = evaluatePolynomial(p, erv_inv_imp_an) / evaluatePolynomial(p, erv_inv_imp_ad);
+ result = (g * y) + (g * r);
+ }
+ else if (q >= 0.25)
+ {
+ // Rational approximation for 0.5 > q >= 0.25
+ //
+ // x = sqrt(-2*log(q)) / (Y + R(q))
+ //
+ // Where Y is a constant, and R(q) is optimized for a low
+ // absolute error compared to Y.
+ //
+ // double : Max error found: 7.403372e-17
+ // long double : Max error found: 6.084616e-20
+ // Maximum Deviation Found (error term) 4.811e-20
+ const float y = 2.249481201171875f;
+ double g = Math.Sqrt(-2 * Math.Log(q));
+ double xs = q - 0.25;
+ double r = evaluatePolynomial(xs, erv_inv_imp_bn) / evaluatePolynomial(xs, erv_inv_imp_bd);
+ result = g / (y + r);
+ }
+ else
+ {
+ // For q < 0.25 we have a series of rational approximations all
+ // of the general form:
+ //
+ // let: x = sqrt(-log(q))
+ //
+ // Then the result is given by:
+ //
+ // x(Y+R(x-B))
+ //
+ // where Y is a constant, B is the lowest value of x for which
+ // the approximation is valid, and R(x-B) is optimized for a low
+ // absolute error compared to Y.
+ //
+ // Note that almost all code will really go through the first
+ // or maybe second approximation. After than we're dealing with very
+ // small input values indeed: 80 and 128 bit long double's go all the
+ // way down to ~ 1e-5000 so the "tail" is rather long...
+ double x = Math.Sqrt(-Math.Log(q));
+
+ if (x < 3)
+ {
+ // Max error found: 1.089051e-20
+ const float y = 0.807220458984375f;
+ double xs = x - 1.125;
+ double r = evaluatePolynomial(xs, erv_inv_imp_cn) / evaluatePolynomial(xs, erv_inv_imp_cd);
+ result = (y * x) + (r * x);
+ }
+ else if (x < 6)
+ {
+ // Max error found: 8.389174e-21
+ const float y = 0.93995571136474609375f;
+ double xs = x - 3;
+ double r = evaluatePolynomial(xs, erv_inv_imp_dn) / evaluatePolynomial(xs, erv_inv_imp_dd);
+ result = (y * x) + (r * x);
+ }
+ else if (x < 18)
+ {
+ // Max error found: 1.481312e-19
+ const float y = 0.98362827301025390625f;
+ double xs = x - 6;
+ double r = evaluatePolynomial(xs, erv_inv_imp_en) / evaluatePolynomial(xs, erv_inv_imp_ed);
+ result = (y * x) + (r * x);
+ }
+ else if (x < 44)
+ {
+ // Max error found: 5.697761e-20
+ const float y = 0.99714565277099609375f;
+ double xs = x - 18;
+ double r = evaluatePolynomial(xs, erv_inv_imp_fn) / evaluatePolynomial(xs, erv_inv_imp_fd);
+ result = (y * x) + (r * x);
+ }
+ else
+ {
+ // Max error found: 1.279746e-20
+ const float y = 0.99941349029541015625f;
+ double xs = x - 44;
+ double r = evaluatePolynomial(xs, erv_inv_imp_gn) / evaluatePolynomial(xs, erv_inv_imp_gd);
+ result = (y * x) + (r * x);
+ }
+ }
+
+ return s * result;
+ }
+
+ ///
+ /// Evaluate a polynomial at point x.
+ /// Coefficients are ordered ascending by power with power k at index k.
+ /// Example: coefficients [3,-1,2] represent y=2x^2-x+3.
+ ///
+ /// The location where to evaluate the polynomial at.
+ /// The coefficients of the polynomial, coefficient for power k at index k.
+ ///
+ /// is a null reference.
+ ///
+ private static double evaluatePolynomial(double z, params double[] coefficients)
+ {
+ // 2020-10-07 jbialogrodzki #730 Since this is public API we should probably
+ // handle null arguments? It doesn't seem to have been done consistently in this class though.
+ if (coefficients == null)
+ {
+ throw new ArgumentNullException(nameof(coefficients));
+ }
+
+ // 2020-10-07 jbialogrodzki #730 Zero polynomials need explicit handling.
+ // Without this check, we attempted to peek coefficients at negative indices!
+ int n = coefficients.Length;
+
+ if (n == 0)
+ {
+ return 0;
+ }
+
+ double sum = coefficients[n - 1];
+
+ for (int i = n - 2; i >= 0; --i)
+ {
+ sum *= z;
+ sum += coefficients[i];
+ }
+
+ return sum;
+ }
+ }
+}
From 941c0487a454fad1447812f2cd12fad26f1cd28c Mon Sep 17 00:00:00 2001
From: Fina
Date: Thu, 21 Mar 2024 19:02:36 -0700
Subject: [PATCH 0074/1255] Make length bonus account for sliders, use proper
misscount for classic
---
.../Difficulty/OsuPerformanceCalculator.cs | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index b31f4ff519..fa5bd8e094 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -23,6 +23,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
private int countMiss;
private double effectiveMissCount;
+ private bool SLIDERS_ACC;
public OsuPerformanceCalculator()
: base(new OsuRuleset())
@@ -33,13 +34,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
var osuAttributes = (OsuDifficultyAttributes)attributes;
+ SLIDERS_ACC = !score.Mods.Any(m => m is OsuModClassic);
+
accuracy = score.Accuracy;
scoreMaxCombo = score.MaxCombo;
countGreat = score.Statistics.GetValueOrDefault(HitResult.Great);
countOk = score.Statistics.GetValueOrDefault(HitResult.Ok);
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
- effectiveMissCount = calculateEffectiveMissCount(osuAttributes);
+ effectiveMissCount = SLIDERS_ACC ? countMiss : calculateEffectiveMissCount(osuAttributes);
double multiplier = PERFORMANCE_BASE_MULTIPLIER;
@@ -191,7 +194,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
// 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;
+ int amountHitObjectsWithAccuracy = SLIDERS_ACC ? attributes.HitCircleCount + attributes.SliderCount : attributes.HitCircleCount;
if (amountHitObjectsWithAccuracy > 0)
betterAccuracyPercentage = ((countGreat - (totalHits - amountHitObjectsWithAccuracy)) * 6 + countOk * 2 + countMeh) / (double)(amountHitObjectsWithAccuracy * 6);
From 4db6f288d3cad1d848d76e11a9f31941169b800d Mon Sep 17 00:00:00 2001
From: Fina
Date: Thu, 21 Mar 2024 23:15:36 -0700
Subject: [PATCH 0075/1255] Use actual sliderends dropped instead of estimating
Score data for non-CL scores includes sliderends dropped, meaning no need to estimate.
CL scores are still estimated.
---
.../Difficulty/OsuPerformanceCalculator.cs | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index b31f4ff519..1e90df3081 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -21,6 +21,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
private int countOk;
private int countMeh;
private int countMiss;
+ private int countSliderEndsDropped;
private double effectiveMissCount;
@@ -39,6 +40,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
countOk = score.Statistics.GetValueOrDefault(HitResult.Ok);
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
+ countSliderEndsDropped = score.Statistics.GetValueOrDefault(HitResult.SmallTickMiss);
effectiveMissCount = calculateEffectiveMissCount(osuAttributes);
double multiplier = PERFORMANCE_BASE_MULTIPLIER;
@@ -123,7 +125,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (attributes.SliderCount > 0)
{
double estimateSliderEndsDropped = Math.Clamp(Math.Min(countOk + countMeh + countMiss, attributes.MaxCombo - scoreMaxCombo), 0, estimateDifficultSliders);
- double sliderNerfFactor = (1 - attributes.SliderFactor) * Math.Pow(1 - estimateSliderEndsDropped / estimateDifficultSliders, 3) + attributes.SliderFactor;
+ double sliderNerfFactor = 0;
+ if (!score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value))
+ sliderNerfFactor = (1 - attributes.SliderFactor) * Math.Pow(1 - countSliderEndsDropped / estimateDifficultSliders, 3) + attributes.SliderFactor;
+ else
+ sliderNerfFactor = (1 - attributes.SliderFactor) * Math.Pow(1 - estimateSliderEndsDropped / estimateDifficultSliders, 3) + attributes.SliderFactor;
aimValue *= sliderNerfFactor;
}
From 3dafdc01bb006b36f1ecea460efa2dd8587f1333 Mon Sep 17 00:00:00 2001
From: Fina
Date: Thu, 21 Mar 2024 23:17:10 -0700
Subject: [PATCH 0076/1255] Revert "Make length bonus account for sliders, use
proper misscount for classic"
This reverts commit 941c0487a454fad1447812f2cd12fad26f1cd28c.
---
.../Difficulty/OsuPerformanceCalculator.cs | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index fa5bd8e094..b31f4ff519 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -23,7 +23,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
private int countMiss;
private double effectiveMissCount;
- private bool SLIDERS_ACC;
public OsuPerformanceCalculator()
: base(new OsuRuleset())
@@ -34,15 +33,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
var osuAttributes = (OsuDifficultyAttributes)attributes;
- SLIDERS_ACC = !score.Mods.Any(m => m is OsuModClassic);
-
accuracy = score.Accuracy;
scoreMaxCombo = score.MaxCombo;
countGreat = score.Statistics.GetValueOrDefault(HitResult.Great);
countOk = score.Statistics.GetValueOrDefault(HitResult.Ok);
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
- effectiveMissCount = SLIDERS_ACC ? countMiss : calculateEffectiveMissCount(osuAttributes);
+ effectiveMissCount = calculateEffectiveMissCount(osuAttributes);
double multiplier = PERFORMANCE_BASE_MULTIPLIER;
@@ -194,7 +191,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
// 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 = SLIDERS_ACC ? attributes.HitCircleCount + attributes.SliderCount : attributes.HitCircleCount;
+ int amountHitObjectsWithAccuracy = attributes.HitCircleCount;
if (amountHitObjectsWithAccuracy > 0)
betterAccuracyPercentage = ((countGreat - (totalHits - amountHitObjectsWithAccuracy)) * 6 + countOk * 2 + countMeh) / (double)(amountHitObjectsWithAccuracy * 6);
From 840845527f68adab6fe0a2f3e28d0ad3af8d863d Mon Sep 17 00:00:00 2001
From: Fina
Date: Thu, 21 Mar 2024 23:24:37 -0700
Subject: [PATCH 0077/1255] Use miss count for effective miss count
No need to estimate misses for non-CL scores.
---
osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index b31f4ff519..e000438e5f 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -39,7 +39,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
countOk = score.Statistics.GetValueOrDefault(HitResult.Ok);
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
- effectiveMissCount = calculateEffectiveMissCount(osuAttributes);
+ if (!score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value))
+ effectiveMissCount = countMiss;
+ else
+ effectiveMissCount = calculateEffectiveMissCount(osuAttributes);
double multiplier = PERFORMANCE_BASE_MULTIPLIER;
From b0d20e68ae303076d4dc644f32af8e98cb62cf98 Mon Sep 17 00:00:00 2001
From: Fina
Date: Thu, 21 Mar 2024 23:31:45 -0700
Subject: [PATCH 0078/1255] Update OsuPerformanceCalculator.cs
---
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 08a09f3929..8fbee5931d 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
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
countSliderEndsDropped = score.Statistics.GetValueOrDefault(HitResult.SmallTickMiss);
-
+
if (!score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value))
effectiveMissCount = countMiss;
else
From 6fe478c8655a2da8979f04e3278b65ea24f58f02 Mon Sep 17 00:00:00 2001
From: Fina
Date: Thu, 21 Mar 2024 23:49:54 -0700
Subject: [PATCH 0079/1255] Add slider ticks and reverse arrows to effective
misscount
Very much open to discussion on if these should be weighed differently
---
osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index 8fbee5931d..94548e4985 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -21,6 +21,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
private int countOk;
private int countMeh;
private int countMiss;
+ private int countLargeTickMiss;
private int countSliderEndsDropped;
private double effectiveMissCount;
@@ -40,10 +41,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
countOk = score.Statistics.GetValueOrDefault(HitResult.Ok);
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
+ countLargeTickMiss = score.Statistics.GetValueOrDefault(HitResult.LargeTickMiss);
countSliderEndsDropped = score.Statistics.GetValueOrDefault(HitResult.SmallTickMiss);
-
if (!score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value))
- effectiveMissCount = countMiss;
+ effectiveMissCount = countMiss + countLargeTickMiss;
else
effectiveMissCount = calculateEffectiveMissCount(osuAttributes);
From 58bc184e0a266ddef4269e89030d96e2e42f38a2 Mon Sep 17 00:00:00 2001
From: Fina
Date: Sat, 23 Mar 2024 14:43:26 -0700
Subject: [PATCH 0080/1255] Use sliderend data for all non-legacy scores
As per suggestion by givikap, I was not aware that non-legacy cl scores stored this data
---
.../Difficulty/OsuPerformanceCalculator.cs | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index 94548e4985..d6b7a17678 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -129,12 +129,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (attributes.SliderCount > 0)
{
- double estimateSliderEndsDropped = Math.Clamp(Math.Min(countOk + countMeh + countMiss, attributes.MaxCombo - scoreMaxCombo), 0, estimateDifficultSliders);
- double sliderNerfFactor = 0;
- if (!score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value))
- sliderNerfFactor = (1 - attributes.SliderFactor) * Math.Pow(1 - countSliderEndsDropped / estimateDifficultSliders, 3) + attributes.SliderFactor;
+ double estimateSliderEndsDropped;
+ if (score.IsLegacyScore)
+ estimateSliderEndsDropped = Math.Clamp(Math.Min(countOk + countMeh + countMiss, attributes.MaxCombo - scoreMaxCombo), 0, estimateDifficultSliders);
else
- sliderNerfFactor = (1 - attributes.SliderFactor) * Math.Pow(1 - estimateSliderEndsDropped / estimateDifficultSliders, 3) + attributes.SliderFactor;
+ estimateSliderEndsDropped = countSliderEndsDropped;
+
+ double sliderNerfFactor = 0;
+ sliderNerfFactor = (1 - attributes.SliderFactor) * Math.Pow(1 - estimateSliderEndsDropped / estimateDifficultSliders, 3) + attributes.SliderFactor;
aimValue *= sliderNerfFactor;
}
From 9f5f6b5d37405787a4308db875331060f54572b4 Mon Sep 17 00:00:00 2001
From: TextAdventurer12
Date: Sat, 6 Apr 2024 21:39:27 +1300
Subject: [PATCH 0081/1255] stop capping difficult strains per note
---
osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
index 84bf8e3bf6..b585a30855 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
@@ -81,7 +81,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
{
double consistentTopStrain = difficulty / 10; // What would the top strain be if all strain values were identical
- return objectStrains.Sum(s => Math.Pow(Math.Min(1, s / consistentTopStrain), 5));
+ // Apply a power to nerf diffspikes, but only apply that power if s / adjustedDifficulty is less than 1, to prevent buffing certain spiky maps
+ return objectStrains.Sum(s => s >= adjustedDifficulty ? s / adjustedDifficulty : Math.Pow(s / adjustedDifficulty, 8));
}
}
-}
+}
\ No newline at end of file
From 2dd49036edaec714641d55475849fcf3f852c452 Mon Sep 17 00:00:00 2001
From: Fina <75299710+Finadoggie@users.noreply.github.com>
Date: Wed, 10 Apr 2024 20:31:52 -0700
Subject: [PATCH 0082/1255] Cap Buzz Slider Related Misses
After letting the comments @Flamiii left brew for a while, I realized they were very much right about the buzz slider thing. As such, I've implemented a quick and dirty untested fix that will hopefully have zero unintended side-effects :)
I don't see this as a permanent or final solution yet. There's definitely some potential issues/inaccuracies that could arise with maps like Notch Hell or IOException's Black Rover, but afaik this implementation would not cause any issues that stable doesn't already have.
---
osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index d6b7a17678..0dd2adaec8 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -44,10 +44,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty
countLargeTickMiss = score.Statistics.GetValueOrDefault(HitResult.LargeTickMiss);
countSliderEndsDropped = score.Statistics.GetValueOrDefault(HitResult.SmallTickMiss);
if (!score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value))
- effectiveMissCount = countMiss + countLargeTickMiss;
+ effectiveMissCount = countMiss + Math.Min(countLargeTickMiss, calculateEffectiveMissCount(osuAttributes) - countMiss); // Cap stuff like buzz-slider related drops to a sane value
else
effectiveMissCount = calculateEffectiveMissCount(osuAttributes);
+
double multiplier = PERFORMANCE_BASE_MULTIPLIER;
if (score.Mods.Any(m => m is OsuModNoFail))
From dd17c898b3037fb5be4996acf4e6a31639674812 Mon Sep 17 00:00:00 2001
From: Fina
Date: Thu, 11 Apr 2024 19:07:48 -0700
Subject: [PATCH 0083/1255] removed large tick misses from effectivemisscount
---
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 0dd2adaec8..830bc47731 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
countLargeTickMiss = score.Statistics.GetValueOrDefault(HitResult.LargeTickMiss);
countSliderEndsDropped = score.Statistics.GetValueOrDefault(HitResult.SmallTickMiss);
if (!score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value))
- effectiveMissCount = countMiss + Math.Min(countLargeTickMiss, calculateEffectiveMissCount(osuAttributes) - countMiss); // Cap stuff like buzz-slider related drops to a sane value
+ effectiveMissCount = countMiss;
else
effectiveMissCount = calculateEffectiveMissCount(osuAttributes);
From b32d73ec9b7be94b3f55a7007e236cffd9228ca1 Mon Sep 17 00:00:00 2001
From: TextAdventurer12
Date: Sat, 13 Apr 2024 02:43:33 +1200
Subject: [PATCH 0084/1255] adjust weighting function
---
osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
index b585a30855..c20ea732ec 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
@@ -80,9 +80,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
public double CountDifficultStrains()
{
double consistentTopStrain = difficulty / 10; // What would the top strain be if all strain values were identical
-
- // Apply a power to nerf diffspikes, but only apply that power if s / adjustedDifficulty is less than 1, to prevent buffing certain spiky maps
- return objectStrains.Sum(s => s >= adjustedDifficulty ? s / adjustedDifficulty : Math.Pow(s / adjustedDifficulty, 8));
+ // Use a weighted sum of all strains. Constants are arbitrary and give nice values
+ return objectStrains.Sum(s => 1.3 / (1 + Math.Exp(-14.15 * (Math.Pow(s / consistentTopStrain, 2) - 0.945))));
}
}
}
\ No newline at end of file
From e2a5d1904b000965f009ee746a3175396011e3fc Mon Sep 17 00:00:00 2001
From: TextAdventurer12
Date: Wed, 17 Apr 2024 01:21:06 +1200
Subject: [PATCH 0085/1255] adjust count difficult strains formula
---
osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
index c20ea732ec..beaf1d1288 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
@@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
{
double consistentTopStrain = difficulty / 10; // What would the top strain be if all strain values were identical
// Use a weighted sum of all strains. Constants are arbitrary and give nice values
- return objectStrains.Sum(s => 1.3 / (1 + Math.Exp(-14.15 * (Math.Pow(s / consistentTopStrain, 2) - 0.945))));
+ return objectStrains.Sum(s => 1.1 / (1 + Math.Exp(-10 * (s / consistentTopStrain - 0.88))));
}
}
}
\ No newline at end of file
From 42b76294db48e604b48ade266444190a29bf1424 Mon Sep 17 00:00:00 2001
From: Dean Herbert
Date: Thu, 18 Apr 2024 09:48:57 +0800
Subject: [PATCH 0086/1255] Update all packages
---
...u.Game.Rulesets.EmptyFreeform.Tests.csproj | 4 ++--
.../osu.Game.Rulesets.Pippidon.Tests.csproj | 4 ++--
....Game.Rulesets.EmptyScrolling.Tests.csproj | 4 ++--
.../osu.Game.Rulesets.Pippidon.Tests.csproj | 4 ++--
.../osu.Game.Benchmarks.csproj | 2 +-
osu.Game/Database/EmptyRealmSet.cs | 2 ++
osu.Game/osu.Game.csproj | 20 +++++++++----------
7 files changed, 21 insertions(+), 19 deletions(-)
diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj
index 7d43eb2b05..c2c91596fa 100644
--- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj
+++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj
@@ -9,8 +9,8 @@
false
-
-
+
+
diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
index 7dc8a1336b..2f56869fc3 100644
--- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
+++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
@@ -9,8 +9,8 @@
false
-
-
+
+
diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj
index 9c4c8217f0..350f8ca6a9 100644
--- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj
+++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj
@@ -9,8 +9,8 @@
false
-
-
+
+
diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
index 7dc8a1336b..2f56869fc3 100644
--- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
+++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
@@ -9,8 +9,8 @@
false
-
-
+
+
diff --git a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj
index af84ee47f1..66027040d3 100644
--- a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj
+++ b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/osu.Game/Database/EmptyRealmSet.cs b/osu.Game/Database/EmptyRealmSet.cs
index 02dfa50fe5..e548d28f68 100644
--- a/osu.Game/Database/EmptyRealmSet.cs
+++ b/osu.Game/Database/EmptyRealmSet.cs
@@ -35,6 +35,8 @@ namespace osu.Game.Database
}
public IRealmCollection Freeze() => throw new NotImplementedException();
+ public IDisposable SubscribeForNotifications(NotificationCallbackDelegate callback, KeyPathsCollection? keyPathCollection = null) => throw new NotImplementedException();
+
public IDisposable SubscribeForNotifications(NotificationCallbackDelegate callback) => throw new NotImplementedException();
public bool IsValid => throw new NotImplementedException();
public Realm Realm => throw new NotImplementedException();
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 21b5bc60a5..7b211cd7ea 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -18,26 +18,26 @@
-
+
-
+
-
-
-
-
-
+
+
+
+
+
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
-
+
From 1bd17d41a99bbc0dcdf9ed46fc9bce78bad8945d Mon Sep 17 00:00:00 2001
From: Dean Herbert
Date: Thu, 18 Apr 2024 09:54:17 +0800
Subject: [PATCH 0087/1255] Remove obsoleted serialisation path from signalr
exceptions
---
osu.Game/Online/Multiplayer/InvalidPasswordException.cs | 6 ------
osu.Game/Online/Multiplayer/InvalidStateChangeException.cs | 6 ------
osu.Game/Online/Multiplayer/InvalidStateException.cs | 6 ------
osu.Game/Online/Multiplayer/NotHostException.cs | 6 ------
osu.Game/Online/Multiplayer/NotJoinedRoomException.cs | 6 ------
osu.Game/Online/Multiplayer/UserBlockedException.cs | 6 ------
osu.Game/Online/Multiplayer/UserBlocksPMsException.cs | 6 ------
7 files changed, 42 deletions(-)
diff --git a/osu.Game/Online/Multiplayer/InvalidPasswordException.cs b/osu.Game/Online/Multiplayer/InvalidPasswordException.cs
index d3da8f491b..8f2543ee1e 100644
--- a/osu.Game/Online/Multiplayer/InvalidPasswordException.cs
+++ b/osu.Game/Online/Multiplayer/InvalidPasswordException.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Runtime.Serialization;
using Microsoft.AspNetCore.SignalR;
namespace osu.Game.Online.Multiplayer
@@ -13,10 +12,5 @@ namespace osu.Game.Online.Multiplayer
public InvalidPasswordException()
{
}
-
- protected InvalidPasswordException(SerializationInfo info, StreamingContext context)
- : base(info, context)
- {
- }
}
}
diff --git a/osu.Game/Online/Multiplayer/InvalidStateChangeException.cs b/osu.Game/Online/Multiplayer/InvalidStateChangeException.cs
index 4c793dba68..2bae31196a 100644
--- a/osu.Game/Online/Multiplayer/InvalidStateChangeException.cs
+++ b/osu.Game/Online/Multiplayer/InvalidStateChangeException.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Runtime.Serialization;
using Microsoft.AspNetCore.SignalR;
namespace osu.Game.Online.Multiplayer
@@ -14,10 +13,5 @@ namespace osu.Game.Online.Multiplayer
: base($"Cannot change from {oldState} to {newState}")
{
}
-
- protected InvalidStateChangeException(SerializationInfo info, StreamingContext context)
- : base(info, context)
- {
- }
}
}
diff --git a/osu.Game/Online/Multiplayer/InvalidStateException.cs b/osu.Game/Online/Multiplayer/InvalidStateException.cs
index 27b111a781..c9705e9e53 100644
--- a/osu.Game/Online/Multiplayer/InvalidStateException.cs
+++ b/osu.Game/Online/Multiplayer/InvalidStateException.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Runtime.Serialization;
using Microsoft.AspNetCore.SignalR;
namespace osu.Game.Online.Multiplayer
@@ -14,10 +13,5 @@ namespace osu.Game.Online.Multiplayer
: base(message)
{
}
-
- protected InvalidStateException(SerializationInfo info, StreamingContext context)
- : base(info, context)
- {
- }
}
}
diff --git a/osu.Game/Online/Multiplayer/NotHostException.cs b/osu.Game/Online/Multiplayer/NotHostException.cs
index cd43b13e52..f4fd217c87 100644
--- a/osu.Game/Online/Multiplayer/NotHostException.cs
+++ b/osu.Game/Online/Multiplayer/NotHostException.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Runtime.Serialization;
using Microsoft.AspNetCore.SignalR;
namespace osu.Game.Online.Multiplayer
@@ -14,10 +13,5 @@ namespace osu.Game.Online.Multiplayer
: base("User is attempting to perform a host level operation while not the host")
{
}
-
- protected NotHostException(SerializationInfo info, StreamingContext context)
- : base(info, context)
- {
- }
}
}
diff --git a/osu.Game/Online/Multiplayer/NotJoinedRoomException.cs b/osu.Game/Online/Multiplayer/NotJoinedRoomException.cs
index 0a96406c16..72773e28db 100644
--- a/osu.Game/Online/Multiplayer/NotJoinedRoomException.cs
+++ b/osu.Game/Online/Multiplayer/NotJoinedRoomException.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Runtime.Serialization;
using Microsoft.AspNetCore.SignalR;
namespace osu.Game.Online.Multiplayer
@@ -14,10 +13,5 @@ namespace osu.Game.Online.Multiplayer
: base("This user has not yet joined a multiplayer room.")
{
}
-
- protected NotJoinedRoomException(SerializationInfo info, StreamingContext context)
- : base(info, context)
- {
- }
}
}
diff --git a/osu.Game/Online/Multiplayer/UserBlockedException.cs b/osu.Game/Online/Multiplayer/UserBlockedException.cs
index e964b13c75..58e86d9f32 100644
--- a/osu.Game/Online/Multiplayer/UserBlockedException.cs
+++ b/osu.Game/Online/Multiplayer/UserBlockedException.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Runtime.Serialization;
using Microsoft.AspNetCore.SignalR;
namespace osu.Game.Online.Multiplayer
@@ -16,10 +15,5 @@ namespace osu.Game.Online.Multiplayer
: base(MESSAGE)
{
}
-
- protected UserBlockedException(SerializationInfo info, StreamingContext context)
- : base(info, context)
- {
- }
}
}
diff --git a/osu.Game/Online/Multiplayer/UserBlocksPMsException.cs b/osu.Game/Online/Multiplayer/UserBlocksPMsException.cs
index 14ed6fc212..0ea583ae2c 100644
--- a/osu.Game/Online/Multiplayer/UserBlocksPMsException.cs
+++ b/osu.Game/Online/Multiplayer/UserBlocksPMsException.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Runtime.Serialization;
using Microsoft.AspNetCore.SignalR;
namespace osu.Game.Online.Multiplayer
@@ -16,10 +15,5 @@ namespace osu.Game.Online.Multiplayer
: base(MESSAGE)
{
}
-
- protected UserBlocksPMsException(SerializationInfo info, StreamingContext context)
- : base(info, context)
- {
- }
}
}
From 5f0af6085120b316beafcdc6c03972e14812d149 Mon Sep 17 00:00:00 2001
From: Dean Herbert
Date: Thu, 18 Apr 2024 09:54:37 +0800
Subject: [PATCH 0088/1255] Update mismatching translation xmldocs
---
.../FirstRunOverlayImportFromStableScreenStrings.cs | 10 ++++------
osu.Game/Localisation/NotificationsStrings.cs | 8 ++++----
2 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/osu.Game/Localisation/FirstRunOverlayImportFromStableScreenStrings.cs b/osu.Game/Localisation/FirstRunOverlayImportFromStableScreenStrings.cs
index 04fecab3df..6293a4f840 100644
--- a/osu.Game/Localisation/FirstRunOverlayImportFromStableScreenStrings.cs
+++ b/osu.Game/Localisation/FirstRunOverlayImportFromStableScreenStrings.cs
@@ -15,10 +15,9 @@ namespace osu.Game.Localisation
public static LocalisableString Header => new TranslatableString(getKey(@"header"), @"Import");
///
- /// "If you have an installation of a previous osu! version, you can choose to migrate your existing content. Note that this will not affect your existing installation's files in any way."
+ /// "If you have an installation of a previous osu! version, you can choose to migrate your existing content. Note that this will not affect your existing installation's files in any way."
///
- public static LocalisableString Description => new TranslatableString(getKey(@"description"),
- @"If you have an installation of a previous osu! version, you can choose to migrate your existing content. Note that this will not affect your existing installation's files in any way.");
+ public static LocalisableString Description => new TranslatableString(getKey(@"description"), @"If you have an installation of a previous osu! version, you can choose to migrate your existing content. Note that this will not affect your existing installation's files in any way.");
///
/// "previous osu! install"
@@ -38,8 +37,7 @@ namespace osu.Game.Localisation
///
/// "Your import will continue in the background. Check on its progress in the notifications sidebar!"
///
- public static LocalisableString ImportInProgress =>
- new TranslatableString(getKey(@"import_in_progress"), @"Your import will continue in the background. Check on its progress in the notifications sidebar!");
+ public static LocalisableString ImportInProgress => new TranslatableString(getKey(@"import_in_progress"), @"Your import will continue in the background. Check on its progress in the notifications sidebar!");
///
/// "calculating..."
@@ -47,7 +45,7 @@ namespace osu.Game.Localisation
public static LocalisableString Calculating => new TranslatableString(getKey(@"calculating"), @"calculating...");
///
- /// "{0} items"
+ /// "{0} item(s)"
///
public static LocalisableString Items(int arg0) => new TranslatableString(getKey(@"items"), @"{0} item(s)", arg0);
diff --git a/osu.Game/Localisation/NotificationsStrings.cs b/osu.Game/Localisation/NotificationsStrings.cs
index 3188ca5533..5857b33f52 100644
--- a/osu.Game/Localisation/NotificationsStrings.cs
+++ b/osu.Game/Localisation/NotificationsStrings.cs
@@ -84,12 +84,12 @@ Please try changing your audio device to a working setting.");
public static LocalisableString LinkTypeNotSupported => new TranslatableString(getKey(@"unsupported_link_type"), @"This link type is not yet supported!");
///
- /// "You received a private message from '{0}'. Click to read it!"
+ /// "You received a private message from '{0}'. Click to read it!"
///
public static LocalisableString PrivateMessageReceived(string username) => new TranslatableString(getKey(@"private_message_received"), @"You received a private message from '{0}'. Click to read it!", username);
///
- /// "Your name was mentioned in chat by '{0}'. Click to find out why!"
+ /// "Your name was mentioned in chat by '{0}'. Click to find out why!"
///
public static LocalisableString YourNameWasMentioned(string username) => new TranslatableString(getKey(@"your_name_was_mentioned"), @"Your name was mentioned in chat by '{0}'. Click to find out why!", username);
@@ -114,8 +114,8 @@ Please try changing your audio device to a working setting.");
public static LocalisableString MismatchingBeatmapForReplay => new TranslatableString(getKey(@"mismatching_beatmap_for_replay"), @"Your local copy of the beatmap for this replay appears to be different than expected. You may need to update or re-download it.");
///
- /// "You are now running osu! {version}.
- /// Click to see what's new!"
+ /// "You are now running osu! {0}.
+ /// Click to see what's new!"
///
public static LocalisableString GameVersionAfterUpdate(string version) => new TranslatableString(getKey(@"game_version_after_update"), @"You are now running osu! {0}.
Click to see what's new!", version);
From ca246015d5c5e46ddbb6eaa003ae1d87a818aa7c Mon Sep 17 00:00:00 2001
From: Fina <75299710+Finadoggie@users.noreply.github.com>
Date: Fri, 19 Apr 2024 16:00:31 -0700
Subject: [PATCH 0089/1255] Add bool useSliderHead
---
.../Difficulty/OsuPerformanceCalculator.cs | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index 830bc47731..c78fae1c79 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -15,6 +15,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
public const double PERFORMANCE_BASE_MULTIPLIER = 1.14; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things.
+ private bool useSliderHead;
+
private double accuracy;
private int scoreMaxCombo;
private int countGreat;
@@ -35,6 +37,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
var osuAttributes = (OsuDifficultyAttributes)attributes;
+ useSliderHead = !score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value);
+
accuracy = score.Accuracy;
scoreMaxCombo = score.MaxCombo;
countGreat = score.Statistics.GetValueOrDefault(HitResult.Great);
@@ -42,13 +46,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
countLargeTickMiss = score.Statistics.GetValueOrDefault(HitResult.LargeTickMiss);
- countSliderEndsDropped = score.Statistics.GetValueOrDefault(HitResult.SmallTickMiss);
- if (!score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value))
- effectiveMissCount = countMiss;
+
+ if (useSliderHead)
+ countSliderEndsDropped = osuAttributes.SliderCount - score.Statistics.GetValueOrDefault(HitResult.SliderTailHit);
+
+ if (useSliderHead)
+ effectiveMissCount = countMiss;
else
effectiveMissCount = calculateEffectiveMissCount(osuAttributes);
-
double multiplier = PERFORMANCE_BASE_MULTIPLIER;
if (score.Mods.Any(m => m is OsuModNoFail))
@@ -131,7 +137,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (attributes.SliderCount > 0)
{
double estimateSliderEndsDropped;
- if (score.IsLegacyScore)
+ if (useSliderHead)
estimateSliderEndsDropped = Math.Clamp(Math.Min(countOk + countMeh + countMiss, attributes.MaxCombo - scoreMaxCombo), 0, estimateDifficultSliders);
else
estimateSliderEndsDropped = countSliderEndsDropped;
From 77814ec69fa3eabc286bd0e77db3dff7dd7b7ec8 Mon Sep 17 00:00:00 2001
From: Fina <75299710+Finadoggie@users.noreply.github.com>
Date: Fri, 19 Apr 2024 16:04:17 -0700
Subject: [PATCH 0090/1255] Fix getting slider head drops
---
.../Difficulty/OsuPerformanceCalculator.cs | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index c78fae1c79..e3dab135dd 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -15,8 +15,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
public const double PERFORMANCE_BASE_MULTIPLIER = 1.14; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things.
- private bool useSliderHead;
-
private double accuracy;
private int scoreMaxCombo;
private int countGreat;
@@ -37,8 +35,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
var osuAttributes = (OsuDifficultyAttributes)attributes;
- useSliderHead = !score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value);
-
accuracy = score.Accuracy;
scoreMaxCombo = score.MaxCombo;
countGreat = score.Statistics.GetValueOrDefault(HitResult.Great);
@@ -46,12 +42,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
countLargeTickMiss = score.Statistics.GetValueOrDefault(HitResult.LargeTickMiss);
-
- if (useSliderHead)
+ if (!score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value))
+ {
countSliderEndsDropped = osuAttributes.SliderCount - score.Statistics.GetValueOrDefault(HitResult.SliderTailHit);
-
- if (useSliderHead)
- effectiveMissCount = countMiss;
+ }
+ if (!score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value))
+ effectiveMissCount = countMiss;
else
effectiveMissCount = calculateEffectiveMissCount(osuAttributes);
@@ -137,7 +133,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (attributes.SliderCount > 0)
{
double estimateSliderEndsDropped;
- if (useSliderHead)
+ if (score.IsLegacyScore)
estimateSliderEndsDropped = Math.Clamp(Math.Min(countOk + countMeh + countMiss, attributes.MaxCombo - scoreMaxCombo), 0, estimateDifficultSliders);
else
estimateSliderEndsDropped = countSliderEndsDropped;
From 759a82655cf3e741b5c27097a8d6ecddb9a71259 Mon Sep 17 00:00:00 2001
From: Fina <75299710+Finadoggie@users.noreply.github.com>
Date: Fri, 19 Apr 2024 16:04:49 -0700
Subject: [PATCH 0091/1255] Clamp estimatedSliderEndsDrop
---
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 e3dab135dd..a2542d83e5 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (score.IsLegacyScore)
estimateSliderEndsDropped = Math.Clamp(Math.Min(countOk + countMeh + countMiss, attributes.MaxCombo - scoreMaxCombo), 0, estimateDifficultSliders);
else
- estimateSliderEndsDropped = countSliderEndsDropped;
+ estimateSliderEndsDropped = Math.Min(countSliderEndsDropped + countLargeTickMiss, estimateDifficultSliders); ;
double sliderNerfFactor = 0;
sliderNerfFactor = (1 - attributes.SliderFactor) * Math.Pow(1 - estimateSliderEndsDropped / estimateDifficultSliders, 3) + attributes.SliderFactor;
From 4a7b8138aeb4cf463a83fd8c51ca31ea22d8fe8f Mon Sep 17 00:00:00 2001
From: Fina <75299710+Finadoggie@users.noreply.github.com>
Date: Fri, 19 Apr 2024 16:07:54 -0700
Subject: [PATCH 0092/1255] Re-add bool useSliderHead
oops
---
.../Difficulty/OsuPerformanceCalculator.cs | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index a2542d83e5..86f427159a 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -15,6 +15,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
public const double PERFORMANCE_BASE_MULTIPLIER = 1.14; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things.
+ private bool useSliderHead;
+
private double accuracy;
private int scoreMaxCombo;
private int countGreat;
@@ -42,12 +44,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
countLargeTickMiss = score.Statistics.GetValueOrDefault(HitResult.LargeTickMiss);
- if (!score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value))
+ if (useSliderHead)
{
countSliderEndsDropped = osuAttributes.SliderCount - score.Statistics.GetValueOrDefault(HitResult.SliderTailHit);
}
- if (!score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value))
- effectiveMissCount = countMiss;
+ if (useSliderHead)
+ effectiveMissCount = countMiss;
else
effectiveMissCount = calculateEffectiveMissCount(osuAttributes);
@@ -133,7 +135,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (attributes.SliderCount > 0)
{
double estimateSliderEndsDropped;
- if (score.IsLegacyScore)
+ if (useSliderHead)
estimateSliderEndsDropped = Math.Clamp(Math.Min(countOk + countMeh + countMiss, attributes.MaxCombo - scoreMaxCombo), 0, estimateDifficultSliders);
else
estimateSliderEndsDropped = Math.Min(countSliderEndsDropped + countLargeTickMiss, estimateDifficultSliders); ;
From d1dcac08c6cf05518288fab49b72b2aa093c5c10 Mon Sep 17 00:00:00 2001
From: Fina <75299710+Finadoggie@users.noreply.github.com>
Date: Fri, 19 Apr 2024 16:11:26 -0700
Subject: [PATCH 0093/1255] fix code formatting
---
.../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 86f427159a..0a92b724d2 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -44,10 +44,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
countLargeTickMiss = score.Statistics.GetValueOrDefault(HitResult.LargeTickMiss);
+
if (useSliderHead)
- {
countSliderEndsDropped = osuAttributes.SliderCount - score.Statistics.GetValueOrDefault(HitResult.SliderTailHit);
- }
+
if (useSliderHead)
effectiveMissCount = countMiss;
else
@@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (useSliderHead)
estimateSliderEndsDropped = Math.Clamp(Math.Min(countOk + countMeh + countMiss, attributes.MaxCombo - scoreMaxCombo), 0, estimateDifficultSliders);
else
- estimateSliderEndsDropped = Math.Min(countSliderEndsDropped + countLargeTickMiss, estimateDifficultSliders); ;
+ estimateSliderEndsDropped = Math.Min(countSliderEndsDropped + countLargeTickMiss, estimateDifficultSliders);
double sliderNerfFactor = 0;
sliderNerfFactor = (1 - attributes.SliderFactor) * Math.Pow(1 - estimateSliderEndsDropped / estimateDifficultSliders, 3) + attributes.SliderFactor;
From 4fe55d437a906817e275217e28687f9512e090a8 Mon Sep 17 00:00:00 2001
From: Fina <75299710+Finadoggie@users.noreply.github.com>
Date: Sat, 20 Apr 2024 14:14:57 -0700
Subject: [PATCH 0094/1255] Renamed useSliderHead to useClassicSlider (and
refactored code accordingly)
---
.../Difficulty/OsuPerformanceCalculator.cs | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index 0a92b724d2..23842f4e67 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
public const double PERFORMANCE_BASE_MULTIPLIER = 1.14; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things.
- private bool useSliderHead;
+ private bool useClassicSlider;
private double accuracy;
private int scoreMaxCombo;
@@ -36,6 +36,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
protected override PerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, DifficultyAttributes attributes)
{
var osuAttributes = (OsuDifficultyAttributes)attributes;
+ useClassicSlider = score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value);
accuracy = score.Accuracy;
scoreMaxCombo = score.MaxCombo;
@@ -45,13 +46,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
countLargeTickMiss = score.Statistics.GetValueOrDefault(HitResult.LargeTickMiss);
- if (useSliderHead)
+ if (!useClassicSlider)
countSliderEndsDropped = osuAttributes.SliderCount - score.Statistics.GetValueOrDefault(HitResult.SliderTailHit);
- if (useSliderHead)
- effectiveMissCount = countMiss;
- else
+ if (useClassicSlider)
effectiveMissCount = calculateEffectiveMissCount(osuAttributes);
+ else
+ effectiveMissCount = countMiss;
double multiplier = PERFORMANCE_BASE_MULTIPLIER;
@@ -135,7 +136,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (attributes.SliderCount > 0)
{
double estimateSliderEndsDropped;
- if (useSliderHead)
+ if (useClassicSlider)
estimateSliderEndsDropped = Math.Clamp(Math.Min(countOk + countMeh + countMiss, attributes.MaxCombo - scoreMaxCombo), 0, estimateDifficultSliders);
else
estimateSliderEndsDropped = Math.Min(countSliderEndsDropped + countLargeTickMiss, estimateDifficultSliders);
From 9363194f156101728527555730f4da71de8602dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Mon, 22 Apr 2024 09:31:27 +0200
Subject: [PATCH 0095/1255] Remove old signature
---
osu.Game/Database/EmptyRealmSet.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/osu.Game/Database/EmptyRealmSet.cs b/osu.Game/Database/EmptyRealmSet.cs
index e548d28f68..7b5296b5a1 100644
--- a/osu.Game/Database/EmptyRealmSet.cs
+++ b/osu.Game/Database/EmptyRealmSet.cs
@@ -37,7 +37,6 @@ namespace osu.Game.Database
public IRealmCollection Freeze() => throw new NotImplementedException();
public IDisposable SubscribeForNotifications(NotificationCallbackDelegate callback, KeyPathsCollection? keyPathCollection = null) => throw new NotImplementedException();
- public IDisposable SubscribeForNotifications(NotificationCallbackDelegate callback) => throw new NotImplementedException();
public bool IsValid => throw new NotImplementedException();
public Realm Realm => throw new NotImplementedException();
public ObjectSchema ObjectSchema => throw new NotImplementedException();
From 5f3241978cba695b1f3ee197841d73122fec6642 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Mon, 22 Apr 2024 09:31:50 +0200
Subject: [PATCH 0096/1255] Remove redundant constructor
---
osu.Game/Online/Multiplayer/InvalidPasswordException.cs | 3 ---
1 file changed, 3 deletions(-)
diff --git a/osu.Game/Online/Multiplayer/InvalidPasswordException.cs b/osu.Game/Online/Multiplayer/InvalidPasswordException.cs
index 8f2543ee1e..b76a1cc05d 100644
--- a/osu.Game/Online/Multiplayer/InvalidPasswordException.cs
+++ b/osu.Game/Online/Multiplayer/InvalidPasswordException.cs
@@ -9,8 +9,5 @@ namespace osu.Game.Online.Multiplayer
[Serializable]
public class InvalidPasswordException : HubException
{
- public InvalidPasswordException()
- {
- }
}
}
From c1efcc054cf1594d3d883fcf66ab67b811d4de81 Mon Sep 17 00:00:00 2001
From: danielthirtle
Date: Tue, 21 May 2024 21:03:53 +1200
Subject: [PATCH 0097/1255] Change miss penalty (nerf longer maps)
---
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 47f6770ce5..36768967ff 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -261,7 +261,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
// Miss penalty assumes that a player will miss on the hardest parts of a map,
// so we use the amount of relatively difficult sections to adjust miss penalty
// to make it more punishing on maps with lower amount of hard sections.
- private double calculateMissPenalty(double missCount, double difficultStrainCount) => 0.94 / ((missCount / (2 * Math.Sqrt(difficultStrainCount))) + 1);
+ 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 int totalHits => countGreat + countOk + countMeh + countMiss;
}
From 20c54ab697eec2bb183a2f7f9ea5022a7b9be66a Mon Sep 17 00:00:00 2001
From: js1086
Date: Thu, 23 May 2024 19:08:32 +0100
Subject: [PATCH 0098/1255] Apply code quality changes
---
osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 3 +--
.../Difficulty/Skills/OsuStrainSkill.cs | 16 ++++++++--------
osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 10 ++++------
3 files changed, 13 insertions(+), 16 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
index ad0e3fd107..6c17c84c19 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
@@ -34,8 +34,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
{
currentStrain *= strainDecay(current.DeltaTime);
currentStrain += AimEvaluator.EvaluateDifficultyOf(current, withSliders) * skillMultiplier;
-
- objectStrains.Add(currentStrain);
+ ObjectStrains.Add(currentStrain);
return currentStrain;
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
index 39175d55e0..c2e9357e3a 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
@@ -34,8 +34,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
///
protected virtual double DifficultyMultiplier => DEFAULT_DIFFICULTY_MULTIPLIER;
- protected List objectStrains = new List();
- protected double difficulty;
+ protected List ObjectStrains = new List();
+ protected double Difficulty;
protected OsuStrainSkill(Mod[] mods)
: base(mods)
@@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
public override double DifficultyValue()
{
- difficulty = 0;
+ Difficulty = 0;
double weight = 1;
// Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871).
@@ -64,11 +64,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
// We're sorting from highest to lowest strain.
foreach (double strain in strains.OrderDescending())
{
- difficulty += strain * weight;
+ Difficulty += strain * weight;
weight *= DecayWeight;
}
- return difficulty * DifficultyMultiplier;
+ return Difficulty * DifficultyMultiplier;
}
///
@@ -77,9 +77,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
///
public double CountDifficultStrains()
{
- double consistentTopStrain = difficulty / 10; // What would the top strain be if all strain values were identical
+ double consistentTopStrain = Difficulty / 10; // What would the top strain be if all strain values were identical
// Use a weighted sum of all strains. Constants are arbitrary and give nice values
- return objectStrains.Sum(s => 1.1 / (1 + Math.Exp(-10 * (s / consistentTopStrain - 0.88))));
+ return ObjectStrains.Sum(s => 1.1 / (1 + Math.Exp(-10 * (s / consistentTopStrain - 0.88))));
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
index b1cd0b21d1..bf8e09bd53 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
@@ -41,23 +41,21 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current);
double totalStrain = currentStrain * currentRhythm;
-
- objectStrains.Add(totalStrain);
+ ObjectStrains.Add(totalStrain);
return totalStrain;
}
public double RelevantNoteCount()
{
- if (objectStrains.Count == 0)
+ if (ObjectStrains.Count == 0)
return 0;
- double maxStrain = objectStrains.Max();
-
+ double maxStrain = ObjectStrains.Max();
if (maxStrain == 0)
return 0;
- return objectStrains.Sum(strain => 1.0 / (1.0 + Math.Exp(-(strain / maxStrain * 12.0 - 6.0))));
+ return ObjectStrains.Sum(strain => 1.0 / (1.0 + Math.Exp(-(strain / maxStrain * 12.0 - 6.0))));
}
}
}
From 1f55c1413bc5fddbae2d9fc932376e189c9c9023 Mon Sep 17 00:00:00 2001
From: Fina <75299710+Finadoggie@users.noreply.github.com>
Date: Fri, 24 May 2024 13:50:26 -0700
Subject: [PATCH 0099/1255] merged givi's accuracy changes
stat acc save me
---
.../Difficulty/OsuPerformanceCalculator.cs | 24 ++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index 23842f4e67..fe9d1f1afc 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -46,6 +46,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
countLargeTickMiss = score.Statistics.GetValueOrDefault(HitResult.LargeTickMiss);
+ if (totalHits > 0) accuracy = calculateEffectiveAccuracy(countGreat, countOk, countMeh, countMiss, osuAttributes);
+
if (!useClassicSlider)
countSliderEndsDropped = osuAttributes.SliderCount - score.Statistics.GetValueOrDefault(HitResult.SliderTailHit);
@@ -192,7 +194,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double relevantCountGreat = Math.Max(0, countGreat - relevantTotalDiff);
double relevantCountOk = Math.Max(0, countOk - Math.Max(0, relevantTotalDiff - countGreat));
double relevantCountMeh = Math.Max(0, countMeh - Math.Max(0, relevantTotalDiff - countGreat - countOk));
- double relevantAccuracy = attributes.SpeedNoteCount == 0 ? 0 : (relevantCountGreat * 6.0 + relevantCountOk * 2.0 + relevantCountMeh) / (attributes.SpeedNoteCount * 6.0);
+ double relevantCountMiss = Math.Max(0, countMiss - Math.Max(0, relevantTotalDiff - countGreat - countOk - countMeh));
+ double relevantAccuracy = attributes.SpeedNoteCount == 0 ? 0 : calculateEffectiveAccuracy(relevantCountGreat, relevantCountOk, relevantCountMeh, relevantCountMiss, attributes);
// Scale the speed value with accuracy and OD.
speedValue *= (0.95 + Math.Pow(attributes.OverallDifficulty, 2) / 750) * Math.Pow((accuracy + relevantAccuracy) / 2.0, (14.5 - Math.Max(attributes.OverallDifficulty, 8)) / 2);
@@ -283,6 +286,25 @@ namespace osu.Game.Rulesets.Osu.Difficulty
return Math.Max(countMiss, comboBasedMissCount);
}
+ // This function is calculating accuracy trying to remove sliders from mistap if slideracc is present
+ // This is wrong way to do this, but doing it right way overnerfs already set scores
+ // This is used to no until the better way (statistical accuracy) to do this is present
+ private double calculateEffectiveAccuracy(double c300, double c100, double c50, double cMiss, OsuDifficultyAttributes attributes)
+ {
+ double accuracy = (c300 * 6 + c100 * 2 + c50) / (c300 + c100 + c50 + cMiss) / 6;
+ if (useClassicSlider) return accuracy;
+
+ // Try to remove sliders from mistakes
+ double mistakesPortion = 1 - accuracy;
+
+ double hitcircleRatio = (double)attributes.HitCircleCount / (attributes.HitCircleCount + attributes.SliderCount);
+ mistakesPortion *= hitcircleRatio;
+
+ accuracy = Math.Max(accuracy, 1 - mistakesPortion);
+
+ return accuracy;
+ }
+
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 int totalHits => countGreat + countOk + countMeh + countMiss;
}
From 6c9e906b2d3c89008be590347f2f9e84b3cf9fd2 Mon Sep 17 00:00:00 2001
From: Fina <75299710+Finadoggie@users.noreply.github.com>
Date: Fri, 24 May 2024 14:00:42 -0700
Subject: [PATCH 0100/1255] Revert "merged givi's accuracy changes"
This reverts commit 1f55c1413bc5fddbae2d9fc932376e189c9c9023.
---
.../Difficulty/OsuPerformanceCalculator.cs | 24 +------------------
1 file changed, 1 insertion(+), 23 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index fe9d1f1afc..23842f4e67 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -46,8 +46,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
countLargeTickMiss = score.Statistics.GetValueOrDefault(HitResult.LargeTickMiss);
- if (totalHits > 0) accuracy = calculateEffectiveAccuracy(countGreat, countOk, countMeh, countMiss, osuAttributes);
-
if (!useClassicSlider)
countSliderEndsDropped = osuAttributes.SliderCount - score.Statistics.GetValueOrDefault(HitResult.SliderTailHit);
@@ -194,8 +192,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double relevantCountGreat = Math.Max(0, countGreat - relevantTotalDiff);
double relevantCountOk = Math.Max(0, countOk - Math.Max(0, relevantTotalDiff - countGreat));
double relevantCountMeh = Math.Max(0, countMeh - Math.Max(0, relevantTotalDiff - countGreat - countOk));
- double relevantCountMiss = Math.Max(0, countMiss - Math.Max(0, relevantTotalDiff - countGreat - countOk - countMeh));
- double relevantAccuracy = attributes.SpeedNoteCount == 0 ? 0 : calculateEffectiveAccuracy(relevantCountGreat, relevantCountOk, relevantCountMeh, relevantCountMiss, attributes);
+ double relevantAccuracy = attributes.SpeedNoteCount == 0 ? 0 : (relevantCountGreat * 6.0 + relevantCountOk * 2.0 + relevantCountMeh) / (attributes.SpeedNoteCount * 6.0);
// Scale the speed value with accuracy and OD.
speedValue *= (0.95 + Math.Pow(attributes.OverallDifficulty, 2) / 750) * Math.Pow((accuracy + relevantAccuracy) / 2.0, (14.5 - Math.Max(attributes.OverallDifficulty, 8)) / 2);
@@ -286,25 +283,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
return Math.Max(countMiss, comboBasedMissCount);
}
- // This function is calculating accuracy trying to remove sliders from mistap if slideracc is present
- // This is wrong way to do this, but doing it right way overnerfs already set scores
- // This is used to no until the better way (statistical accuracy) to do this is present
- private double calculateEffectiveAccuracy(double c300, double c100, double c50, double cMiss, OsuDifficultyAttributes attributes)
- {
- double accuracy = (c300 * 6 + c100 * 2 + c50) / (c300 + c100 + c50 + cMiss) / 6;
- if (useClassicSlider) return accuracy;
-
- // Try to remove sliders from mistakes
- double mistakesPortion = 1 - accuracy;
-
- double hitcircleRatio = (double)attributes.HitCircleCount / (attributes.HitCircleCount + attributes.SliderCount);
- mistakesPortion *= hitcircleRatio;
-
- accuracy = Math.Max(accuracy, 1 - mistakesPortion);
-
- return accuracy;
- }
-
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 int totalHits => countGreat + countOk + countMeh + countMiss;
}
From 61afda1089eeee4d42f59ab6185023d699acb788 Mon Sep 17 00:00:00 2001
From: js1086
Date: Sun, 26 May 2024 11:24:06 +0100
Subject: [PATCH 0101/1255] Fix NaN case when difficulty is 0
---
osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 3 +++
1 file changed, 3 insertions(+)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
index c2e9357e3a..af97d90e53 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
@@ -77,6 +77,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
///
public double CountDifficultStrains()
{
+ if (double.IsNaN(Difficulty))
+ return 0.0;
+
double consistentTopStrain = Difficulty / 10; // What would the top strain be if all strain values were identical
// Use a weighted sum of all strains. Constants are arbitrary and give nice values
return ObjectStrains.Sum(s => 1.1 / (1 + Math.Exp(-10 * (s / consistentTopStrain - 0.88))));
From c25e1bdeb586db8a2def47232632be61b4d4242e Mon Sep 17 00:00:00 2001
From: js1086
Date: Sun, 26 May 2024 14:21:47 +0100
Subject: [PATCH 0102/1255] Use correct operation for 0 difficulty case
---
osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
index af97d90e53..ec132237db 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
@@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
///
public double CountDifficultStrains()
{
- if (double.IsNaN(Difficulty))
+ if (Difficulty == 0)
return 0.0;
double consistentTopStrain = Difficulty / 10; // What would the top strain be if all strain values were identical
From 17145673421118baee2d6971277771625512a78a Mon Sep 17 00:00:00 2001
From: Nathen
Date: Wed, 29 May 2024 09:40:39 -0400
Subject: [PATCH 0103/1255] Save deviation calculations to variables
---
.../Difficulty/TaikoPerformanceCalculator.cs | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index ca11397801..a8ccaedfd0 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -129,6 +129,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
const double z = 2.32634787404; // 99% critical value for the normal distribution (one-tailed).
+ double? deviationGreatWindow = calcDeviationGreatWindow();
+ double? deviationGoodWindow = calcDeviationGoodWindow();
+
+ if (deviationGreatWindow is null)
+ return deviationGoodWindow;
+
+ return Math.Min(deviationGreatWindow.Value, deviationGoodWindow!.Value);
+
// The upper bound on deviation, calculated with the ratio of 300s to objects, and the great hit window.
double? calcDeviationGreatWindow()
{
@@ -163,11 +171,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
// We can be 99% confident that the deviation is not higher than:
return h100 / (Math.Sqrt(2) * SpecialFunctions.ErfInv(pLowerBound));
}
-
- if (calcDeviationGreatWindow() is null)
- return calcDeviationGoodWindow();
-
- return Math.Min(calcDeviationGreatWindow()!.Value, calcDeviationGoodWindow()!.Value);
}
private int totalHits => countGreat + countOk + countMeh + countMiss;
From f8f18b6cbd49750916c4fb75b85f0ad93e2f98c4 Mon Sep 17 00:00:00 2001
From: Nathen
Date: Wed, 29 May 2024 09:40:59 -0400
Subject: [PATCH 0104/1255] Fix naming convention
---
.../Difficulty/TaikoPerformanceAttributes.cs | 4 ++--
.../Difficulty/TaikoPerformanceCalculator.cs | 14 +++++++-------
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs
index 6d85aa8f3d..7c74e43db1 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs
@@ -18,8 +18,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
[JsonProperty("effective_miss_count")]
public double EffectiveMissCount { get; set; }
- [JsonProperty("estimated_ur")]
- public double? EstimatedUr { get; set; }
+ [JsonProperty("estimated_unstable_rate")]
+ public double? EstimatedUnstableRate { get; set; }
public override IEnumerable GetAttributesForDisplay()
{
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index a8ccaedfd0..ab809a0ade 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
private int countOk;
private int countMeh;
private int countMiss;
- private double? estimatedUr;
+ private double? estimatedUnstableRate;
private double effectiveMissCount;
@@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
countOk = score.Statistics.GetValueOrDefault(HitResult.Ok);
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
- estimatedUr = computeDeviationUpperBound(taikoAttributes) * 10;
+ estimatedUnstableRate = computeDeviationUpperBound(taikoAttributes) * 10;
// The effectiveMissCount is calculated by gaining a ratio for totalSuccessfulHits and increasing the miss penalty for shorter object counts lower than 1000.
if (totalSuccessfulHits > 0)
@@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
Difficulty = difficultyValue,
Accuracy = accuracyValue,
EffectiveMissCount = effectiveMissCount,
- EstimatedUr = estimatedUr,
+ EstimatedUnstableRate = estimatedUnstableRate,
Total = totalValue
};
}
@@ -92,18 +92,18 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (score.Mods.Any(m => m is ModFlashlight))
difficultyValue *= 1.050 * lengthBonus;
- if (estimatedUr == null)
+ if (estimatedUnstableRate == null)
return 0;
- return difficultyValue * Math.Pow(SpecialFunctions.Erf(400 / (Math.Sqrt(2) * estimatedUr.Value)), 2.0);
+ return difficultyValue * Math.Pow(SpecialFunctions.Erf(400 / (Math.Sqrt(2) * estimatedUnstableRate.Value)), 2.0);
}
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes, bool isConvert)
{
- if (attributes.GreatHitWindow <= 0 || estimatedUr == null)
+ if (attributes.GreatHitWindow <= 0 || estimatedUnstableRate == null)
return 0;
- double accuracyValue = Math.Pow(70 / estimatedUr.Value, 1.1) * Math.Pow(attributes.StarRating, 0.4) * 100.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));
From e85869e0b48e7d683933ed821f6ec4baa5ea57a3 Mon Sep 17 00:00:00 2001
From: Aurelian
Date: Sun, 2 Jun 2024 16:38:58 +0200
Subject: [PATCH 0105/1255] Move already placed objects when adjusting
offset/BPM
---
.../Screens/Edit/Timing/TapTimingControl.cs | 56 ++++++++++++++++++-
1 file changed, 55 insertions(+), 1 deletion(-)
diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
index 8cdbd97ecb..cc0d195626 100644
--- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
+++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
@@ -1,6 +1,8 @@
// 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.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -9,11 +11,13 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
+using osu.Framework.Utils;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Overlays;
+using osu.Game.Rulesets.Objects;
using osuTK;
namespace osu.Game.Screens.Edit.Timing
@@ -33,6 +37,8 @@ namespace osu.Game.Screens.Edit.Timing
private MetronomeDisplay metronome = null!;
+ private LabelledSwitchButton adjustPlacedNotes = null!;
+
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider, OsuColour colours)
{
@@ -59,6 +65,7 @@ namespace osu.Game.Screens.Edit.Timing
{
new Dimension(GridSizeMode.Absolute, 200),
new Dimension(GridSizeMode.Absolute, 50),
+ new Dimension(GridSizeMode.Absolute, 50),
new Dimension(GridSizeMode.Absolute, TapButton.SIZE + padding),
},
Content = new[]
@@ -116,6 +123,18 @@ namespace osu.Game.Screens.Edit.Timing
},
},
new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Bottom = padding, Horizontal = padding },
+ Children = new Drawable[]
+ {
+ adjustPlacedNotes = new LabelledSwitchButton { Label = "Move already placed notes\nwhen changing the offset/BPM" },
+ }
+ },
+ },
+ new Drawable[]
{
new Container
{
@@ -192,6 +211,17 @@ namespace osu.Game.Screens.Edit.Timing
editorClock.Seek(selectedGroup.Value.Time);
}
+ private List hitObjectsInTimingRange(EditorBeatmap beatmap, ControlPointGroup selectedGroup)
+ {
+ // If the first group, we grab all hitobjects prior to the next, if the last group, we grab all remaining hitobjects
+ double firstGroupTime = beatmap.ControlPointInfo.Groups.Any(x => x.ControlPoints.Any(y => y is TimingControlPoint) && x.Time < selectedGroup.Time) ? selectedGroup.Time : double.MinValue;
+ double nextGroupTime = beatmap.ControlPointInfo.Groups.FirstOrDefault(x => x.ControlPoints.Any(y => y is TimingControlPoint) && x.Time > selectedGroup.Time)?.Time ?? double.MaxValue;
+
+ var result = beatmap.HitObjects.Where(x => Precision.AlmostBigger(x.StartTime, firstGroupTime) && Precision.DefinitelyBigger(nextGroupTime, x.StartTime)).ToList();
+ Console.WriteLine(firstGroupTime + ", " + nextGroupTime + ", " + result.Count);
+ return result;
+ }
+
private void adjustOffset(double adjust)
{
if (selectedGroup.Value == null)
@@ -199,6 +229,8 @@ namespace osu.Game.Screens.Edit.Timing
bool wasAtStart = editorClock.CurrentTimeAccurate == selectedGroup.Value.Time;
+ List hitObjectsInRange = hitObjectsInTimingRange(beatmap, selectedGroup.Value);
+
// VERY TEMPORARY
var currentGroupItems = selectedGroup.Value.ControlPoints.ToArray();
@@ -212,6 +244,14 @@ namespace osu.Game.Screens.Edit.Timing
// the control point might not necessarily exist yet, if currentGroupItems was empty.
selectedGroup.Value = beatmap.ControlPointInfo.GroupAt(newOffset, true);
+ if (adjustPlacedNotes.Current.Value)
+ {
+ foreach (HitObject hitObject in hitObjectsInRange)
+ {
+ hitObject.StartTime += adjust;
+ }
+ }
+
if (!editorClock.IsRunning && wasAtStart)
editorClock.Seek(newOffset);
}
@@ -223,7 +263,21 @@ namespace osu.Game.Screens.Edit.Timing
if (timing == null)
return;
- timing.BeatLength = 60000 / (timing.BPM + adjust);
+ double newBeatLength = 60000 / (timing.BPM + adjust);
+
+ List hitObjectsInRange = hitObjectsInTimingRange(beatmap, selectedGroup.Value!);
+
+ if (adjustPlacedNotes.Current.Value)
+ {
+ foreach (HitObject hitObject in hitObjectsInRange)
+ {
+ double beat = (hitObject.StartTime - selectedGroup.Value!.Time) / timing.BeatLength;
+
+ hitObject.StartTime = beat * newBeatLength + selectedGroup.Value.Time;
+ }
+ }
+
+ timing.BeatLength = newBeatLength;
}
private partial class InlineButton : OsuButton
From d02c291168af670e66612cda33d35842ec5f05cf Mon Sep 17 00:00:00 2001
From: Aurelian
Date: Sun, 2 Jun 2024 16:42:06 +0200
Subject: [PATCH 0106/1255] Removed debugging information
---
osu.Game/Screens/Edit/Timing/TapTimingControl.cs | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
index cc0d195626..c4d67eaf1a 100644
--- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
+++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
@@ -1,7 +1,6 @@
// 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.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
@@ -217,9 +216,7 @@ namespace osu.Game.Screens.Edit.Timing
double firstGroupTime = beatmap.ControlPointInfo.Groups.Any(x => x.ControlPoints.Any(y => y is TimingControlPoint) && x.Time < selectedGroup.Time) ? selectedGroup.Time : double.MinValue;
double nextGroupTime = beatmap.ControlPointInfo.Groups.FirstOrDefault(x => x.ControlPoints.Any(y => y is TimingControlPoint) && x.Time > selectedGroup.Time)?.Time ?? double.MaxValue;
- var result = beatmap.HitObjects.Where(x => Precision.AlmostBigger(x.StartTime, firstGroupTime) && Precision.DefinitelyBigger(nextGroupTime, x.StartTime)).ToList();
- Console.WriteLine(firstGroupTime + ", " + nextGroupTime + ", " + result.Count);
- return result;
+ return beatmap.HitObjects.Where(x => Precision.AlmostBigger(x.StartTime, firstGroupTime) && Precision.DefinitelyBigger(nextGroupTime, x.StartTime)).ToList();
}
private void adjustOffset(double adjust)
From 649cfb11fc76b70573fe865f2a4669b1e5a804e9 Mon Sep 17 00:00:00 2001
From: Aurelian
Date: Sun, 2 Jun 2024 16:59:16 +0200
Subject: [PATCH 0107/1255] To satisfy CodeFactor
---
osu.Game/Screens/Edit/Timing/TapTimingControl.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
index c4d67eaf1a..952bdf0ccc 100644
--- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
+++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
@@ -270,7 +270,7 @@ namespace osu.Game.Screens.Edit.Timing
{
double beat = (hitObject.StartTime - selectedGroup.Value!.Time) / timing.BeatLength;
- hitObject.StartTime = beat * newBeatLength + selectedGroup.Value.Time;
+ hitObject.StartTime = (beat * newBeatLength) + selectedGroup.Value.Time;
}
}
From a940809fbf8694486f9b1f0c3fb0f6ce243026e7 Mon Sep 17 00:00:00 2001
From: Aurelian
Date: Mon, 3 Jun 2024 14:36:53 +0200
Subject: [PATCH 0108/1255] Addressed some more code maintainability issues
---
osu.Game/Screens/Edit/Timing/TapTimingControl.cs | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
index 952bdf0ccc..6ef7bd1a40 100644
--- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
+++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
+using System.Runtime.CompilerServices;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@@ -210,13 +211,13 @@ namespace osu.Game.Screens.Edit.Timing
editorClock.Seek(selectedGroup.Value.Time);
}
- private List hitObjectsInTimingRange(EditorBeatmap beatmap, ControlPointGroup selectedGroup)
+ private static List hitObjectsInTimingRange(EditorBeatmap beatmap, ControlPointGroup selectedGroup)
{
// If the first group, we grab all hitobjects prior to the next, if the last group, we grab all remaining hitobjects
- double firstGroupTime = beatmap.ControlPointInfo.Groups.Any(x => x.ControlPoints.Any(y => y is TimingControlPoint) && x.Time < selectedGroup.Time) ? selectedGroup.Time : double.MinValue;
- double nextGroupTime = beatmap.ControlPointInfo.Groups.FirstOrDefault(x => x.ControlPoints.Any(y => y is TimingControlPoint) && x.Time > selectedGroup.Time)?.Time ?? double.MaxValue;
+ double startTime = beatmap.ControlPointInfo.TimingPoints.Any(x => x.Time < selectedGroup.Time) ? selectedGroup.Time : double.MinValue;
+ double endTime = beatmap.ControlPointInfo.TimingPoints.FirstOrDefault(x => x.Time > selectedGroup.Time)?.Time ?? double.MaxValue;
- return beatmap.HitObjects.Where(x => Precision.AlmostBigger(x.StartTime, firstGroupTime) && Precision.DefinitelyBigger(nextGroupTime, x.StartTime)).ToList();
+ return beatmap.HitObjects.Where(x => Precision.AlmostBigger(x.StartTime, startTime) && Precision.DefinitelyBigger(endTime, x.StartTime)).ToList();
}
private void adjustOffset(double adjust)
From 09f3fb9eeea540197032c187688db03e036863bf Mon Sep 17 00:00:00 2001
From: Aurelian
Date: Mon, 3 Jun 2024 23:02:57 +0200
Subject: [PATCH 0109/1255] Compatible IHasDuration hitobjects now scale with
BPM changes Does not apply to hitobjects with IHasRepeats
---
osu.Game/Screens/Edit/Timing/TapTimingControl.cs | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
index 6ef7bd1a40..cffa31d117 100644
--- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
+++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
@@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.CompilerServices;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@@ -18,6 +17,7 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Overlays;
using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Types;
using osuTK;
namespace osu.Game.Screens.Edit.Timing
@@ -272,6 +272,9 @@ namespace osu.Game.Screens.Edit.Timing
double beat = (hitObject.StartTime - selectedGroup.Value!.Time) / timing.BeatLength;
hitObject.StartTime = (beat * newBeatLength) + selectedGroup.Value.Time;
+
+ if (hitObject is not IHasRepeats && hitObject is IHasDuration hitObjectWithDuration)
+ hitObjectWithDuration.Duration *= newBeatLength / timing.BeatLength;
}
}
From 3d5a04ac99d4b9f2002a24206106472c53da5c7b Mon Sep 17 00:00:00 2001
From: Aurelian
Date: Wed, 5 Jun 2024 17:57:44 +0200
Subject: [PATCH 0110/1255] HitObject has defaults applied on bpm/offset
adjustment
---
osu.Game/Screens/Edit/Timing/TapTimingControl.cs | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
index cffa31d117..739b9894ce 100644
--- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
+++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
@@ -252,6 +252,9 @@ namespace osu.Game.Screens.Edit.Timing
if (!editorClock.IsRunning && wasAtStart)
editorClock.Seek(newOffset);
+
+ foreach (HitObject hitObject in hitObjectsInRange)
+ hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.Difficulty);
}
private void adjustBpm(double adjust)
@@ -279,6 +282,9 @@ namespace osu.Game.Screens.Edit.Timing
}
timing.BeatLength = newBeatLength;
+
+ foreach (HitObject hitObject in hitObjectsInRange)
+ hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.Difficulty);
}
private partial class InlineButton : OsuButton
From 2db55f9379d6171ac9b583a883301d07dc03446a Mon Sep 17 00:00:00 2001
From: Aurelian
Date: Fri, 7 Jun 2024 08:21:09 +0200
Subject: [PATCH 0111/1255] Change HitObject update call to use established
patterns
---
osu.Game/Screens/Edit/Timing/TapTimingControl.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
index 739b9894ce..1c7f89c80d 100644
--- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
+++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
@@ -254,7 +254,7 @@ namespace osu.Game.Screens.Edit.Timing
editorClock.Seek(newOffset);
foreach (HitObject hitObject in hitObjectsInRange)
- hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.Difficulty);
+ beatmap.Update(hitObject);
}
private void adjustBpm(double adjust)
@@ -284,7 +284,7 @@ namespace osu.Game.Screens.Edit.Timing
timing.BeatLength = newBeatLength;
foreach (HitObject hitObject in hitObjectsInRange)
- hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.Difficulty);
+ beatmap.Update(hitObject);
}
private partial class InlineButton : OsuButton
From 559f94aa02a138154a136fbb1708ca01d83b3b5b Mon Sep 17 00:00:00 2001
From: Aurelian
Date: Fri, 7 Jun 2024 21:57:12 +0200
Subject: [PATCH 0112/1255] Moved HitObject adjustments to TimingControlPoint
---
.../ControlPoints/TimingControlPoint.cs | 35 +++++++++++++++
.../Screens/Edit/Timing/TapTimingControl.cs | 44 ++++---------------
2 files changed, 43 insertions(+), 36 deletions(-)
diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
index 4e69486e2d..a3224a6ab9 100644
--- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
@@ -2,9 +2,14 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Bindables;
+using osu.Framework.Utils;
using osu.Game.Beatmaps.Timing;
using osu.Game.Graphics;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Types;
using osuTK.Graphics;
namespace osu.Game.Beatmaps.ControlPoints
@@ -105,5 +110,35 @@ namespace osu.Game.Beatmaps.ControlPoints
&& BeatLength.Equals(other.BeatLength);
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), TimeSignature, BeatLength, OmitFirstBarLine);
+
+ public List HitObjectsInTimingRange(IBeatmap beatmap)
+ {
+ // If the first group, we grab all hitobjects prior to the next, if the last group, we grab all remaining hitobjects
+ double startTime = beatmap.ControlPointInfo.TimingPoints.Any(x => x.Time < Time) ? Time : double.MinValue;
+ double endTime = beatmap.ControlPointInfo.TimingPoints.FirstOrDefault(x => x.Time > Time)?.Time ?? double.MaxValue;
+
+ return beatmap.HitObjects.Where(x => Precision.AlmostBigger(x.StartTime, startTime) && Precision.DefinitelyBigger(endTime, x.StartTime)).ToList();
+ }
+
+ public void AdjustHitObjectOffset(IBeatmap beatmap, double adjust)
+ {
+ foreach (HitObject hitObject in HitObjectsInTimingRange(beatmap))
+ {
+ hitObject.StartTime += adjust;
+ }
+ }
+
+ public void SetHitObjectBPM(IBeatmap beatmap, double newBeatLength)
+ {
+ foreach (HitObject hitObject in HitObjectsInTimingRange(beatmap))
+ {
+ double beat = (hitObject.StartTime - Time) / BeatLength;
+
+ hitObject.StartTime = (beat * newBeatLength) + Time;
+
+ if (hitObject is not IHasRepeats && hitObject is IHasDuration hitObjectWithDuration)
+ hitObjectWithDuration.Duration *= newBeatLength / BeatLength;
+ }
+ }
}
}
diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
index 1c7f89c80d..49d3df4aef 100644
--- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
+++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
@@ -211,15 +211,6 @@ namespace osu.Game.Screens.Edit.Timing
editorClock.Seek(selectedGroup.Value.Time);
}
- private static List hitObjectsInTimingRange(EditorBeatmap beatmap, ControlPointGroup selectedGroup)
- {
- // If the first group, we grab all hitobjects prior to the next, if the last group, we grab all remaining hitobjects
- double startTime = beatmap.ControlPointInfo.TimingPoints.Any(x => x.Time < selectedGroup.Time) ? selectedGroup.Time : double.MinValue;
- double endTime = beatmap.ControlPointInfo.TimingPoints.FirstOrDefault(x => x.Time > selectedGroup.Time)?.Time ?? double.MaxValue;
-
- return beatmap.HitObjects.Where(x => Precision.AlmostBigger(x.StartTime, startTime) && Precision.DefinitelyBigger(endTime, x.StartTime)).ToList();
- }
-
private void adjustOffset(double adjust)
{
if (selectedGroup.Value == null)
@@ -227,8 +218,6 @@ namespace osu.Game.Screens.Edit.Timing
bool wasAtStart = editorClock.CurrentTimeAccurate == selectedGroup.Value.Time;
- List hitObjectsInRange = hitObjectsInTimingRange(beatmap, selectedGroup.Value);
-
// VERY TEMPORARY
var currentGroupItems = selectedGroup.Value.ControlPoints.ToArray();
@@ -237,26 +226,22 @@ namespace osu.Game.Screens.Edit.Timing
double newOffset = selectedGroup.Value.Time + adjust;
foreach (var cp in currentGroupItems)
+ {
+ if (adjustPlacedNotes.Current.Value && cp is TimingControlPoint tp)
+ tp.AdjustHitObjectOffset(beatmap, adjust);
beatmap.ControlPointInfo.Add(newOffset, cp);
+ }
// the control point might not necessarily exist yet, if currentGroupItems was empty.
selectedGroup.Value = beatmap.ControlPointInfo.GroupAt(newOffset, true);
- if (adjustPlacedNotes.Current.Value)
- {
- foreach (HitObject hitObject in hitObjectsInRange)
- {
- hitObject.StartTime += adjust;
- }
- }
-
if (!editorClock.IsRunning && wasAtStart)
editorClock.Seek(newOffset);
- foreach (HitObject hitObject in hitObjectsInRange)
- beatmap.Update(hitObject);
+ beatmap.UpdateAllHitObjects();
}
+
private void adjustBpm(double adjust)
{
var timing = selectedGroup.Value?.ControlPoints.OfType().FirstOrDefault();
@@ -266,25 +251,12 @@ namespace osu.Game.Screens.Edit.Timing
double newBeatLength = 60000 / (timing.BPM + adjust);
- List hitObjectsInRange = hitObjectsInTimingRange(beatmap, selectedGroup.Value!);
-
if (adjustPlacedNotes.Current.Value)
- {
- foreach (HitObject hitObject in hitObjectsInRange)
- {
- double beat = (hitObject.StartTime - selectedGroup.Value!.Time) / timing.BeatLength;
-
- hitObject.StartTime = (beat * newBeatLength) + selectedGroup.Value.Time;
-
- if (hitObject is not IHasRepeats && hitObject is IHasDuration hitObjectWithDuration)
- hitObjectWithDuration.Duration *= newBeatLength / timing.BeatLength;
- }
- }
+ timing.SetHitObjectBPM(beatmap, newBeatLength);
timing.BeatLength = newBeatLength;
- foreach (HitObject hitObject in hitObjectsInRange)
- beatmap.Update(hitObject);
+ beatmap.UpdateAllHitObjects();
}
private partial class InlineButton : OsuButton
From ac0c425e2921a2b84467b4703eacff35d380dba7 Mon Sep 17 00:00:00 2001
From: Aurelian
Date: Sun, 9 Jun 2024 11:27:53 +0200
Subject: [PATCH 0113/1255] Moved setting to the menu bar
---
.../ControlPoints/TimingControlPoint.cs | 8 +++---
osu.Game/Localisation/EditorStrings.cs | 5 ++++
osu.Game/Screens/Edit/Editor.cs | 6 ++++-
osu.Game/Screens/Edit/EditorBeatmap.cs | 2 ++
.../Screens/Edit/Timing/TapTimingControl.cs | 25 +++----------------
5 files changed, 20 insertions(+), 26 deletions(-)
diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
index a3224a6ab9..234a34dc87 100644
--- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
@@ -128,16 +128,16 @@ namespace osu.Game.Beatmaps.ControlPoints
}
}
- public void SetHitObjectBPM(IBeatmap beatmap, double newBeatLength)
+ public void SetHitObjectBPM(IBeatmap beatmap, double oldBeatLength)
{
foreach (HitObject hitObject in HitObjectsInTimingRange(beatmap))
{
- double beat = (hitObject.StartTime - Time) / BeatLength;
+ double beat = (hitObject.StartTime - Time) / oldBeatLength;
- hitObject.StartTime = (beat * newBeatLength) + Time;
+ hitObject.StartTime = (beat * BeatLength) + Time;
if (hitObject is not IHasRepeats && hitObject is IHasDuration hitObjectWithDuration)
- hitObjectWithDuration.Duration *= newBeatLength / BeatLength;
+ hitObjectWithDuration.Duration *= BeatLength / oldBeatLength;
}
}
}
diff --git a/osu.Game/Localisation/EditorStrings.cs b/osu.Game/Localisation/EditorStrings.cs
index 6ad12f54df..b604fc3889 100644
--- a/osu.Game/Localisation/EditorStrings.cs
+++ b/osu.Game/Localisation/EditorStrings.cs
@@ -39,6 +39,11 @@ namespace osu.Game.Localisation
///
public static LocalisableString SetPreviewPointToCurrent => new TranslatableString(getKey(@"set_preview_point_to_current"), @"Set preview point to current time");
+ ///
+ /// "Move already placed notes when changing the offset / BPM"
+ ///
+ public static LocalisableString AdjustNotesOnOffsetBPMChange => new TranslatableString(getKey(@"adjust_notes_on_offset_bpm_change"), @"Move already placed notes when changing the offset / BPM");
+
///
/// "For editing (.olz)"
///
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index 07c32983f5..af099413fd 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -367,7 +367,11 @@ namespace osu.Game.Screens.Edit
{
Items = new MenuItem[]
{
- new EditorMenuItem(EditorStrings.SetPreviewPointToCurrent, MenuItemType.Standard, SetPreviewPointToCurrentTime)
+ new EditorMenuItem(EditorStrings.SetPreviewPointToCurrent, MenuItemType.Standard, SetPreviewPointToCurrentTime),
+ new ToggleMenuItem(EditorStrings.AdjustNotesOnOffsetBPMChange)
+ {
+ State = { BindTarget = editorBeatmap.AdjustNotesOnOffsetBPMChange },
+ }
}
}
}
diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs
index 7a3ea474fb..42a3a6d3b2 100644
--- a/osu.Game/Screens/Edit/EditorBeatmap.cs
+++ b/osu.Game/Screens/Edit/EditorBeatmap.cs
@@ -88,6 +88,8 @@ namespace osu.Game.Screens.Edit
public BindableInt PreviewTime { get; }
+ public Bindable AdjustNotesOnOffsetBPMChange { get; } = new Bindable(false);
+
private readonly IBeatmapProcessor beatmapProcessor;
private readonly Dictionary> startTimeBindables = new Dictionary>();
diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
index 49d3df4aef..30e5919411 100644
--- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
+++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
@@ -37,8 +37,6 @@ namespace osu.Game.Screens.Edit.Timing
private MetronomeDisplay metronome = null!;
- private LabelledSwitchButton adjustPlacedNotes = null!;
-
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider, OsuColour colours)
{
@@ -65,7 +63,6 @@ namespace osu.Game.Screens.Edit.Timing
{
new Dimension(GridSizeMode.Absolute, 200),
new Dimension(GridSizeMode.Absolute, 50),
- new Dimension(GridSizeMode.Absolute, 50),
new Dimension(GridSizeMode.Absolute, TapButton.SIZE + padding),
},
Content = new[]
@@ -123,18 +120,6 @@ namespace osu.Game.Screens.Edit.Timing
},
},
new Drawable[]
- {
- new Container
- {
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Bottom = padding, Horizontal = padding },
- Children = new Drawable[]
- {
- adjustPlacedNotes = new LabelledSwitchButton { Label = "Move already placed notes\nwhen changing the offset/BPM" },
- }
- },
- },
- new Drawable[]
{
new Container
{
@@ -227,7 +212,7 @@ namespace osu.Game.Screens.Edit.Timing
foreach (var cp in currentGroupItems)
{
- if (adjustPlacedNotes.Current.Value && cp is TimingControlPoint tp)
+ if (beatmap.AdjustNotesOnOffsetBPMChange.Value && cp is TimingControlPoint tp)
tp.AdjustHitObjectOffset(beatmap, adjust);
beatmap.ControlPointInfo.Add(newOffset, cp);
}
@@ -249,12 +234,10 @@ namespace osu.Game.Screens.Edit.Timing
if (timing == null)
return;
- double newBeatLength = 60000 / (timing.BPM + adjust);
+ timing.BeatLength = 60000 / (timing.BPM + adjust);
- if (adjustPlacedNotes.Current.Value)
- timing.SetHitObjectBPM(beatmap, newBeatLength);
-
- timing.BeatLength = newBeatLength;
+ if (beatmap.AdjustNotesOnOffsetBPMChange.Value)
+ timing.SetHitObjectBPM(beatmap, 60000 / (timing.BPM - adjust));
beatmap.UpdateAllHitObjects();
}
From 33d0d4c8f22b284c784a55ab8e143781a580ed16 Mon Sep 17 00:00:00 2001
From: Aurelian
Date: Sun, 9 Jun 2024 11:29:25 +0200
Subject: [PATCH 0114/1255] Fixed certain UI elements not working for HitObject
BPM/Offset adjustment
---
osu.Game/Screens/Edit/Timing/GroupSection.cs | 7 +++++++
osu.Game/Screens/Edit/Timing/TapTimingControl.cs | 5 -----
osu.Game/Screens/Edit/Timing/TimingSection.cs | 11 +++++++++++
3 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/osu.Game/Screens/Edit/Timing/GroupSection.cs b/osu.Game/Screens/Edit/Timing/GroupSection.cs
index 487a871881..7d0eab1f7f 100644
--- a/osu.Game/Screens/Edit/Timing/GroupSection.cs
+++ b/osu.Game/Screens/Edit/Timing/GroupSection.cs
@@ -109,11 +109,18 @@ namespace osu.Game.Screens.Edit.Timing
Beatmap.ControlPointInfo.RemoveGroup(SelectedGroup.Value);
foreach (var cp in currentGroupItems)
+ {
+ // Only adjust hit object offsets if the group contains a timing control point
+ if (Beatmap.AdjustNotesOnOffsetBPMChange.Value && cp is TimingControlPoint tp)
+ tp.AdjustHitObjectOffset(Beatmap, time - SelectedGroup.Value.Time);
Beatmap.ControlPointInfo.Add(time, cp);
+ }
// the control point might not necessarily exist yet, if currentGroupItems was empty.
SelectedGroup.Value = Beatmap.ControlPointInfo.GroupAt(time, true);
+ Beatmap.UpdateAllHitObjects();
+
changeHandler?.EndChange();
}
}
diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
index 30e5919411..937235c7bc 100644
--- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
+++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -10,14 +9,11 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
-using osu.Framework.Utils;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Overlays;
-using osu.Game.Rulesets.Objects;
-using osu.Game.Rulesets.Objects.Types;
using osuTK;
namespace osu.Game.Screens.Edit.Timing
@@ -226,7 +222,6 @@ namespace osu.Game.Screens.Edit.Timing
beatmap.UpdateAllHitObjects();
}
-
private void adjustBpm(double adjust)
{
var timing = selectedGroup.Value?.ControlPoints.OfType().FirstOrDefault();
diff --git a/osu.Game/Screens/Edit/Timing/TimingSection.cs b/osu.Game/Screens/Edit/Timing/TimingSection.cs
index 2757753b07..24cf865ce3 100644
--- a/osu.Game/Screens/Edit/Timing/TimingSection.cs
+++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs
@@ -42,6 +42,17 @@ namespace osu.Game.Screens.Edit.Timing
{
if (!isRebinding) ChangeHandler?.SaveState();
}
+
+ bpmTextEntry.Bindable.BindValueChanged(val =>
+ {
+ if (ControlPoint.Value == null)
+ return;
+
+ ChangeHandler?.BeginChange();
+ ControlPoint.Value.SetHitObjectBPM(Beatmap, val.OldValue);
+ Beatmap.UpdateAllHitObjects();
+ ChangeHandler?.EndChange();
+ });
}
private bool isRebinding;
From 101887d3154306e53021352f47c609f90362c913 Mon Sep 17 00:00:00 2001
From: Aurelian
Date: Sun, 9 Jun 2024 18:04:27 +0200
Subject: [PATCH 0115/1255] Notes aren't adjusted if setting is off
---
osu.Game/Screens/Edit/Timing/TimingSection.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game/Screens/Edit/Timing/TimingSection.cs b/osu.Game/Screens/Edit/Timing/TimingSection.cs
index 24cf865ce3..1ae85e6c9e 100644
--- a/osu.Game/Screens/Edit/Timing/TimingSection.cs
+++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs
@@ -45,7 +45,7 @@ namespace osu.Game.Screens.Edit.Timing
bpmTextEntry.Bindable.BindValueChanged(val =>
{
- if (ControlPoint.Value == null)
+ if (!Beatmap.AdjustNotesOnOffsetBPMChange.Value || ControlPoint.Value == null)
return;
ChangeHandler?.BeginChange();
From 4339e2dc4afb5035221398a88cc04f6718d8d523 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Wed, 12 Jun 2024 12:41:07 +0200
Subject: [PATCH 0116/1255] Move `AudioLeadIn` out of `BeatmapInfo`
---
osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 4 ++--
osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs | 2 +-
osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs | 4 ++--
osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 2 +-
osu.Game.Tests/Visual/Gameplay/TestSceneReplayPlayer.cs | 2 +-
.../Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs | 4 ++--
osu.Game/Beatmaps/Beatmap.cs | 2 ++
osu.Game/Beatmaps/BeatmapConverter.cs | 1 +
osu.Game/Beatmaps/BeatmapImporter.cs | 1 -
osu.Game/Beatmaps/BeatmapInfo.cs | 2 --
osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +-
osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +-
osu.Game/Beatmaps/IBeatmap.cs | 2 ++
osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 6 ++++++
osu.Game/Screens/Edit/EditorBeatmap.cs | 6 ++++++
osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 4 ++--
16 files changed, 30 insertions(+), 16 deletions(-)
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index a4cd888823..9ffb3327b9 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -80,7 +80,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
var metadata = beatmap.Metadata;
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", metadata.AudioFile);
- Assert.AreEqual(0, beatmapInfo.AudioLeadIn);
+ Assert.AreEqual(0, beatmap.AudioLeadIn);
Assert.AreEqual(164471, metadata.PreviewTime);
Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
@@ -950,7 +950,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.Multiple(() =>
{
- Assert.That(decoded.BeatmapInfo.AudioLeadIn, Is.EqualTo(0));
+ Assert.That(decoded.AudioLeadIn, Is.EqualTo(0));
Assert.That(decoded.BeatmapInfo.StackLeniency, Is.EqualTo(0.7f));
Assert.That(decoded.BeatmapInfo.SpecialStyle, Is.False);
Assert.That(decoded.BeatmapInfo.LetterboxInBreaks, Is.False);
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
index 3764467047..3fd05b692d 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
@@ -51,7 +51,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
{
var beatmap = decodeAsJson(normal);
var beatmapInfo = beatmap.BeatmapInfo;
- Assert.AreEqual(0, beatmapInfo.AudioLeadIn);
+ Assert.AreEqual(0, beatmap.AudioLeadIn);
Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
Assert.AreEqual(false, beatmapInfo.SpecialStyle);
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs
index 5a71369976..a6b8e679b9 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs
@@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo)
{
- BeatmapInfo = { AudioLeadIn = leadIn }
+ AudioLeadIn = leadIn
});
checkFirstFrameTime(expectedStartTime);
@@ -130,7 +130,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
Text = $"GameplayStartTime: {DrawableRuleset.GameplayStartTime} "
+ $"FirstHitObjectTime: {FirstHitObjectTime} "
- + $"LeadInTime: {Beatmap.Value.BeatmapInfo.AudioLeadIn} "
+ + $"LeadInTime: {Beatmap.Value.Beatmap.AudioLeadIn} "
+ $"FirstFrameClockTime: {FirstFrameClockTime}"
});
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
index 1949808dfe..c17405c2ec 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
@@ -135,7 +135,7 @@ namespace osu.Game.Tests.Visual.Gameplay
var workingBeatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
// Add intro time to test quick retry skipping (TestQuickRetry).
- workingBeatmap.BeatmapInfo.AudioLeadIn = 60000;
+ workingBeatmap.Beatmap.AudioLeadIn = 60000;
// Set up data for testing disclaimer display.
workingBeatmap.BeatmapInfo.EpilepsyWarning = epilepsyWarning ?? false;
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayPlayer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayPlayer.cs
index ae10207de0..81dd23661c 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayPlayer.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayPlayer.cs
@@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo)
{
- BeatmapInfo = { AudioLeadIn = 60000 }
+ AudioLeadIn = 60000
});
AddUntilStep("wait for skip overlay", () => Player.ChildrenOfType().First().IsButtonVisible);
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
index 2b17f91e68..6108260481 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
@@ -406,13 +406,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
///
- /// Tests spectating with a beatmap that has a high value.
+ /// Tests spectating with a beatmap that has a high value.
///
/// This test is not intended not to check the correct initial time value, but only to guard against
/// gameplay potentially getting stuck in a stopped state due to lead in time being present.
///
[Test]
- public void TestAudioLeadIn() => testLeadIn(b => b.BeatmapInfo.AudioLeadIn = 2000);
+ public void TestAudioLeadIn() => testLeadIn(b => b.Beatmap.AudioLeadIn = 2000);
///
/// Tests spectating with a beatmap that has a storyboard element with a negative start time (i.e. intro storyboard element).
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index ae77e4adcf..f3ad02558a 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -114,6 +114,8 @@ namespace osu.Game.Beatmaps
return mostCommon.beatLength;
}
+ public double AudioLeadIn { get; set; }
+
IBeatmap IBeatmap.Clone() => Clone();
public Beatmap Clone() => (Beatmap)MemberwiseClone();
diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs
index b68c80d4b3..f33cdaf81f 100644
--- a/osu.Game/Beatmaps/BeatmapConverter.cs
+++ b/osu.Game/Beatmaps/BeatmapConverter.cs
@@ -67,6 +67,7 @@ namespace osu.Game.Beatmaps
beatmap.HitObjects = convertHitObjects(original.HitObjects, original, cancellationToken).OrderBy(s => s.StartTime).ToList();
beatmap.Breaks = original.Breaks;
beatmap.UnhandledEventLines = original.UnhandledEventLines;
+ beatmap.AudioLeadIn = original.AudioLeadIn;
return beatmap;
}
diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs
index 2137f33e77..b8e253527b 100644
--- a/osu.Game/Beatmaps/BeatmapImporter.cs
+++ b/osu.Game/Beatmaps/BeatmapImporter.cs
@@ -414,7 +414,6 @@ namespace osu.Game.Beatmaps
Hash = hash,
DifficultyName = decodedInfo.DifficultyName,
OnlineID = decodedInfo.OnlineID,
- AudioLeadIn = decodedInfo.AudioLeadIn,
StackLeniency = decodedInfo.StackLeniency,
SpecialStyle = decodedInfo.SpecialStyle,
LetterboxInBreaks = decodedInfo.LetterboxInBreaks,
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index 425fd98d27..e1580dc74e 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -138,8 +138,6 @@ namespace osu.Game.Beatmaps
#region Properties we may not want persisted (but also maybe no harm?)
- public double AudioLeadIn { get; set; }
-
public float StackLeniency { get; set; } = 0.7f;
public bool SpecialStyle { get; set; }
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index c2f4097889..5966658c93 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -238,7 +238,7 @@ namespace osu.Game.Beatmaps.Formats
break;
case @"AudioLeadIn":
- beatmap.BeatmapInfo.AudioLeadIn = Parsing.ParseInt(pair.Value);
+ beatmap.AudioLeadIn = Parsing.ParseInt(pair.Value);
break;
case @"PreviewTime":
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index 186b565c39..072223c8fb 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -79,7 +79,7 @@ namespace osu.Game.Beatmaps.Formats
writer.WriteLine("[General]");
if (!string.IsNullOrEmpty(beatmap.Metadata.AudioFile)) writer.WriteLine(FormattableString.Invariant($"AudioFilename: {Path.GetFileName(beatmap.Metadata.AudioFile)}"));
- writer.WriteLine(FormattableString.Invariant($"AudioLeadIn: {beatmap.BeatmapInfo.AudioLeadIn}"));
+ writer.WriteLine(FormattableString.Invariant($"AudioLeadIn: {beatmap.AudioLeadIn}"));
writer.WriteLine(FormattableString.Invariant($"PreviewTime: {beatmap.Metadata.PreviewTime}"));
writer.WriteLine(FormattableString.Invariant($"Countdown: {(int)beatmap.BeatmapInfo.Countdown}"));
writer.WriteLine(FormattableString.Invariant(
diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs
index 5cc38e5b84..993155a32e 100644
--- a/osu.Game/Beatmaps/IBeatmap.cs
+++ b/osu.Game/Beatmaps/IBeatmap.cs
@@ -68,6 +68,8 @@ namespace osu.Game.Beatmaps
///
double GetMostCommonBeatLength();
+ double AudioLeadIn { get; internal set; }
+
///
/// Creates a shallow-clone of this beatmap and returns it.
///
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
index d37cfc28b9..5557051f05 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
@@ -337,6 +337,12 @@ namespace osu.Game.Rulesets.Difficulty
public double GetMostCommonBeatLength() => baseBeatmap.GetMostCommonBeatLength();
public IBeatmap Clone() => new ProgressiveCalculationBeatmap(baseBeatmap.Clone());
+ public double AudioLeadIn
+ {
+ get => baseBeatmap.AudioLeadIn;
+ set => baseBeatmap.AudioLeadIn = value;
+ }
+
#endregion
}
}
diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs
index 5be1d27805..7392c66a26 100644
--- a/osu.Game/Screens/Edit/EditorBeatmap.cs
+++ b/osu.Game/Screens/Edit/EditorBeatmap.cs
@@ -184,6 +184,12 @@ namespace osu.Game.Screens.Edit
public double GetMostCommonBeatLength() => PlayableBeatmap.GetMostCommonBeatLength();
+ public double AudioLeadIn
+ {
+ get => PlayableBeatmap.AudioLeadIn;
+ set => PlayableBeatmap.AudioLeadIn = value;
+ }
+
public IBeatmap Clone() => (EditorBeatmap)MemberwiseClone();
private IList mutableHitObjects => (IList)PlayableBeatmap.HitObjects;
diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs
index b2f0ae5561..3851806788 100644
--- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs
+++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs
@@ -95,8 +95,8 @@ namespace osu.Game.Screens.Play
// some beatmaps specify a current lead-in time which should be used instead of the ruleset-provided value when available.
// this is not available as an option in the live editor but can still be applied via .osu editing.
double firstHitObjectTime = beatmap.Beatmap.HitObjects.First().StartTime;
- if (beatmap.BeatmapInfo.AudioLeadIn > 0)
- time = Math.Min(time, firstHitObjectTime - beatmap.BeatmapInfo.AudioLeadIn);
+ if (beatmap.Beatmap.AudioLeadIn > 0)
+ time = Math.Min(time, firstHitObjectTime - beatmap.Beatmap.AudioLeadIn);
return time;
}
From 0a4560a03e0c5dfccecc6be661586d378d3b88aa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Wed, 12 Jun 2024 12:50:41 +0200
Subject: [PATCH 0117/1255] Move `StackLeniency` out of `BeatmapInfo`
---
.../Mods/TestSceneOsuModFlashlight.cs | 5 +----
.../Mods/TestSceneOsuModRandom.cs | 2 +-
.../TestSceneSliderLateHitJudgement.cs | 2 +-
.../Beatmaps/OsuBeatmapProcessor.cs | 14 +++++++-------
.../Edit/Setup/OsuSetupSection.cs | 4 ++--
.../Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 4 ++--
.../Beatmaps/Formats/OsuJsonDecoderTest.cs | 2 +-
osu.Game/Beatmaps/Beatmap.cs | 2 ++
osu.Game/Beatmaps/BeatmapConverter.cs | 1 +
osu.Game/Beatmaps/BeatmapImporter.cs | 1 -
osu.Game/Beatmaps/BeatmapInfo.cs | 2 --
osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +-
osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +-
osu.Game/Beatmaps/IBeatmap.cs | 2 ++
.../Rulesets/Difficulty/DifficultyCalculator.cs | 6 ++++++
osu.Game/Screens/Edit/EditorBeatmap.cs | 6 ++++++
16 files changed, 34 insertions(+), 23 deletions(-)
diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs
index 075fdd88ca..1a3b0310f7 100644
--- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs
@@ -83,10 +83,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
})
}
},
- BeatmapInfo =
- {
- StackLeniency = 0,
- }
+ StackLeniency = 0,
},
ReplayFrames = new List
{
diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModRandom.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModRandom.cs
index 060a845137..75a5d36f32 100644
--- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModRandom.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModRandom.cs
@@ -74,12 +74,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
{
BeatmapInfo = new BeatmapInfo
{
- StackLeniency = 0,
Difficulty = new BeatmapDifficulty
{
ApproachRate = 8.5f
}
},
+ StackLeniency = 0,
ControlPointInfo = controlPointInfo
};
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs
index 1ba4a60b75..d089e924ca 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs
@@ -465,7 +465,7 @@ namespace osu.Game.Rulesets.Osu.Tests
private void performTest(List frames, Beatmap beatmap)
{
beatmap.BeatmapInfo.Ruleset = new OsuRuleset().RulesetInfo;
- beatmap.BeatmapInfo.StackLeniency = 0;
+ beatmap.StackLeniency = 0;
beatmap.BeatmapInfo.Difficulty = new BeatmapDifficulty
{
SliderMultiplier = 4,
diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs
index d335913586..9cc22b764f 100644
--- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs
+++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs
@@ -51,13 +51,13 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
h.StackHeight = 0;
if (Beatmap.BeatmapInfo.BeatmapVersion >= 6)
- applyStacking(Beatmap.BeatmapInfo, hitObjects, 0, hitObjects.Count - 1);
+ applyStacking(Beatmap, hitObjects, 0, hitObjects.Count - 1);
else
- applyStackingOld(Beatmap.BeatmapInfo, hitObjects);
+ applyStackingOld(Beatmap, hitObjects);
}
}
- private void applyStacking(BeatmapInfo beatmapInfo, List hitObjects, int startIndex, int endIndex)
+ private void applyStacking(IBeatmap beatmap, List hitObjects, int startIndex, int endIndex)
{
ArgumentOutOfRangeException.ThrowIfGreaterThan(startIndex, endIndex);
ArgumentOutOfRangeException.ThrowIfNegative(startIndex);
@@ -82,7 +82,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
continue;
double endTime = stackBaseObject.GetEndTime();
- double stackThreshold = objectN.TimePreempt * beatmapInfo.StackLeniency;
+ double stackThreshold = objectN.TimePreempt * beatmap.StackLeniency;
if (objectN.StartTime - endTime > stackThreshold)
// We are no longer within stacking range of the next object.
@@ -127,7 +127,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
OsuHitObject objectI = hitObjects[i];
if (objectI.StackHeight != 0 || objectI is Spinner) continue;
- double stackThreshold = objectI.TimePreempt * beatmapInfo.StackLeniency;
+ double stackThreshold = objectI.TimePreempt * beatmap.StackLeniency;
/* If this object is a hitcircle, then we enter this "special" case.
* It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider.
@@ -209,7 +209,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
}
}
- private void applyStackingOld(BeatmapInfo beatmapInfo, List hitObjects)
+ private void applyStackingOld(IBeatmap beatmap, List hitObjects)
{
for (int i = 0; i < hitObjects.Count; i++)
{
@@ -223,7 +223,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
for (int j = i + 1; j < hitObjects.Count; j++)
{
- double stackThreshold = hitObjects[i].TimePreempt * beatmapInfo.StackLeniency;
+ double stackThreshold = hitObjects[i].TimePreempt * beatmap.StackLeniency;
if (hitObjects[j].StartTime - stackThreshold > startTime)
break;
diff --git a/osu.Game.Rulesets.Osu/Edit/Setup/OsuSetupSection.cs b/osu.Game.Rulesets.Osu/Edit/Setup/OsuSetupSection.cs
index 552b887081..e1a588a32a 100644
--- a/osu.Game.Rulesets.Osu/Edit/Setup/OsuSetupSection.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Setup/OsuSetupSection.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Setup
{
Label = "Stack Leniency",
Description = "In play mode, osu! automatically stacks notes which occur at the same location. Increasing this value means it is more likely to snap notes of further time-distance.",
- Current = new BindableFloat(Beatmap.BeatmapInfo.StackLeniency)
+ Current = new BindableFloat(Beatmap.StackLeniency)
{
Default = 0.7f,
MinValue = 0,
@@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Setup
private void updateBeatmap()
{
- Beatmap.BeatmapInfo.StackLeniency = stackLeniency.Current.Value;
+ Beatmap.StackLeniency = stackLeniency.Current.Value;
Beatmap.UpdateAllHitObjects();
Beatmap.SaveState();
}
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index 9ffb3327b9..565c481920 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -82,7 +82,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", metadata.AudioFile);
Assert.AreEqual(0, beatmap.AudioLeadIn);
Assert.AreEqual(164471, metadata.PreviewTime);
- Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
+ Assert.AreEqual(0.7f, beatmap.StackLeniency);
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
Assert.IsFalse(beatmapInfo.LetterboxInBreaks);
Assert.IsFalse(beatmapInfo.SpecialStyle);
@@ -951,7 +951,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.Multiple(() =>
{
Assert.That(decoded.AudioLeadIn, Is.EqualTo(0));
- Assert.That(decoded.BeatmapInfo.StackLeniency, Is.EqualTo(0.7f));
+ Assert.That(decoded.StackLeniency, Is.EqualTo(0.7f));
Assert.That(decoded.BeatmapInfo.SpecialStyle, Is.False);
Assert.That(decoded.BeatmapInfo.LetterboxInBreaks, Is.False);
Assert.That(decoded.BeatmapInfo.WidescreenStoryboard, Is.False);
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
index 3fd05b692d..5d2d9e006e 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
@@ -52,7 +52,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
var beatmap = decodeAsJson(normal);
var beatmapInfo = beatmap.BeatmapInfo;
Assert.AreEqual(0, beatmap.AudioLeadIn);
- Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
+ Assert.AreEqual(0.7f, beatmap.StackLeniency);
Assert.AreEqual(false, beatmapInfo.SpecialStyle);
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks);
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index f3ad02558a..ecee6e3416 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -116,6 +116,8 @@ namespace osu.Game.Beatmaps
public double AudioLeadIn { get; set; }
+ public float StackLeniency { get; set; } = 0.7f;
+
IBeatmap IBeatmap.Clone() => Clone();
public Beatmap Clone() => (Beatmap)MemberwiseClone();
diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs
index f33cdaf81f..a5e9025404 100644
--- a/osu.Game/Beatmaps/BeatmapConverter.cs
+++ b/osu.Game/Beatmaps/BeatmapConverter.cs
@@ -68,6 +68,7 @@ namespace osu.Game.Beatmaps
beatmap.Breaks = original.Breaks;
beatmap.UnhandledEventLines = original.UnhandledEventLines;
beatmap.AudioLeadIn = original.AudioLeadIn;
+ beatmap.StackLeniency = original.StackLeniency;
return beatmap;
}
diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs
index b8e253527b..e589b0a754 100644
--- a/osu.Game/Beatmaps/BeatmapImporter.cs
+++ b/osu.Game/Beatmaps/BeatmapImporter.cs
@@ -414,7 +414,6 @@ namespace osu.Game.Beatmaps
Hash = hash,
DifficultyName = decodedInfo.DifficultyName,
OnlineID = decodedInfo.OnlineID,
- StackLeniency = decodedInfo.StackLeniency,
SpecialStyle = decodedInfo.SpecialStyle,
LetterboxInBreaks = decodedInfo.LetterboxInBreaks,
WidescreenStoryboard = decodedInfo.WidescreenStoryboard,
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index e1580dc74e..fa2911438b 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -138,8 +138,6 @@ namespace osu.Game.Beatmaps
#region Properties we may not want persisted (but also maybe no harm?)
- public float StackLeniency { get; set; } = 0.7f;
-
public bool SpecialStyle { get; set; }
public bool LetterboxInBreaks { get; set; }
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 5966658c93..86552b21dd 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -255,7 +255,7 @@ namespace osu.Game.Beatmaps.Formats
break;
case @"StackLeniency":
- beatmap.BeatmapInfo.StackLeniency = Parsing.ParseFloat(pair.Value);
+ beatmap.StackLeniency = Parsing.ParseFloat(pair.Value);
break;
case @"Mode":
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index 072223c8fb..8c371026ff 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -84,7 +84,7 @@ namespace osu.Game.Beatmaps.Formats
writer.WriteLine(FormattableString.Invariant($"Countdown: {(int)beatmap.BeatmapInfo.Countdown}"));
writer.WriteLine(FormattableString.Invariant(
$"SampleSet: {toLegacySampleBank(((beatmap.ControlPointInfo as LegacyControlPointInfo)?.SamplePoints.FirstOrDefault() ?? SampleControlPoint.DEFAULT).SampleBank)}"));
- writer.WriteLine(FormattableString.Invariant($"StackLeniency: {beatmap.BeatmapInfo.StackLeniency}"));
+ writer.WriteLine(FormattableString.Invariant($"StackLeniency: {beatmap.StackLeniency}"));
writer.WriteLine(FormattableString.Invariant($"Mode: {onlineRulesetID}"));
writer.WriteLine(FormattableString.Invariant($"LetterboxInBreaks: {(beatmap.BeatmapInfo.LetterboxInBreaks ? '1' : '0')}"));
// if (beatmap.BeatmapInfo.UseSkinSprites)
diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs
index 993155a32e..28d601620a 100644
--- a/osu.Game/Beatmaps/IBeatmap.cs
+++ b/osu.Game/Beatmaps/IBeatmap.cs
@@ -70,6 +70,8 @@ namespace osu.Game.Beatmaps
double AudioLeadIn { get; internal set; }
+ float StackLeniency { get; internal set; }
+
///
/// Creates a shallow-clone of this beatmap and returns it.
///
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
index 5557051f05..616d6d0848 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
@@ -343,6 +343,12 @@ namespace osu.Game.Rulesets.Difficulty
set => baseBeatmap.AudioLeadIn = value;
}
+ public float StackLeniency
+ {
+ get => baseBeatmap.StackLeniency;
+ set => baseBeatmap.StackLeniency = value;
+ }
+
#endregion
}
}
diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs
index 7392c66a26..c02a22ae03 100644
--- a/osu.Game/Screens/Edit/EditorBeatmap.cs
+++ b/osu.Game/Screens/Edit/EditorBeatmap.cs
@@ -190,6 +190,12 @@ namespace osu.Game.Screens.Edit
set => PlayableBeatmap.AudioLeadIn = value;
}
+ public float StackLeniency
+ {
+ get => PlayableBeatmap.StackLeniency;
+ set => PlayableBeatmap.StackLeniency = value;
+ }
+
public IBeatmap Clone() => (EditorBeatmap)MemberwiseClone();
private IList mutableHitObjects => (IList)PlayableBeatmap.HitObjects;
From 011c2e3651fe1485eca8663697630777a848a440 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Wed, 12 Jun 2024 13:12:30 +0200
Subject: [PATCH 0118/1255] Move `SpecialStyle` out of `BeatmapInfo`
---
osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs | 4 ++--
osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 4 ++--
osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs | 2 +-
osu.Game/Beatmaps/Beatmap.cs | 2 ++
osu.Game/Beatmaps/BeatmapConverter.cs | 1 +
osu.Game/Beatmaps/BeatmapImporter.cs | 1 -
osu.Game/Beatmaps/BeatmapInfo.cs | 2 --
osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +-
osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +-
osu.Game/Beatmaps/IBeatmap.cs | 2 ++
osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 6 ++++++
osu.Game/Screens/Edit/EditorBeatmap.cs | 6 ++++++
12 files changed, 24 insertions(+), 10 deletions(-)
diff --git a/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs b/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs
index d5a9a311bc..8778c18c38 100644
--- a/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs
+++ b/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup
{
Label = "Use special (N+1) style",
Description = "Changes one column to act as a classic \"scratch\" or \"special\" column, which can be moved around by the user's skin (to the left/right/centre). Generally used in 6K (5+1) or 8K (7+1) configurations.",
- Current = { Value = Beatmap.BeatmapInfo.SpecialStyle }
+ Current = { Value = Beatmap.SpecialStyle }
}
};
}
@@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup
private void updateBeatmap()
{
- Beatmap.BeatmapInfo.SpecialStyle = specialStyle.Current.Value;
+ Beatmap.SpecialStyle = specialStyle.Current.Value;
Beatmap.SaveState();
}
}
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index 565c481920..ad3721220a 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -85,7 +85,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(0.7f, beatmap.StackLeniency);
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
Assert.IsFalse(beatmapInfo.LetterboxInBreaks);
- Assert.IsFalse(beatmapInfo.SpecialStyle);
+ Assert.IsFalse(beatmap.SpecialStyle);
Assert.IsFalse(beatmapInfo.WidescreenStoryboard);
Assert.IsFalse(beatmapInfo.SamplesMatchPlaybackRate);
Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown);
@@ -952,7 +952,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
{
Assert.That(decoded.AudioLeadIn, Is.EqualTo(0));
Assert.That(decoded.StackLeniency, Is.EqualTo(0.7f));
- Assert.That(decoded.BeatmapInfo.SpecialStyle, Is.False);
+ Assert.That(decoded.SpecialStyle, Is.False);
Assert.That(decoded.BeatmapInfo.LetterboxInBreaks, Is.False);
Assert.That(decoded.BeatmapInfo.WidescreenStoryboard, Is.False);
Assert.That(decoded.BeatmapInfo.EpilepsyWarning, Is.False);
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
index 5d2d9e006e..18f4651e94 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
@@ -53,7 +53,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
var beatmapInfo = beatmap.BeatmapInfo;
Assert.AreEqual(0, beatmap.AudioLeadIn);
Assert.AreEqual(0.7f, beatmap.StackLeniency);
- Assert.AreEqual(false, beatmapInfo.SpecialStyle);
+ Assert.AreEqual(false, beatmap.SpecialStyle);
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks);
Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard);
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index ecee6e3416..f06d884bd1 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -118,6 +118,8 @@ namespace osu.Game.Beatmaps
public float StackLeniency { get; set; } = 0.7f;
+ public bool SpecialStyle { get; set; }
+
IBeatmap IBeatmap.Clone() => Clone();
public Beatmap Clone() => (Beatmap)MemberwiseClone();
diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs
index a5e9025404..3b3b68de0a 100644
--- a/osu.Game/Beatmaps/BeatmapConverter.cs
+++ b/osu.Game/Beatmaps/BeatmapConverter.cs
@@ -69,6 +69,7 @@ namespace osu.Game.Beatmaps
beatmap.UnhandledEventLines = original.UnhandledEventLines;
beatmap.AudioLeadIn = original.AudioLeadIn;
beatmap.StackLeniency = original.StackLeniency;
+ beatmap.SpecialStyle = original.SpecialStyle;
return beatmap;
}
diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs
index e589b0a754..435a282b52 100644
--- a/osu.Game/Beatmaps/BeatmapImporter.cs
+++ b/osu.Game/Beatmaps/BeatmapImporter.cs
@@ -414,7 +414,6 @@ namespace osu.Game.Beatmaps
Hash = hash,
DifficultyName = decodedInfo.DifficultyName,
OnlineID = decodedInfo.OnlineID,
- SpecialStyle = decodedInfo.SpecialStyle,
LetterboxInBreaks = decodedInfo.LetterboxInBreaks,
WidescreenStoryboard = decodedInfo.WidescreenStoryboard,
EpilepsyWarning = decodedInfo.EpilepsyWarning,
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index fa2911438b..4a1fa9b0b4 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -138,8 +138,6 @@ namespace osu.Game.Beatmaps
#region Properties we may not want persisted (but also maybe no harm?)
- public bool SpecialStyle { get; set; }
-
public bool LetterboxInBreaks { get; set; }
public bool WidescreenStoryboard { get; set; } = true;
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 86552b21dd..ea34c7d924 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -289,7 +289,7 @@ namespace osu.Game.Beatmaps.Formats
break;
case @"SpecialStyle":
- beatmap.BeatmapInfo.SpecialStyle = Parsing.ParseInt(pair.Value) == 1;
+ beatmap.SpecialStyle = Parsing.ParseInt(pair.Value) == 1;
break;
case @"WidescreenStoryboard":
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index 8c371026ff..805ce49ca3 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -100,7 +100,7 @@ namespace osu.Game.Beatmaps.Formats
if (beatmap.BeatmapInfo.CountdownOffset > 0)
writer.WriteLine(FormattableString.Invariant($@"CountdownOffset: {beatmap.BeatmapInfo.CountdownOffset}"));
if (onlineRulesetID == 3)
- writer.WriteLine(FormattableString.Invariant($"SpecialStyle: {(beatmap.BeatmapInfo.SpecialStyle ? '1' : '0')}"));
+ writer.WriteLine(FormattableString.Invariant($"SpecialStyle: {(beatmap.SpecialStyle ? '1' : '0')}"));
writer.WriteLine(FormattableString.Invariant($"WidescreenStoryboard: {(beatmap.BeatmapInfo.WidescreenStoryboard ? '1' : '0')}"));
if (beatmap.BeatmapInfo.SamplesMatchPlaybackRate)
writer.WriteLine(@"SamplesMatchPlaybackRate: 1");
diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs
index 28d601620a..3ac48c09b4 100644
--- a/osu.Game/Beatmaps/IBeatmap.cs
+++ b/osu.Game/Beatmaps/IBeatmap.cs
@@ -72,6 +72,8 @@ namespace osu.Game.Beatmaps
float StackLeniency { get; internal set; }
+ bool SpecialStyle { get; internal set; }
+
///
/// Creates a shallow-clone of this beatmap and returns it.
///
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
index 616d6d0848..87a20eec0b 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
@@ -349,6 +349,12 @@ namespace osu.Game.Rulesets.Difficulty
set => baseBeatmap.StackLeniency = value;
}
+ public bool SpecialStyle
+ {
+ get => baseBeatmap.SpecialStyle;
+ set => baseBeatmap.SpecialStyle = value;
+ }
+
#endregion
}
}
diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs
index c02a22ae03..96216c6b1a 100644
--- a/osu.Game/Screens/Edit/EditorBeatmap.cs
+++ b/osu.Game/Screens/Edit/EditorBeatmap.cs
@@ -196,6 +196,12 @@ namespace osu.Game.Screens.Edit
set => PlayableBeatmap.StackLeniency = value;
}
+ public bool SpecialStyle
+ {
+ get => PlayableBeatmap.SpecialStyle;
+ set => PlayableBeatmap.SpecialStyle = value;
+ }
+
public IBeatmap Clone() => (EditorBeatmap)MemberwiseClone();
private IList mutableHitObjects => (IList)PlayableBeatmap.HitObjects;
From a6b7600bf2b85f154624d8893fd9e658b098ab3f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Wed, 12 Jun 2024 13:15:41 +0200
Subject: [PATCH 0119/1255] Move `LetterboxInBreaks` out of `BeatmapInfo`
---
osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 4 ++--
osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs | 2 +-
osu.Game/Beatmaps/Beatmap.cs | 2 ++
osu.Game/Beatmaps/BeatmapConverter.cs | 1 +
osu.Game/Beatmaps/BeatmapImporter.cs | 1 -
osu.Game/Beatmaps/BeatmapInfo.cs | 2 --
osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +-
osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +-
osu.Game/Beatmaps/IBeatmap.cs | 2 ++
osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 6 ++++++
osu.Game/Screens/Edit/EditorBeatmap.cs | 6 ++++++
osu.Game/Screens/Edit/Setup/DesignSection.cs | 4 ++--
osu.Game/Screens/Play/Player.cs | 2 +-
13 files changed, 25 insertions(+), 11 deletions(-)
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index ad3721220a..103bafb2d8 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -84,7 +84,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(164471, metadata.PreviewTime);
Assert.AreEqual(0.7f, beatmap.StackLeniency);
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
- Assert.IsFalse(beatmapInfo.LetterboxInBreaks);
+ Assert.IsFalse(beatmap.LetterboxInBreaks);
Assert.IsFalse(beatmap.SpecialStyle);
Assert.IsFalse(beatmapInfo.WidescreenStoryboard);
Assert.IsFalse(beatmapInfo.SamplesMatchPlaybackRate);
@@ -953,7 +953,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.That(decoded.AudioLeadIn, Is.EqualTo(0));
Assert.That(decoded.StackLeniency, Is.EqualTo(0.7f));
Assert.That(decoded.SpecialStyle, Is.False);
- Assert.That(decoded.BeatmapInfo.LetterboxInBreaks, Is.False);
+ Assert.That(decoded.LetterboxInBreaks, Is.False);
Assert.That(decoded.BeatmapInfo.WidescreenStoryboard, Is.False);
Assert.That(decoded.BeatmapInfo.EpilepsyWarning, Is.False);
Assert.That(decoded.BeatmapInfo.SamplesMatchPlaybackRate, Is.False);
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
index 18f4651e94..c1c996fd42 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
@@ -55,7 +55,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(0.7f, beatmap.StackLeniency);
Assert.AreEqual(false, beatmap.SpecialStyle);
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
- Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks);
+ Assert.AreEqual(false, beatmap.LetterboxInBreaks);
Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard);
Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown);
Assert.AreEqual(0, beatmapInfo.CountdownOffset);
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index f06d884bd1..614bb4f42a 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -120,6 +120,8 @@ namespace osu.Game.Beatmaps
public bool SpecialStyle { get; set; }
+ public bool LetterboxInBreaks { get; set; }
+
IBeatmap IBeatmap.Clone() => Clone();
public Beatmap Clone() => (Beatmap)MemberwiseClone();
diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs
index 3b3b68de0a..a56ce58532 100644
--- a/osu.Game/Beatmaps/BeatmapConverter.cs
+++ b/osu.Game/Beatmaps/BeatmapConverter.cs
@@ -70,6 +70,7 @@ namespace osu.Game.Beatmaps
beatmap.AudioLeadIn = original.AudioLeadIn;
beatmap.StackLeniency = original.StackLeniency;
beatmap.SpecialStyle = original.SpecialStyle;
+ beatmap.LetterboxInBreaks = original.LetterboxInBreaks;
return beatmap;
}
diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs
index 435a282b52..c54eece9f8 100644
--- a/osu.Game/Beatmaps/BeatmapImporter.cs
+++ b/osu.Game/Beatmaps/BeatmapImporter.cs
@@ -414,7 +414,6 @@ namespace osu.Game.Beatmaps
Hash = hash,
DifficultyName = decodedInfo.DifficultyName,
OnlineID = decodedInfo.OnlineID,
- LetterboxInBreaks = decodedInfo.LetterboxInBreaks,
WidescreenStoryboard = decodedInfo.WidescreenStoryboard,
EpilepsyWarning = decodedInfo.EpilepsyWarning,
SamplesMatchPlaybackRate = decodedInfo.SamplesMatchPlaybackRate,
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index 4a1fa9b0b4..fafca5e014 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -138,8 +138,6 @@ namespace osu.Game.Beatmaps
#region Properties we may not want persisted (but also maybe no harm?)
- public bool LetterboxInBreaks { get; set; }
-
public bool WidescreenStoryboard { get; set; } = true;
public bool EpilepsyWarning { get; set; }
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index ea34c7d924..b8469f27dd 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -285,7 +285,7 @@ namespace osu.Game.Beatmaps.Formats
break;
case @"LetterboxInBreaks":
- beatmap.BeatmapInfo.LetterboxInBreaks = Parsing.ParseInt(pair.Value) == 1;
+ beatmap.LetterboxInBreaks = Parsing.ParseInt(pair.Value) == 1;
break;
case @"SpecialStyle":
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index 805ce49ca3..5a3fd0a2f3 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -86,7 +86,7 @@ namespace osu.Game.Beatmaps.Formats
$"SampleSet: {toLegacySampleBank(((beatmap.ControlPointInfo as LegacyControlPointInfo)?.SamplePoints.FirstOrDefault() ?? SampleControlPoint.DEFAULT).SampleBank)}"));
writer.WriteLine(FormattableString.Invariant($"StackLeniency: {beatmap.StackLeniency}"));
writer.WriteLine(FormattableString.Invariant($"Mode: {onlineRulesetID}"));
- writer.WriteLine(FormattableString.Invariant($"LetterboxInBreaks: {(beatmap.BeatmapInfo.LetterboxInBreaks ? '1' : '0')}"));
+ writer.WriteLine(FormattableString.Invariant($"LetterboxInBreaks: {(beatmap.LetterboxInBreaks ? '1' : '0')}"));
// if (beatmap.BeatmapInfo.UseSkinSprites)
// writer.WriteLine(@"UseSkinSprites: 1");
// if (b.AlwaysShowPlayfield)
diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs
index 3ac48c09b4..e5562b608e 100644
--- a/osu.Game/Beatmaps/IBeatmap.cs
+++ b/osu.Game/Beatmaps/IBeatmap.cs
@@ -74,6 +74,8 @@ namespace osu.Game.Beatmaps
bool SpecialStyle { get; internal set; }
+ bool LetterboxInBreaks { get; internal set; }
+
///
/// Creates a shallow-clone of this beatmap and returns it.
///
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
index 87a20eec0b..04dcb2a552 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
@@ -355,6 +355,12 @@ namespace osu.Game.Rulesets.Difficulty
set => baseBeatmap.SpecialStyle = value;
}
+ public bool LetterboxInBreaks
+ {
+ get => baseBeatmap.LetterboxInBreaks;
+ set => baseBeatmap.LetterboxInBreaks = value;
+ }
+
#endregion
}
}
diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs
index 96216c6b1a..7470505712 100644
--- a/osu.Game/Screens/Edit/EditorBeatmap.cs
+++ b/osu.Game/Screens/Edit/EditorBeatmap.cs
@@ -202,6 +202,12 @@ namespace osu.Game.Screens.Edit
set => PlayableBeatmap.SpecialStyle = value;
}
+ public bool LetterboxInBreaks
+ {
+ get => PlayableBeatmap.LetterboxInBreaks;
+ set => PlayableBeatmap.LetterboxInBreaks = value;
+ }
+
public IBeatmap Clone() => (EditorBeatmap)MemberwiseClone();
private IList mutableHitObjects => (IList)PlayableBeatmap.HitObjects;
diff --git a/osu.Game/Screens/Edit/Setup/DesignSection.cs b/osu.Game/Screens/Edit/Setup/DesignSection.cs
index b05a073146..c1fe7f405d 100644
--- a/osu.Game/Screens/Edit/Setup/DesignSection.cs
+++ b/osu.Game/Screens/Edit/Setup/DesignSection.cs
@@ -80,7 +80,7 @@ namespace osu.Game.Screens.Edit.Setup
{
Label = EditorSetupStrings.LetterboxDuringBreaks,
Description = EditorSetupStrings.LetterboxDuringBreaksDescription,
- Current = { Value = Beatmap.BeatmapInfo.LetterboxInBreaks }
+ Current = { Value = Beatmap.LetterboxInBreaks }
},
samplesMatchPlaybackRate = new LabelledSwitchButton
{
@@ -123,7 +123,7 @@ namespace osu.Game.Screens.Edit.Setup
Beatmap.BeatmapInfo.WidescreenStoryboard = widescreenSupport.Current.Value;
Beatmap.BeatmapInfo.EpilepsyWarning = epilepsyWarning.Current.Value;
- Beatmap.BeatmapInfo.LetterboxInBreaks = letterboxDuringBreaks.Current.Value;
+ Beatmap.LetterboxInBreaks = letterboxDuringBreaks.Current.Value;
Beatmap.BeatmapInfo.SamplesMatchPlaybackRate = samplesMatchPlaybackRate.Current.Value;
Beatmap.SaveState();
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index 42ff1d74f3..eaadc236f7 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -430,7 +430,7 @@ namespace osu.Game.Screens.Play
Children = new[]
{
DimmableStoryboard.OverlayLayerContainer.CreateProxy(),
- BreakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
+ BreakOverlay = new BreakOverlay(working.Beatmap.LetterboxInBreaks, ScoreProcessor)
{
Clock = DrawableRuleset.FrameStableClock,
ProcessCustomClock = false,
From 1ab86ebd249e31812ee07af2191957d6115a02c8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Wed, 12 Jun 2024 13:23:53 +0200
Subject: [PATCH 0120/1255] Move `WidescreenStoryboard` out of `BeatmapInfo`
---
.../Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 4 ++--
osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs | 2 +-
osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs | 2 +-
osu.Game/Beatmaps/Beatmap.cs | 2 ++
osu.Game/Beatmaps/BeatmapConverter.cs | 1 +
osu.Game/Beatmaps/BeatmapImporter.cs | 1 -
osu.Game/Beatmaps/BeatmapInfo.cs | 2 --
osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 10 +++++-----
osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +-
osu.Game/Beatmaps/IBeatmap.cs | 2 ++
osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 6 ++++++
osu.Game/Screens/Edit/EditorBeatmap.cs | 6 ++++++
osu.Game/Screens/Edit/Setup/DesignSection.cs | 4 ++--
osu.Game/Storyboards/Drawables/DrawableStoryboard.cs | 2 +-
osu.Game/Storyboards/Storyboard.cs | 1 +
15 files changed, 31 insertions(+), 16 deletions(-)
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index 103bafb2d8..cd36b6b986 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -86,7 +86,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
Assert.IsFalse(beatmap.LetterboxInBreaks);
Assert.IsFalse(beatmap.SpecialStyle);
- Assert.IsFalse(beatmapInfo.WidescreenStoryboard);
+ Assert.IsFalse(beatmap.WidescreenStoryboard);
Assert.IsFalse(beatmapInfo.SamplesMatchPlaybackRate);
Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown);
Assert.AreEqual(0, beatmapInfo.CountdownOffset);
@@ -954,7 +954,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.That(decoded.StackLeniency, Is.EqualTo(0.7f));
Assert.That(decoded.SpecialStyle, Is.False);
Assert.That(decoded.LetterboxInBreaks, Is.False);
- Assert.That(decoded.BeatmapInfo.WidescreenStoryboard, Is.False);
+ Assert.That(decoded.WidescreenStoryboard, Is.False);
Assert.That(decoded.BeatmapInfo.EpilepsyWarning, Is.False);
Assert.That(decoded.BeatmapInfo.SamplesMatchPlaybackRate, Is.False);
Assert.That(decoded.BeatmapInfo.Countdown, Is.EqualTo(CountdownType.Normal));
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
index c1c996fd42..92715b6aa2 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
@@ -56,7 +56,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(false, beatmap.SpecialStyle);
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
Assert.AreEqual(false, beatmap.LetterboxInBreaks);
- Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard);
+ Assert.AreEqual(false, beatmap.WidescreenStoryboard);
Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown);
Assert.AreEqual(0, beatmapInfo.CountdownOffset);
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs
index 893b9f11f4..95aee43456 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs
@@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("load storyboard with only video", () =>
{
// LegacyStoryboardDecoder doesn't parse WidescreenStoryboard, so it is set manually
- loadStoryboard("storyboard_only_video.osu", s => s.BeatmapInfo.WidescreenStoryboard = false);
+ loadStoryboard("storyboard_only_video.osu", s => s.Beatmap.WidescreenStoryboard = false);
});
AddAssert("storyboard is correct width", () => Precision.AlmostEquals(storyboard?.Width ?? 0f, 480 * 16 / 9f));
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index 614bb4f42a..d909e87417 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -122,6 +122,8 @@ namespace osu.Game.Beatmaps
public bool LetterboxInBreaks { get; set; }
+ public bool WidescreenStoryboard { get; set; } = true;
+
IBeatmap IBeatmap.Clone() => Clone();
public Beatmap Clone() => (Beatmap)MemberwiseClone();
diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs
index a56ce58532..c097389c56 100644
--- a/osu.Game/Beatmaps/BeatmapConverter.cs
+++ b/osu.Game/Beatmaps/BeatmapConverter.cs
@@ -71,6 +71,7 @@ namespace osu.Game.Beatmaps
beatmap.StackLeniency = original.StackLeniency;
beatmap.SpecialStyle = original.SpecialStyle;
beatmap.LetterboxInBreaks = original.LetterboxInBreaks;
+ beatmap.WidescreenStoryboard = original.WidescreenStoryboard;
return beatmap;
}
diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs
index c54eece9f8..0bb2ddda7d 100644
--- a/osu.Game/Beatmaps/BeatmapImporter.cs
+++ b/osu.Game/Beatmaps/BeatmapImporter.cs
@@ -414,7 +414,6 @@ namespace osu.Game.Beatmaps
Hash = hash,
DifficultyName = decodedInfo.DifficultyName,
OnlineID = decodedInfo.OnlineID,
- WidescreenStoryboard = decodedInfo.WidescreenStoryboard,
EpilepsyWarning = decodedInfo.EpilepsyWarning,
SamplesMatchPlaybackRate = decodedInfo.SamplesMatchPlaybackRate,
DistanceSpacing = decodedInfo.DistanceSpacing,
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index fafca5e014..1bfa65a3fb 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -138,8 +138,6 @@ namespace osu.Game.Beatmaps
#region Properties we may not want persisted (but also maybe no harm?)
- public bool WidescreenStoryboard { get; set; } = true;
-
public bool EpilepsyWarning { get; set; }
public bool SamplesMatchPlaybackRate { get; set; } = true;
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index b8469f27dd..355114fd0b 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -81,7 +81,7 @@ namespace osu.Game.Beatmaps.Formats
this.beatmap = beatmap;
this.beatmap.BeatmapInfo.BeatmapVersion = FormatVersion;
- applyLegacyDefaults(this.beatmap.BeatmapInfo);
+ applyLegacyDefaults(this.beatmap);
base.ParseStreamInto(stream, beatmap);
@@ -183,10 +183,10 @@ namespace osu.Game.Beatmaps.Formats
/// This method's intention is to restore those legacy defaults.
/// See also: https://osu.ppy.sh/wiki/en/Client/File_formats/Osu_%28file_format%29
///
- private static void applyLegacyDefaults(BeatmapInfo beatmapInfo)
+ private static void applyLegacyDefaults(Beatmap beatmap)
{
- beatmapInfo.WidescreenStoryboard = false;
- beatmapInfo.SamplesMatchPlaybackRate = false;
+ beatmap.WidescreenStoryboard = false;
+ beatmap.BeatmapInfo.SamplesMatchPlaybackRate = false;
}
protected override void ParseLine(Beatmap beatmap, Section section, string line)
@@ -293,7 +293,7 @@ namespace osu.Game.Beatmaps.Formats
break;
case @"WidescreenStoryboard":
- beatmap.BeatmapInfo.WidescreenStoryboard = Parsing.ParseInt(pair.Value) == 1;
+ beatmap.WidescreenStoryboard = Parsing.ParseInt(pair.Value) == 1;
break;
case @"EpilepsyWarning":
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index 5a3fd0a2f3..478b78fa29 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -101,7 +101,7 @@ namespace osu.Game.Beatmaps.Formats
writer.WriteLine(FormattableString.Invariant($@"CountdownOffset: {beatmap.BeatmapInfo.CountdownOffset}"));
if (onlineRulesetID == 3)
writer.WriteLine(FormattableString.Invariant($"SpecialStyle: {(beatmap.SpecialStyle ? '1' : '0')}"));
- writer.WriteLine(FormattableString.Invariant($"WidescreenStoryboard: {(beatmap.BeatmapInfo.WidescreenStoryboard ? '1' : '0')}"));
+ writer.WriteLine(FormattableString.Invariant($"WidescreenStoryboard: {(beatmap.WidescreenStoryboard ? '1' : '0')}"));
if (beatmap.BeatmapInfo.SamplesMatchPlaybackRate)
writer.WriteLine(@"SamplesMatchPlaybackRate: 1");
}
diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs
index e5562b608e..3091e02054 100644
--- a/osu.Game/Beatmaps/IBeatmap.cs
+++ b/osu.Game/Beatmaps/IBeatmap.cs
@@ -76,6 +76,8 @@ namespace osu.Game.Beatmaps
bool LetterboxInBreaks { get; internal set; }
+ bool WidescreenStoryboard { get; internal set; }
+
///
/// Creates a shallow-clone of this beatmap and returns it.
///
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
index 04dcb2a552..bd051383de 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
@@ -361,6 +361,12 @@ namespace osu.Game.Rulesets.Difficulty
set => baseBeatmap.LetterboxInBreaks = value;
}
+ public bool WidescreenStoryboard
+ {
+ get => baseBeatmap.WidescreenStoryboard;
+ set => baseBeatmap.WidescreenStoryboard = value;
+ }
+
#endregion
}
}
diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs
index 7470505712..a217e132d0 100644
--- a/osu.Game/Screens/Edit/EditorBeatmap.cs
+++ b/osu.Game/Screens/Edit/EditorBeatmap.cs
@@ -208,6 +208,12 @@ namespace osu.Game.Screens.Edit
set => PlayableBeatmap.LetterboxInBreaks = value;
}
+ public bool WidescreenStoryboard
+ {
+ get => PlayableBeatmap.WidescreenStoryboard;
+ set => PlayableBeatmap.WidescreenStoryboard = value;
+ }
+
public IBeatmap Clone() => (EditorBeatmap)MemberwiseClone();
private IList mutableHitObjects => (IList)PlayableBeatmap.HitObjects;
diff --git a/osu.Game/Screens/Edit/Setup/DesignSection.cs b/osu.Game/Screens/Edit/Setup/DesignSection.cs
index c1fe7f405d..8c420b979f 100644
--- a/osu.Game/Screens/Edit/Setup/DesignSection.cs
+++ b/osu.Game/Screens/Edit/Setup/DesignSection.cs
@@ -68,7 +68,7 @@ namespace osu.Game.Screens.Edit.Setup
{
Label = EditorSetupStrings.WidescreenSupport,
Description = EditorSetupStrings.WidescreenSupportDescription,
- Current = { Value = Beatmap.BeatmapInfo.WidescreenStoryboard }
+ Current = { Value = Beatmap.WidescreenStoryboard }
},
epilepsyWarning = new LabelledSwitchButton
{
@@ -121,7 +121,7 @@ namespace osu.Game.Screens.Edit.Setup
Beatmap.BeatmapInfo.Countdown = EnableCountdown.Current.Value ? CountdownSpeed.Current.Value : CountdownType.None;
Beatmap.BeatmapInfo.CountdownOffset = int.TryParse(CountdownOffset.Current.Value, NumberStyles.None, CultureInfo.InvariantCulture, out int offset) ? offset : 0;
- Beatmap.BeatmapInfo.WidescreenStoryboard = widescreenSupport.Current.Value;
+ Beatmap.WidescreenStoryboard = widescreenSupport.Current.Value;
Beatmap.BeatmapInfo.EpilepsyWarning = epilepsyWarning.Current.Value;
Beatmap.LetterboxInBreaks = letterboxDuringBreaks.Current.Value;
Beatmap.BeatmapInfo.SamplesMatchPlaybackRate = samplesMatchPlaybackRate.Current.Value;
diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
index fc5ef12fb8..858c257e85 100644
--- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
+++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
@@ -78,7 +78,7 @@ namespace osu.Game.Storyboards.Drawables
bool onlyHasVideoElements = Storyboard.Layers.SelectMany(l => l.Elements).All(e => e is StoryboardVideo);
- Width = Height * (storyboard.BeatmapInfo.WidescreenStoryboard || onlyHasVideoElements ? 16 / 9f : 4 / 3f);
+ Width = Height * (storyboard.Beatmap.WidescreenStoryboard || onlyHasVideoElements ? 16 / 9f : 4 / 3f);
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs
index 8c43b99702..aca5fc34f4 100644
--- a/osu.Game/Storyboards/Storyboard.cs
+++ b/osu.Game/Storyboards/Storyboard.cs
@@ -16,6 +16,7 @@ namespace osu.Game.Storyboards
public IEnumerable Layers => layers.Values;
public BeatmapInfo BeatmapInfo = new BeatmapInfo();
+ public IBeatmap Beatmap { get; set; } = new Beatmap();
///
/// Whether the storyboard should prefer textures from the current skin before using local storyboard textures.
From f64a0624a5b1808a7c0ca91db99ecc7d679c4ddf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Wed, 12 Jun 2024 13:28:41 +0200
Subject: [PATCH 0121/1255] Move `EpilepsyWarning` out of `BeatmapInfo`
---
osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 2 +-
osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 2 +-
osu.Game/Beatmaps/Beatmap.cs | 2 ++
osu.Game/Beatmaps/BeatmapConverter.cs | 1 +
osu.Game/Beatmaps/BeatmapImporter.cs | 1 -
osu.Game/Beatmaps/BeatmapInfo.cs | 2 --
osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +-
osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +-
osu.Game/Beatmaps/IBeatmap.cs | 2 ++
osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 6 ++++++
osu.Game/Screens/Edit/EditorBeatmap.cs | 6 ++++++
osu.Game/Screens/Edit/Setup/DesignSection.cs | 4 ++--
osu.Game/Screens/Play/PlayerLoader.cs | 2 +-
13 files changed, 24 insertions(+), 10 deletions(-)
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index cd36b6b986..d3b027d253 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -955,7 +955,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.That(decoded.SpecialStyle, Is.False);
Assert.That(decoded.LetterboxInBreaks, Is.False);
Assert.That(decoded.WidescreenStoryboard, Is.False);
- Assert.That(decoded.BeatmapInfo.EpilepsyWarning, Is.False);
+ Assert.That(decoded.EpilepsyWarning, Is.False);
Assert.That(decoded.BeatmapInfo.SamplesMatchPlaybackRate, Is.False);
Assert.That(decoded.BeatmapInfo.Countdown, Is.EqualTo(CountdownType.Normal));
Assert.That(decoded.BeatmapInfo.CountdownOffset, Is.EqualTo(0));
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
index c17405c2ec..64211cddf3 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
@@ -138,7 +138,7 @@ namespace osu.Game.Tests.Visual.Gameplay
workingBeatmap.Beatmap.AudioLeadIn = 60000;
// Set up data for testing disclaimer display.
- workingBeatmap.BeatmapInfo.EpilepsyWarning = epilepsyWarning ?? false;
+ workingBeatmap.Beatmap.EpilepsyWarning = epilepsyWarning ?? false;
workingBeatmap.BeatmapInfo.Status = onlineStatus ?? BeatmapOnlineStatus.Ranked;
Beatmap.Value = workingBeatmap;
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index d909e87417..7516c8958c 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -124,6 +124,8 @@ namespace osu.Game.Beatmaps
public bool WidescreenStoryboard { get; set; } = true;
+ public bool EpilepsyWarning { get; set; }
+
IBeatmap IBeatmap.Clone() => Clone();
public Beatmap Clone() => (Beatmap)MemberwiseClone();
diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs
index c097389c56..f9be44599f 100644
--- a/osu.Game/Beatmaps/BeatmapConverter.cs
+++ b/osu.Game/Beatmaps/BeatmapConverter.cs
@@ -72,6 +72,7 @@ namespace osu.Game.Beatmaps
beatmap.SpecialStyle = original.SpecialStyle;
beatmap.LetterboxInBreaks = original.LetterboxInBreaks;
beatmap.WidescreenStoryboard = original.WidescreenStoryboard;
+ beatmap.EpilepsyWarning = original.EpilepsyWarning;
return beatmap;
}
diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs
index 0bb2ddda7d..232c8b7e24 100644
--- a/osu.Game/Beatmaps/BeatmapImporter.cs
+++ b/osu.Game/Beatmaps/BeatmapImporter.cs
@@ -414,7 +414,6 @@ namespace osu.Game.Beatmaps
Hash = hash,
DifficultyName = decodedInfo.DifficultyName,
OnlineID = decodedInfo.OnlineID,
- EpilepsyWarning = decodedInfo.EpilepsyWarning,
SamplesMatchPlaybackRate = decodedInfo.SamplesMatchPlaybackRate,
DistanceSpacing = decodedInfo.DistanceSpacing,
BeatDivisor = decodedInfo.BeatDivisor,
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index 1bfa65a3fb..41a0260250 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -138,8 +138,6 @@ namespace osu.Game.Beatmaps
#region Properties we may not want persisted (but also maybe no harm?)
- public bool EpilepsyWarning { get; set; }
-
public bool SamplesMatchPlaybackRate { get; set; } = true;
///
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 355114fd0b..1152e5f30d 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -297,7 +297,7 @@ namespace osu.Game.Beatmaps.Formats
break;
case @"EpilepsyWarning":
- beatmap.BeatmapInfo.EpilepsyWarning = Parsing.ParseInt(pair.Value) == 1;
+ beatmap.EpilepsyWarning = Parsing.ParseInt(pair.Value) == 1;
break;
case @"SamplesMatchPlaybackRate":
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index 478b78fa29..d7078a71d9 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -95,7 +95,7 @@ namespace osu.Game.Beatmaps.Formats
// writer.WriteLine(@"OverlayPosition: " + b.OverlayPosition);
// if (!string.IsNullOrEmpty(b.SkinPreference))
// writer.WriteLine(@"SkinPreference:" + b.SkinPreference);
- if (beatmap.BeatmapInfo.EpilepsyWarning)
+ if (beatmap.EpilepsyWarning)
writer.WriteLine(@"EpilepsyWarning: 1");
if (beatmap.BeatmapInfo.CountdownOffset > 0)
writer.WriteLine(FormattableString.Invariant($@"CountdownOffset: {beatmap.BeatmapInfo.CountdownOffset}"));
diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs
index 3091e02054..3d563004d1 100644
--- a/osu.Game/Beatmaps/IBeatmap.cs
+++ b/osu.Game/Beatmaps/IBeatmap.cs
@@ -78,6 +78,8 @@ namespace osu.Game.Beatmaps
bool WidescreenStoryboard { get; internal set; }
+ bool EpilepsyWarning { get; internal set; }
+
///
/// Creates a shallow-clone of this beatmap and returns it.
///
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
index bd051383de..7f9d2ae6ab 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
@@ -367,6 +367,12 @@ namespace osu.Game.Rulesets.Difficulty
set => baseBeatmap.WidescreenStoryboard = value;
}
+ public bool EpilepsyWarning
+ {
+ get => baseBeatmap.EpilepsyWarning;
+ set => baseBeatmap.EpilepsyWarning = value;
+ }
+
#endregion
}
}
diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs
index a217e132d0..063803ab42 100644
--- a/osu.Game/Screens/Edit/EditorBeatmap.cs
+++ b/osu.Game/Screens/Edit/EditorBeatmap.cs
@@ -214,6 +214,12 @@ namespace osu.Game.Screens.Edit
set => PlayableBeatmap.WidescreenStoryboard = value;
}
+ public bool EpilepsyWarning
+ {
+ get => PlayableBeatmap.EpilepsyWarning;
+ set => PlayableBeatmap.EpilepsyWarning = value;
+ }
+
public IBeatmap Clone() => (EditorBeatmap)MemberwiseClone();
private IList mutableHitObjects => (IList)PlayableBeatmap.HitObjects;
diff --git a/osu.Game/Screens/Edit/Setup/DesignSection.cs b/osu.Game/Screens/Edit/Setup/DesignSection.cs
index 8c420b979f..5d729cf4f8 100644
--- a/osu.Game/Screens/Edit/Setup/DesignSection.cs
+++ b/osu.Game/Screens/Edit/Setup/DesignSection.cs
@@ -74,7 +74,7 @@ namespace osu.Game.Screens.Edit.Setup
{
Label = EditorSetupStrings.EpilepsyWarning,
Description = EditorSetupStrings.EpilepsyWarningDescription,
- Current = { Value = Beatmap.BeatmapInfo.EpilepsyWarning }
+ Current = { Value = Beatmap.EpilepsyWarning }
},
letterboxDuringBreaks = new LabelledSwitchButton
{
@@ -122,7 +122,7 @@ namespace osu.Game.Screens.Edit.Setup
Beatmap.BeatmapInfo.CountdownOffset = int.TryParse(CountdownOffset.Current.Value, NumberStyles.None, CultureInfo.InvariantCulture, out int offset) ? offset : 0;
Beatmap.WidescreenStoryboard = widescreenSupport.Current.Value;
- Beatmap.BeatmapInfo.EpilepsyWarning = epilepsyWarning.Current.Value;
+ Beatmap.EpilepsyWarning = epilepsyWarning.Current.Value;
Beatmap.LetterboxInBreaks = letterboxDuringBreaks.Current.Value;
Beatmap.BeatmapInfo.SamplesMatchPlaybackRate = samplesMatchPlaybackRate.Current.Value;
diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs
index 51a0c94ff0..fc7e7fb58f 100644
--- a/osu.Game/Screens/Play/PlayerLoader.cs
+++ b/osu.Game/Screens/Play/PlayerLoader.cs
@@ -228,7 +228,7 @@ namespace osu.Game.Screens.Play
sampleRestart = new SkinnableSound(new SampleInfo(@"Gameplay/restart", @"pause-retry-click"))
};
- if (Beatmap.Value.BeatmapInfo.EpilepsyWarning)
+ if (Beatmap.Value.Beatmap.EpilepsyWarning)
{
disclaimers.Add(epilepsyWarning = new PlayerLoaderDisclaimer(PlayerLoaderStrings.EpilepsyWarningTitle, PlayerLoaderStrings.EpilepsyWarningContent));
}
From c216283bf4ee5964e793869f2a63d941e58e6d74 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Wed, 12 Jun 2024 13:32:23 +0200
Subject: [PATCH 0122/1255] Move `SamplesMatchPlaybackRate` out of
`BeatmapInfo`
---
osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 4 ++--
osu.Game/Beatmaps/Beatmap.cs | 2 ++
osu.Game/Beatmaps/BeatmapConverter.cs | 1 +
osu.Game/Beatmaps/BeatmapImporter.cs | 1 -
osu.Game/Beatmaps/BeatmapInfo.cs | 2 --
osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 4 ++--
osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +-
osu.Game/Beatmaps/IBeatmap.cs | 2 ++
osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 6 ++++++
osu.Game/Screens/Edit/EditorBeatmap.cs | 6 ++++++
osu.Game/Screens/Edit/Setup/DesignSection.cs | 4 ++--
11 files changed, 24 insertions(+), 10 deletions(-)
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index d3b027d253..51c1e51d3b 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -87,7 +87,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.IsFalse(beatmap.LetterboxInBreaks);
Assert.IsFalse(beatmap.SpecialStyle);
Assert.IsFalse(beatmap.WidescreenStoryboard);
- Assert.IsFalse(beatmapInfo.SamplesMatchPlaybackRate);
+ Assert.IsFalse(beatmap.SamplesMatchPlaybackRate);
Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown);
Assert.AreEqual(0, beatmapInfo.CountdownOffset);
}
@@ -956,7 +956,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.That(decoded.LetterboxInBreaks, Is.False);
Assert.That(decoded.WidescreenStoryboard, Is.False);
Assert.That(decoded.EpilepsyWarning, Is.False);
- Assert.That(decoded.BeatmapInfo.SamplesMatchPlaybackRate, Is.False);
+ Assert.That(decoded.SamplesMatchPlaybackRate, Is.False);
Assert.That(decoded.BeatmapInfo.Countdown, Is.EqualTo(CountdownType.Normal));
Assert.That(decoded.BeatmapInfo.CountdownOffset, Is.EqualTo(0));
Assert.That(decoded.BeatmapInfo.Metadata.PreviewTime, Is.EqualTo(-1));
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index 7516c8958c..76864a1d70 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -126,6 +126,8 @@ namespace osu.Game.Beatmaps
public bool EpilepsyWarning { get; set; }
+ public bool SamplesMatchPlaybackRate { get; set; } = true;
+
IBeatmap IBeatmap.Clone() => Clone();
public Beatmap Clone() => (Beatmap)MemberwiseClone();
diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs
index f9be44599f..b86a445aa8 100644
--- a/osu.Game/Beatmaps/BeatmapConverter.cs
+++ b/osu.Game/Beatmaps/BeatmapConverter.cs
@@ -73,6 +73,7 @@ namespace osu.Game.Beatmaps
beatmap.LetterboxInBreaks = original.LetterboxInBreaks;
beatmap.WidescreenStoryboard = original.WidescreenStoryboard;
beatmap.EpilepsyWarning = original.EpilepsyWarning;
+ beatmap.SamplesMatchPlaybackRate = original.SamplesMatchPlaybackRate;
return beatmap;
}
diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs
index 232c8b7e24..650b0bf510 100644
--- a/osu.Game/Beatmaps/BeatmapImporter.cs
+++ b/osu.Game/Beatmaps/BeatmapImporter.cs
@@ -414,7 +414,6 @@ namespace osu.Game.Beatmaps
Hash = hash,
DifficultyName = decodedInfo.DifficultyName,
OnlineID = decodedInfo.OnlineID,
- SamplesMatchPlaybackRate = decodedInfo.SamplesMatchPlaybackRate,
DistanceSpacing = decodedInfo.DistanceSpacing,
BeatDivisor = decodedInfo.BeatDivisor,
GridSize = decodedInfo.GridSize,
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index 41a0260250..93abfa8d9b 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -138,8 +138,6 @@ namespace osu.Game.Beatmaps
#region Properties we may not want persisted (but also maybe no harm?)
- public bool SamplesMatchPlaybackRate { get; set; } = true;
-
///
/// The time at which this beatmap was last played by the local user.
///
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 1152e5f30d..0c8770782d 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -186,7 +186,7 @@ namespace osu.Game.Beatmaps.Formats
private static void applyLegacyDefaults(Beatmap beatmap)
{
beatmap.WidescreenStoryboard = false;
- beatmap.BeatmapInfo.SamplesMatchPlaybackRate = false;
+ beatmap.SamplesMatchPlaybackRate = false;
}
protected override void ParseLine(Beatmap beatmap, Section section, string line)
@@ -301,7 +301,7 @@ namespace osu.Game.Beatmaps.Formats
break;
case @"SamplesMatchPlaybackRate":
- beatmap.BeatmapInfo.SamplesMatchPlaybackRate = Parsing.ParseInt(pair.Value) == 1;
+ beatmap.SamplesMatchPlaybackRate = Parsing.ParseInt(pair.Value) == 1;
break;
case @"Countdown":
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index d7078a71d9..860ca68f6b 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -102,7 +102,7 @@ namespace osu.Game.Beatmaps.Formats
if (onlineRulesetID == 3)
writer.WriteLine(FormattableString.Invariant($"SpecialStyle: {(beatmap.SpecialStyle ? '1' : '0')}"));
writer.WriteLine(FormattableString.Invariant($"WidescreenStoryboard: {(beatmap.WidescreenStoryboard ? '1' : '0')}"));
- if (beatmap.BeatmapInfo.SamplesMatchPlaybackRate)
+ if (beatmap.SamplesMatchPlaybackRate)
writer.WriteLine(@"SamplesMatchPlaybackRate: 1");
}
diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs
index 3d563004d1..9900609f18 100644
--- a/osu.Game/Beatmaps/IBeatmap.cs
+++ b/osu.Game/Beatmaps/IBeatmap.cs
@@ -80,6 +80,8 @@ namespace osu.Game.Beatmaps
bool EpilepsyWarning { get; internal set; }
+ bool SamplesMatchPlaybackRate { get; internal set; }
+
///
/// Creates a shallow-clone of this beatmap and returns it.
///
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
index 7f9d2ae6ab..960eab6f2c 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
@@ -373,6 +373,12 @@ namespace osu.Game.Rulesets.Difficulty
set => baseBeatmap.EpilepsyWarning = value;
}
+ public bool SamplesMatchPlaybackRate
+ {
+ get => baseBeatmap.SamplesMatchPlaybackRate;
+ set => baseBeatmap.SamplesMatchPlaybackRate = value;
+ }
+
#endregion
}
}
diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs
index 063803ab42..4303764fa7 100644
--- a/osu.Game/Screens/Edit/EditorBeatmap.cs
+++ b/osu.Game/Screens/Edit/EditorBeatmap.cs
@@ -220,6 +220,12 @@ namespace osu.Game.Screens.Edit
set => PlayableBeatmap.EpilepsyWarning = value;
}
+ public bool SamplesMatchPlaybackRate
+ {
+ get => PlayableBeatmap.SamplesMatchPlaybackRate;
+ set => PlayableBeatmap.SamplesMatchPlaybackRate = value;
+ }
+
public IBeatmap Clone() => (EditorBeatmap)MemberwiseClone();
private IList mutableHitObjects => (IList)PlayableBeatmap.HitObjects;
diff --git a/osu.Game/Screens/Edit/Setup/DesignSection.cs b/osu.Game/Screens/Edit/Setup/DesignSection.cs
index 5d729cf4f8..4c4755064f 100644
--- a/osu.Game/Screens/Edit/Setup/DesignSection.cs
+++ b/osu.Game/Screens/Edit/Setup/DesignSection.cs
@@ -86,7 +86,7 @@ namespace osu.Game.Screens.Edit.Setup
{
Label = EditorSetupStrings.SamplesMatchPlaybackRate,
Description = EditorSetupStrings.SamplesMatchPlaybackRateDescription,
- Current = { Value = Beatmap.BeatmapInfo.SamplesMatchPlaybackRate }
+ Current = { Value = Beatmap.SamplesMatchPlaybackRate }
}
};
}
@@ -124,7 +124,7 @@ namespace osu.Game.Screens.Edit.Setup
Beatmap.WidescreenStoryboard = widescreenSupport.Current.Value;
Beatmap.EpilepsyWarning = epilepsyWarning.Current.Value;
Beatmap.LetterboxInBreaks = letterboxDuringBreaks.Current.Value;
- Beatmap.BeatmapInfo.SamplesMatchPlaybackRate = samplesMatchPlaybackRate.Current.Value;
+ Beatmap.SamplesMatchPlaybackRate = samplesMatchPlaybackRate.Current.Value;
Beatmap.SaveState();
}
From 3634307d7ceadc23604014fba8aaa077682f2988 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Wed, 12 Jun 2024 13:36:27 +0200
Subject: [PATCH 0123/1255] Move `DistanceSpacing` out of `BeatmapInfo`
---
.../Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 14 +++++++-------
.../Beatmaps/Formats/OsuJsonDecoderTest.cs | 2 +-
.../Visual/Editing/TestSceneHitObjectComposer.cs | 4 ++--
osu.Game/Beatmaps/Beatmap.cs | 2 ++
osu.Game/Beatmaps/BeatmapConverter.cs | 1 +
osu.Game/Beatmaps/BeatmapImporter.cs | 1 -
osu.Game/Beatmaps/BeatmapInfo.cs | 14 --------------
osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +-
osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +-
osu.Game/Beatmaps/IBeatmap.cs | 13 +++++++++++++
.../Rulesets/Difficulty/DifficultyCalculator.cs | 6 ++++++
.../Rulesets/Edit/ComposerDistanceSnapProvider.cs | 4 ++--
osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs | 2 +-
osu.Game/Screens/Edit/EditorBeatmap.cs | 6 ++++++
14 files changed, 43 insertions(+), 30 deletions(-)
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index 51c1e51d3b..71dcd38bcd 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -101,7 +101,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new LineBufferedReader(resStream))
{
- var beatmapInfo = decoder.Decode(stream).BeatmapInfo;
+ var beatmap = decoder.Decode(stream);
int[] expectedBookmarks =
{
@@ -109,13 +109,13 @@ namespace osu.Game.Tests.Beatmaps.Formats
95901, 106450, 116999, 119637, 130186, 140735, 151285,
161834, 164471, 175020, 185570, 196119, 206669, 209306
};
- Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length);
+ Assert.AreEqual(expectedBookmarks.Length, beatmap.BeatmapInfo.Bookmarks.Length);
for (int i = 0; i < expectedBookmarks.Length; i++)
- Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]);
- Assert.AreEqual(1.8, beatmapInfo.DistanceSpacing);
- Assert.AreEqual(4, beatmapInfo.BeatDivisor);
- Assert.AreEqual(4, beatmapInfo.GridSize);
- Assert.AreEqual(2, beatmapInfo.TimelineZoom);
+ Assert.AreEqual(expectedBookmarks[i], beatmap.BeatmapInfo.Bookmarks[i]);
+ Assert.AreEqual(1.8, beatmap.DistanceSpacing);
+ Assert.AreEqual(4, beatmap.BeatmapInfo.BeatDivisor);
+ Assert.AreEqual(4, beatmap.BeatmapInfo.GridSize);
+ Assert.AreEqual(2, beatmap.BeatmapInfo.TimelineZoom);
}
}
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
index 92715b6aa2..4832ee26b7 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
@@ -76,7 +76,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length);
for (int i = 0; i < expectedBookmarks.Length; i++)
Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]);
- Assert.AreEqual(1.8, beatmapInfo.DistanceSpacing);
+ Assert.AreEqual(1.8, beatmap.DistanceSpacing);
Assert.AreEqual(4, beatmapInfo.BeatDivisor);
Assert.AreEqual(4, beatmapInfo.GridSize);
Assert.AreEqual(2, beatmapInfo.TimelineZoom);
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs
index f392841ac7..d77c86729a 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs
@@ -165,7 +165,7 @@ namespace osu.Game.Tests.Visual.Editing
{
double originalSpacing = 0;
- AddStep("retrieve original spacing", () => originalSpacing = editorBeatmap.BeatmapInfo.DistanceSpacing);
+ AddStep("retrieve original spacing", () => originalSpacing = editorBeatmap.DistanceSpacing);
AddStep("hold ctrl", () => InputManager.PressKey(Key.LControl));
AddStep("hold alt", () => InputManager.PressKey(Key.LAlt));
@@ -175,7 +175,7 @@ namespace osu.Game.Tests.Visual.Editing
AddStep("release alt", () => InputManager.ReleaseKey(Key.LAlt));
AddStep("release ctrl", () => InputManager.ReleaseKey(Key.LControl));
- AddAssert("distance spacing increased by 0.5", () => editorBeatmap.BeatmapInfo.DistanceSpacing == originalSpacing + 0.5);
+ AddAssert("distance spacing increased by 0.5", () => editorBeatmap.DistanceSpacing == originalSpacing + 0.5);
}
public partial class EditorBeatmapContainer : PopoverContainer
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index 76864a1d70..2f7c00af4a 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -128,6 +128,8 @@ namespace osu.Game.Beatmaps
public bool SamplesMatchPlaybackRate { get; set; } = true;
+ public double DistanceSpacing { get; set; } = 1.0;
+
IBeatmap IBeatmap.Clone() => Clone();
public Beatmap Clone() => (Beatmap)MemberwiseClone();
diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs
index b86a445aa8..eda7f8025f 100644
--- a/osu.Game/Beatmaps/BeatmapConverter.cs
+++ b/osu.Game/Beatmaps/BeatmapConverter.cs
@@ -74,6 +74,7 @@ namespace osu.Game.Beatmaps
beatmap.WidescreenStoryboard = original.WidescreenStoryboard;
beatmap.EpilepsyWarning = original.EpilepsyWarning;
beatmap.SamplesMatchPlaybackRate = original.SamplesMatchPlaybackRate;
+ beatmap.DistanceSpacing = original.DistanceSpacing;
return beatmap;
}
diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs
index 650b0bf510..a8964a365a 100644
--- a/osu.Game/Beatmaps/BeatmapImporter.cs
+++ b/osu.Game/Beatmaps/BeatmapImporter.cs
@@ -414,7 +414,6 @@ namespace osu.Game.Beatmaps
Hash = hash,
DifficultyName = decodedInfo.DifficultyName,
OnlineID = decodedInfo.OnlineID,
- DistanceSpacing = decodedInfo.DistanceSpacing,
BeatDivisor = decodedInfo.BeatDivisor,
GridSize = decodedInfo.GridSize,
TimelineZoom = decodedInfo.TimelineZoom,
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index 93abfa8d9b..8328e3df95 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -6,14 +6,12 @@ using System.Diagnostics;
using System.Linq;
using JetBrains.Annotations;
using Newtonsoft.Json;
-using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Collections;
using osu.Game.Database;
using osu.Game.Models;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.BeatmapSet.Scores;
using osu.Game.Rulesets;
-using osu.Game.Rulesets.Edit;
using osu.Game.Scoring;
using Realms;
@@ -143,18 +141,6 @@ namespace osu.Game.Beatmaps
///
public DateTimeOffset? LastPlayed { get; set; }
- ///
- /// The ratio of distance travelled per time unit.
- /// Generally used to decouple the spacing between hit objects from the enforced "velocity" of the beatmap (see ).
- ///
- ///
- /// The most common method of understanding is that at a default value of 1.0, the time-to-distance ratio will match the slider velocity of the beatmap
- /// at the current point in time. Increasing this value will make hit objects more spaced apart when compared to the cursor movement required to track a slider.
- ///
- /// This is only a hint property, used by the editor in implementations. It does not directly affect the beatmap or gameplay.
- ///
- public double DistanceSpacing { get; set; } = 1.0;
-
public int BeatDivisor { get; set; } = 4;
public int GridSize { get; set; }
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 0c8770782d..92a26464ee 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -329,7 +329,7 @@ namespace osu.Game.Beatmaps.Formats
break;
case @"DistanceSpacing":
- beatmap.BeatmapInfo.DistanceSpacing = Math.Max(0, Parsing.ParseDouble(pair.Value));
+ beatmap.DistanceSpacing = Math.Max(0, Parsing.ParseDouble(pair.Value));
break;
case @"BeatDivisor":
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index 860ca68f6b..a07e8d2226 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -112,7 +112,7 @@ namespace osu.Game.Beatmaps.Formats
if (beatmap.BeatmapInfo.Bookmarks.Length > 0)
writer.WriteLine(FormattableString.Invariant($"Bookmarks: {string.Join(',', beatmap.BeatmapInfo.Bookmarks)}"));
- writer.WriteLine(FormattableString.Invariant($"DistanceSpacing: {beatmap.BeatmapInfo.DistanceSpacing}"));
+ writer.WriteLine(FormattableString.Invariant($"DistanceSpacing: {beatmap.DistanceSpacing}"));
writer.WriteLine(FormattableString.Invariant($"BeatDivisor: {beatmap.BeatmapInfo.BeatDivisor}"));
writer.WriteLine(FormattableString.Invariant($"GridSize: {beatmap.BeatmapInfo.GridSize}"));
writer.WriteLine(FormattableString.Invariant($"TimelineZoom: {beatmap.BeatmapInfo.TimelineZoom}"));
diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs
index 9900609f18..b2c8b7604a 100644
--- a/osu.Game/Beatmaps/IBeatmap.cs
+++ b/osu.Game/Beatmaps/IBeatmap.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Timing;
+using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
@@ -82,6 +83,18 @@ namespace osu.Game.Beatmaps
bool SamplesMatchPlaybackRate { get; internal set; }
+ ///
+ /// The ratio of distance travelled per time unit.
+ /// Generally used to decouple the spacing between hit objects from the enforced "velocity" of the beatmap (see ).
+ ///
+ ///
+ /// The most common method of understanding is that at a default value of 1.0, the time-to-distance ratio will match the slider velocity of the beatmap
+ /// at the current point in time. Increasing this value will make hit objects more spaced apart when compared to the cursor movement required to track a slider.
+ ///
+ /// This is only a hint property, used by the editor in implementations. It does not directly affect the beatmap or gameplay.
+ ///
+ double DistanceSpacing { get; internal set; }
+
///
/// Creates a shallow-clone of this beatmap and returns it.
///
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
index 960eab6f2c..7473882c15 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
@@ -379,6 +379,12 @@ namespace osu.Game.Rulesets.Difficulty
set => baseBeatmap.SamplesMatchPlaybackRate = value;
}
+ public double DistanceSpacing
+ {
+ get => baseBeatmap.DistanceSpacing;
+ set => baseBeatmap.DistanceSpacing = value;
+ }
+
#endregion
}
}
diff --git a/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs b/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs
index b9850a94a3..665e6ba074 100644
--- a/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs
+++ b/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs
@@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Edit
}
});
- DistanceSpacingMultiplier.Value = editorBeatmap.BeatmapInfo.DistanceSpacing;
+ DistanceSpacingMultiplier.Value = editorBeatmap.DistanceSpacing;
DistanceSpacingMultiplier.BindValueChanged(multiplier =>
{
distanceSpacingSlider.ContractedLabelText = $"D. S. ({multiplier.NewValue:0.##x})";
@@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Edit
if (multiplier.NewValue != multiplier.OldValue)
onScreenDisplay?.Display(new DistanceSpacingToast(multiplier.NewValue.ToLocalisableString(@"0.##x"), multiplier));
- editorBeatmap.BeatmapInfo.DistanceSpacing = multiplier.NewValue;
+ editorBeatmap.DistanceSpacing = multiplier.NewValue;
}, true);
DistanceSpacingMultiplier.BindDisabledChanged(disabled => distanceSpacingSlider.Alpha = disabled ? 0 : 1, true);
diff --git a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs b/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs
index 380038eadf..c312642fbd 100644
--- a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs
+++ b/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Edit
/// A multiplier which changes the ratio of distance travelled per time unit.
/// Importantly, this is provided for manual usage, and not multiplied into any of the methods exposed by this interface.
///
- ///
+ ///
Bindable DistanceSpacingMultiplier { get; }
///
diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs
index 4303764fa7..aa63dfab8d 100644
--- a/osu.Game/Screens/Edit/EditorBeatmap.cs
+++ b/osu.Game/Screens/Edit/EditorBeatmap.cs
@@ -226,6 +226,12 @@ namespace osu.Game.Screens.Edit
set => PlayableBeatmap.SamplesMatchPlaybackRate = value;
}
+ public double DistanceSpacing
+ {
+ get => PlayableBeatmap.DistanceSpacing;
+ set => PlayableBeatmap.DistanceSpacing = value;
+ }
+
public IBeatmap Clone() => (EditorBeatmap)MemberwiseClone();
private IList mutableHitObjects => (IList)PlayableBeatmap.HitObjects;
From 6685c5ab741441140e7e9ba4d1e67b29f3ce828a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Wed, 12 Jun 2024 13:44:36 +0200
Subject: [PATCH 0124/1255] Move `GridSize` out of `BeatmapInfo`
---
.../Editor/TestSceneOsuEditorGrids.cs | 2 +-
osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs | 4 ++--
osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 2 +-
osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs | 2 +-
osu.Game/Beatmaps/Beatmap.cs | 2 ++
osu.Game/Beatmaps/BeatmapConverter.cs | 1 +
osu.Game/Beatmaps/BeatmapImporter.cs | 1 -
osu.Game/Beatmaps/BeatmapInfo.cs | 2 --
osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +-
osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +-
osu.Game/Beatmaps/IBeatmap.cs | 2 ++
osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 6 ++++++
osu.Game/Screens/Edit/EditorBeatmap.cs | 6 ++++++
13 files changed, 24 insertions(+), 10 deletions(-)
diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs
index 48aa74c5bf..cb9347b177 100644
--- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs
@@ -185,6 +185,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
private void gridSizeIs(int size)
=> AddAssert($"grid size is {size}", () => this.ChildrenOfType().Single().Spacing.Value == new Vector2(size)
- && EditorBeatmap.BeatmapInfo.GridSize == size);
+ && EditorBeatmap.GridSize == size);
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs b/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
index 21cce553b1..3740c54752 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
@@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.Edit
},
};
- Spacing.Value = editorBeatmap.BeatmapInfo.GridSize;
+ Spacing.Value = editorBeatmap.GridSize;
}
protected override void LoadComplete()
@@ -137,7 +137,7 @@ namespace osu.Game.Rulesets.Osu.Edit
spacingSlider.ContractedLabelText = $"S: {spacing.NewValue:N0}";
spacingSlider.ExpandedLabelText = $"Spacing: {spacing.NewValue:N0}";
SpacingVector.Value = new Vector2(spacing.NewValue);
- editorBeatmap.BeatmapInfo.GridSize = (int)spacing.NewValue;
+ editorBeatmap.GridSize = (int)spacing.NewValue;
}, true);
GridLinesRotation.BindValueChanged(rotation =>
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index 71dcd38bcd..a15485cdf1 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -114,7 +114,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(expectedBookmarks[i], beatmap.BeatmapInfo.Bookmarks[i]);
Assert.AreEqual(1.8, beatmap.DistanceSpacing);
Assert.AreEqual(4, beatmap.BeatmapInfo.BeatDivisor);
- Assert.AreEqual(4, beatmap.BeatmapInfo.GridSize);
+ Assert.AreEqual(4, beatmap.GridSize);
Assert.AreEqual(2, beatmap.BeatmapInfo.TimelineZoom);
}
}
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
index 4832ee26b7..95cba082a7 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
@@ -78,7 +78,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]);
Assert.AreEqual(1.8, beatmap.DistanceSpacing);
Assert.AreEqual(4, beatmapInfo.BeatDivisor);
- Assert.AreEqual(4, beatmapInfo.GridSize);
+ Assert.AreEqual(4, beatmap.GridSize);
Assert.AreEqual(2, beatmapInfo.TimelineZoom);
}
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index 2f7c00af4a..aacbe359b1 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -130,6 +130,8 @@ namespace osu.Game.Beatmaps
public double DistanceSpacing { get; set; } = 1.0;
+ public int GridSize { get; set; }
+
IBeatmap IBeatmap.Clone() => Clone();
public Beatmap Clone() => (Beatmap)MemberwiseClone();
diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs
index eda7f8025f..a70d449fc5 100644
--- a/osu.Game/Beatmaps/BeatmapConverter.cs
+++ b/osu.Game/Beatmaps/BeatmapConverter.cs
@@ -75,6 +75,7 @@ namespace osu.Game.Beatmaps
beatmap.EpilepsyWarning = original.EpilepsyWarning;
beatmap.SamplesMatchPlaybackRate = original.SamplesMatchPlaybackRate;
beatmap.DistanceSpacing = original.DistanceSpacing;
+ beatmap.GridSize = original.GridSize;
return beatmap;
}
diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs
index a8964a365a..ff9bf4b477 100644
--- a/osu.Game/Beatmaps/BeatmapImporter.cs
+++ b/osu.Game/Beatmaps/BeatmapImporter.cs
@@ -415,7 +415,6 @@ namespace osu.Game.Beatmaps
DifficultyName = decodedInfo.DifficultyName,
OnlineID = decodedInfo.OnlineID,
BeatDivisor = decodedInfo.BeatDivisor,
- GridSize = decodedInfo.GridSize,
TimelineZoom = decodedInfo.TimelineZoom,
MD5Hash = memoryStream.ComputeMD5Hash(),
EndTimeObjectCount = decoded.HitObjects.Count(h => h is IHasDuration),
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index 8328e3df95..6b192063e8 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -143,8 +143,6 @@ namespace osu.Game.Beatmaps
public int BeatDivisor { get; set; } = 4;
- public int GridSize { get; set; }
-
public double TimelineZoom { get; set; } = 1.0;
///
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 92a26464ee..80bfae3036 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -337,7 +337,7 @@ namespace osu.Game.Beatmaps.Formats
break;
case @"GridSize":
- beatmap.BeatmapInfo.GridSize = Parsing.ParseInt(pair.Value);
+ beatmap.GridSize = Parsing.ParseInt(pair.Value);
break;
case @"TimelineZoom":
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index a07e8d2226..1bfba0962c 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -114,7 +114,7 @@ namespace osu.Game.Beatmaps.Formats
writer.WriteLine(FormattableString.Invariant($"Bookmarks: {string.Join(',', beatmap.BeatmapInfo.Bookmarks)}"));
writer.WriteLine(FormattableString.Invariant($"DistanceSpacing: {beatmap.DistanceSpacing}"));
writer.WriteLine(FormattableString.Invariant($"BeatDivisor: {beatmap.BeatmapInfo.BeatDivisor}"));
- writer.WriteLine(FormattableString.Invariant($"GridSize: {beatmap.BeatmapInfo.GridSize}"));
+ writer.WriteLine(FormattableString.Invariant($"GridSize: {beatmap.GridSize}"));
writer.WriteLine(FormattableString.Invariant($"TimelineZoom: {beatmap.BeatmapInfo.TimelineZoom}"));
}
diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs
index b2c8b7604a..ecfc8ea398 100644
--- a/osu.Game/Beatmaps/IBeatmap.cs
+++ b/osu.Game/Beatmaps/IBeatmap.cs
@@ -95,6 +95,8 @@ namespace osu.Game.Beatmaps
///
double DistanceSpacing { get; internal set; }
+ int GridSize { get; internal set; }
+
///
/// Creates a shallow-clone of this beatmap and returns it.
///
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
index 7473882c15..d4a95558cc 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
@@ -385,6 +385,12 @@ namespace osu.Game.Rulesets.Difficulty
set => baseBeatmap.DistanceSpacing = value;
}
+ public int GridSize
+ {
+ get => baseBeatmap.GridSize;
+ set => baseBeatmap.GridSize = value;
+ }
+
#endregion
}
}
diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs
index aa63dfab8d..b1df60126d 100644
--- a/osu.Game/Screens/Edit/EditorBeatmap.cs
+++ b/osu.Game/Screens/Edit/EditorBeatmap.cs
@@ -232,6 +232,12 @@ namespace osu.Game.Screens.Edit
set => PlayableBeatmap.DistanceSpacing = value;
}
+ public int GridSize
+ {
+ get => PlayableBeatmap.GridSize;
+ set => PlayableBeatmap.GridSize = value;
+ }
+
public IBeatmap Clone() => (EditorBeatmap)MemberwiseClone();
private IList mutableHitObjects => (IList)PlayableBeatmap.HitObjects;
From 7f2a6f6f5a143e44ca427060eee77c5acad84d5d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Wed, 12 Jun 2024 13:54:31 +0200
Subject: [PATCH 0125/1255] Move `TimelineZoom` out of `BeatmapInfo`
---
.../Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 2 +-
osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs | 2 +-
osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs | 8 ++++----
osu.Game/Beatmaps/Beatmap.cs | 2 ++
osu.Game/Beatmaps/BeatmapConverter.cs | 1 +
osu.Game/Beatmaps/BeatmapImporter.cs | 1 -
osu.Game/Beatmaps/BeatmapInfo.cs | 2 --
osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +-
osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +-
osu.Game/Beatmaps/IBeatmap.cs | 2 ++
osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 6 ++++++
.../Screens/Edit/Compose/Components/Timeline/Timeline.cs | 6 +++---
osu.Game/Screens/Edit/EditorBeatmap.cs | 6 ++++++
13 files changed, 28 insertions(+), 14 deletions(-)
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index a15485cdf1..af0e4a8b3c 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -115,7 +115,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(1.8, beatmap.DistanceSpacing);
Assert.AreEqual(4, beatmap.BeatmapInfo.BeatDivisor);
Assert.AreEqual(4, beatmap.GridSize);
- Assert.AreEqual(2, beatmap.BeatmapInfo.TimelineZoom);
+ Assert.AreEqual(2, beatmap.TimelineZoom);
}
}
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
index 95cba082a7..1fed9633f7 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
@@ -79,7 +79,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(1.8, beatmap.DistanceSpacing);
Assert.AreEqual(4, beatmapInfo.BeatDivisor);
Assert.AreEqual(4, beatmap.GridSize);
- Assert.AreEqual(2, beatmapInfo.TimelineZoom);
+ Assert.AreEqual(2, beatmap.TimelineZoom);
}
[Test]
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs
index 64c48e74cf..429b458b9f 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs
@@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.Editing
AddStep("Set beat divisor", () => Editor.Dependencies.Get().Value = 16);
AddStep("Set timeline zoom", () =>
{
- originalTimelineZoom = EditorBeatmap.BeatmapInfo.TimelineZoom;
+ originalTimelineZoom = EditorBeatmap.TimelineZoom;
var timeline = Editor.ChildrenOfType().Single();
InputManager.MoveMouseTo(timeline);
@@ -81,19 +81,19 @@ namespace osu.Game.Tests.Visual.Editing
AddAssert("Ensure timeline zoom changed", () =>
{
- changedTimelineZoom = EditorBeatmap.BeatmapInfo.TimelineZoom;
+ changedTimelineZoom = EditorBeatmap.TimelineZoom;
return !Precision.AlmostEquals(changedTimelineZoom, originalTimelineZoom);
});
SaveEditor();
AddAssert("Beatmap has correct beat divisor", () => EditorBeatmap.BeatmapInfo.BeatDivisor == 16);
- AddAssert("Beatmap has correct timeline zoom", () => EditorBeatmap.BeatmapInfo.TimelineZoom == changedTimelineZoom);
+ AddAssert("Beatmap has correct timeline zoom", () => EditorBeatmap.TimelineZoom == changedTimelineZoom);
ReloadEditorToSameBeatmap();
AddAssert("Beatmap still has correct beat divisor", () => EditorBeatmap.BeatmapInfo.BeatDivisor == 16);
- AddAssert("Beatmap still has correct timeline zoom", () => EditorBeatmap.BeatmapInfo.TimelineZoom == changedTimelineZoom);
+ AddAssert("Beatmap still has correct timeline zoom", () => EditorBeatmap.TimelineZoom == changedTimelineZoom);
}
[Test]
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index aacbe359b1..35bd935d66 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -132,6 +132,8 @@ namespace osu.Game.Beatmaps
public int GridSize { get; set; }
+ public double TimelineZoom { get; set; } = 1.0;
+
IBeatmap IBeatmap.Clone() => Clone();
public Beatmap Clone() => (Beatmap)MemberwiseClone();
diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs
index a70d449fc5..140771a5d5 100644
--- a/osu.Game/Beatmaps/BeatmapConverter.cs
+++ b/osu.Game/Beatmaps/BeatmapConverter.cs
@@ -76,6 +76,7 @@ namespace osu.Game.Beatmaps
beatmap.SamplesMatchPlaybackRate = original.SamplesMatchPlaybackRate;
beatmap.DistanceSpacing = original.DistanceSpacing;
beatmap.GridSize = original.GridSize;
+ beatmap.TimelineZoom = original.TimelineZoom;
return beatmap;
}
diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs
index ff9bf4b477..e230f912ab 100644
--- a/osu.Game/Beatmaps/BeatmapImporter.cs
+++ b/osu.Game/Beatmaps/BeatmapImporter.cs
@@ -415,7 +415,6 @@ namespace osu.Game.Beatmaps
DifficultyName = decodedInfo.DifficultyName,
OnlineID = decodedInfo.OnlineID,
BeatDivisor = decodedInfo.BeatDivisor,
- TimelineZoom = decodedInfo.TimelineZoom,
MD5Hash = memoryStream.ComputeMD5Hash(),
EndTimeObjectCount = decoded.HitObjects.Count(h => h is IHasDuration),
TotalObjectCount = decoded.HitObjects.Count
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index 6b192063e8..39cd320ad7 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -143,8 +143,6 @@ namespace osu.Game.Beatmaps
public int BeatDivisor { get; set; } = 4;
- public double TimelineZoom { get; set; } = 1.0;
-
///
/// The time in milliseconds when last exiting the editor with this beatmap loaded.
///
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 80bfae3036..cb2b1820d7 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -341,7 +341,7 @@ namespace osu.Game.Beatmaps.Formats
break;
case @"TimelineZoom":
- beatmap.BeatmapInfo.TimelineZoom = Math.Max(0, Parsing.ParseDouble(pair.Value));
+ beatmap.TimelineZoom = Math.Max(0, Parsing.ParseDouble(pair.Value));
break;
}
}
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index 1bfba0962c..d705deb5df 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -115,7 +115,7 @@ namespace osu.Game.Beatmaps.Formats
writer.WriteLine(FormattableString.Invariant($"DistanceSpacing: {beatmap.DistanceSpacing}"));
writer.WriteLine(FormattableString.Invariant($"BeatDivisor: {beatmap.BeatmapInfo.BeatDivisor}"));
writer.WriteLine(FormattableString.Invariant($"GridSize: {beatmap.GridSize}"));
- writer.WriteLine(FormattableString.Invariant($"TimelineZoom: {beatmap.BeatmapInfo.TimelineZoom}"));
+ writer.WriteLine(FormattableString.Invariant($"TimelineZoom: {beatmap.TimelineZoom}"));
}
private void handleMetadata(TextWriter writer)
diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs
index ecfc8ea398..99a9d31807 100644
--- a/osu.Game/Beatmaps/IBeatmap.cs
+++ b/osu.Game/Beatmaps/IBeatmap.cs
@@ -97,6 +97,8 @@ namespace osu.Game.Beatmaps
int GridSize { get; internal set; }
+ double TimelineZoom { get; internal set; }
+
///
/// Creates a shallow-clone of this beatmap and returns it.
///
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
index d4a95558cc..ebdde0fca6 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
@@ -391,6 +391,12 @@ namespace osu.Game.Rulesets.Difficulty
set => baseBeatmap.GridSize = value;
}
+ public double TimelineZoom
+ {
+ get => baseBeatmap.TimelineZoom;
+ set => baseBeatmap.TimelineZoom = value;
+ }
+
#endregion
}
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
index a2704e550c..7c1f2e3730 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
@@ -147,7 +147,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
Scheduler.AddOnce(applyVisualOffset, beatmap);
}, true);
- Zoom = (float)(defaultTimelineZoom * editorBeatmap.BeatmapInfo.TimelineZoom);
+ Zoom = (float)(defaultTimelineZoom * editorBeatmap.TimelineZoom);
}
private void applyVisualOffset(IBindable beatmap)
@@ -215,7 +215,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
float minimumZoom = getZoomLevelForVisibleMilliseconds(10000);
float maximumZoom = getZoomLevelForVisibleMilliseconds(500);
- float initialZoom = (float)Math.Clamp(defaultTimelineZoom * (editorBeatmap.BeatmapInfo.TimelineZoom == 0 ? 1 : editorBeatmap.BeatmapInfo.TimelineZoom), minimumZoom, maximumZoom);
+ float initialZoom = (float)Math.Clamp(defaultTimelineZoom * (editorBeatmap.TimelineZoom == 0 ? 1 : editorBeatmap.TimelineZoom), minimumZoom, maximumZoom);
SetupZoom(initialZoom, minimumZoom, maximumZoom);
@@ -237,7 +237,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
protected override void OnZoomChanged()
{
base.OnZoomChanged();
- editorBeatmap.BeatmapInfo.TimelineZoom = Zoom / defaultTimelineZoom;
+ editorBeatmap.TimelineZoom = Zoom / defaultTimelineZoom;
}
protected override void UpdateAfterChildren()
diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs
index b1df60126d..0d07e16828 100644
--- a/osu.Game/Screens/Edit/EditorBeatmap.cs
+++ b/osu.Game/Screens/Edit/EditorBeatmap.cs
@@ -238,6 +238,12 @@ namespace osu.Game.Screens.Edit
set => PlayableBeatmap.GridSize = value;
}
+ public double TimelineZoom
+ {
+ get => PlayableBeatmap.TimelineZoom;
+ set => PlayableBeatmap.TimelineZoom = value;
+ }
+
public IBeatmap Clone() => (EditorBeatmap)MemberwiseClone();
private IList mutableHitObjects => (IList)PlayableBeatmap.HitObjects;
From d373f752d667c35ae68ef47eced2dcb3c94920ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Wed, 12 Jun 2024 13:58:00 +0200
Subject: [PATCH 0126/1255] Move `Countdown` out of `BeatmapInfo`
---
.../Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 4 ++--
osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs | 2 +-
osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs | 8 ++++----
osu.Game/Beatmaps/Beatmap.cs | 2 ++
osu.Game/Beatmaps/BeatmapInfo.cs | 3 ---
osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +-
osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +-
osu.Game/Beatmaps/IBeatmap.cs | 2 ++
osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 6 ++++++
osu.Game/Screens/Edit/EditorBeatmap.cs | 6 ++++++
osu.Game/Screens/Edit/Setup/DesignSection.cs | 6 +++---
11 files changed, 28 insertions(+), 15 deletions(-)
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index af0e4a8b3c..9cea6ef507 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -88,7 +88,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.IsFalse(beatmap.SpecialStyle);
Assert.IsFalse(beatmap.WidescreenStoryboard);
Assert.IsFalse(beatmap.SamplesMatchPlaybackRate);
- Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown);
+ Assert.AreEqual(CountdownType.None, beatmap.Countdown);
Assert.AreEqual(0, beatmapInfo.CountdownOffset);
}
}
@@ -957,7 +957,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.That(decoded.WidescreenStoryboard, Is.False);
Assert.That(decoded.EpilepsyWarning, Is.False);
Assert.That(decoded.SamplesMatchPlaybackRate, Is.False);
- Assert.That(decoded.BeatmapInfo.Countdown, Is.EqualTo(CountdownType.Normal));
+ Assert.That(decoded.Countdown, Is.EqualTo(CountdownType.Normal));
Assert.That(decoded.BeatmapInfo.CountdownOffset, Is.EqualTo(0));
Assert.That(decoded.BeatmapInfo.Metadata.PreviewTime, Is.EqualTo(-1));
Assert.That(decoded.BeatmapInfo.Ruleset.OnlineID, Is.EqualTo(0));
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
index 1fed9633f7..bc6628cea0 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
@@ -57,7 +57,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
Assert.AreEqual(false, beatmap.LetterboxInBreaks);
Assert.AreEqual(false, beatmap.WidescreenStoryboard);
- Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown);
+ Assert.AreEqual(CountdownType.None, beatmap.Countdown);
Assert.AreEqual(0, beatmapInfo.CountdownOffset);
}
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs b/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs
index 9a66e1676d..c91c22a145 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs
@@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.Editing
{
AddStep("turn countdown off", () => designSection.EnableCountdown.Current.Value = false);
- AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.None);
+ AddAssert("beatmap has correct type", () => editorBeatmap.Countdown == CountdownType.None);
AddUntilStep("other controls hidden", () => !designSection.CountdownSettings.IsPresent);
}
@@ -60,12 +60,12 @@ namespace osu.Game.Tests.Visual.Editing
{
AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true);
- AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.Normal);
+ AddAssert("beatmap has correct type", () => editorBeatmap.Countdown == CountdownType.Normal);
AddUntilStep("other controls shown", () => designSection.CountdownSettings.IsPresent);
AddStep("change countdown speed", () => designSection.CountdownSpeed.Current.Value = CountdownType.DoubleSpeed);
- AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.DoubleSpeed);
+ AddAssert("beatmap has correct type", () => editorBeatmap.Countdown == CountdownType.DoubleSpeed);
AddUntilStep("other controls still shown", () => designSection.CountdownSettings.IsPresent);
}
@@ -74,7 +74,7 @@ namespace osu.Game.Tests.Visual.Editing
{
AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true);
- AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.Normal);
+ AddAssert("beatmap has correct type", () => editorBeatmap.Countdown == CountdownType.Normal);
checkOffsetAfter("1", 1);
checkOffsetAfter(string.Empty, 0);
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index 35bd935d66..54fb1fd3b1 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -134,6 +134,8 @@ namespace osu.Game.Beatmaps
public double TimelineZoom { get; set; } = 1.0;
+ public CountdownType Countdown { get; set; } = CountdownType.Normal;
+
IBeatmap IBeatmap.Clone() => Clone();
public Beatmap Clone() => (Beatmap)MemberwiseClone();
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index 39cd320ad7..0214ae4c3a 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -148,9 +148,6 @@ namespace osu.Game.Beatmaps
///
public double? EditorTimestamp { get; set; }
- [Ignored]
- public CountdownType Countdown { get; set; } = CountdownType.Normal;
-
///
/// The number of beats to move the countdown backwards (compared to its default location).
///
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index cb2b1820d7..48959025c9 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -305,7 +305,7 @@ namespace osu.Game.Beatmaps.Formats
break;
case @"Countdown":
- beatmap.BeatmapInfo.Countdown = Enum.Parse(pair.Value);
+ beatmap.Countdown = Enum.Parse(pair.Value);
break;
case @"CountdownOffset":
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index d705deb5df..73399e93d0 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -81,7 +81,7 @@ namespace osu.Game.Beatmaps.Formats
if (!string.IsNullOrEmpty(beatmap.Metadata.AudioFile)) writer.WriteLine(FormattableString.Invariant($"AudioFilename: {Path.GetFileName(beatmap.Metadata.AudioFile)}"));
writer.WriteLine(FormattableString.Invariant($"AudioLeadIn: {beatmap.AudioLeadIn}"));
writer.WriteLine(FormattableString.Invariant($"PreviewTime: {beatmap.Metadata.PreviewTime}"));
- writer.WriteLine(FormattableString.Invariant($"Countdown: {(int)beatmap.BeatmapInfo.Countdown}"));
+ writer.WriteLine(FormattableString.Invariant($"Countdown: {(int)beatmap.Countdown}"));
writer.WriteLine(FormattableString.Invariant(
$"SampleSet: {toLegacySampleBank(((beatmap.ControlPointInfo as LegacyControlPointInfo)?.SamplePoints.FirstOrDefault() ?? SampleControlPoint.DEFAULT).SampleBank)}"));
writer.WriteLine(FormattableString.Invariant($"StackLeniency: {beatmap.StackLeniency}"));
diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs
index 99a9d31807..cf7bd29088 100644
--- a/osu.Game/Beatmaps/IBeatmap.cs
+++ b/osu.Game/Beatmaps/IBeatmap.cs
@@ -99,6 +99,8 @@ namespace osu.Game.Beatmaps
double TimelineZoom { get; internal set; }
+ CountdownType Countdown { get; internal set; }
+
///
/// Creates a shallow-clone of this beatmap and returns it.
///
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
index ebdde0fca6..a444cc135b 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
@@ -397,6 +397,12 @@ namespace osu.Game.Rulesets.Difficulty
set => baseBeatmap.TimelineZoom = value;
}
+ public CountdownType Countdown
+ {
+ get => baseBeatmap.Countdown;
+ set => baseBeatmap.Countdown = value;
+ }
+
#endregion
}
}
diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs
index 0d07e16828..a86d1fbaef 100644
--- a/osu.Game/Screens/Edit/EditorBeatmap.cs
+++ b/osu.Game/Screens/Edit/EditorBeatmap.cs
@@ -244,6 +244,12 @@ namespace osu.Game.Screens.Edit
set => PlayableBeatmap.TimelineZoom = value;
}
+ public CountdownType Countdown
+ {
+ get => PlayableBeatmap.Countdown;
+ set => PlayableBeatmap.Countdown = value;
+ }
+
public IBeatmap Clone() => (EditorBeatmap)MemberwiseClone();
private IList mutableHitObjects => (IList)PlayableBeatmap.HitObjects;
diff --git a/osu.Game/Screens/Edit/Setup/DesignSection.cs b/osu.Game/Screens/Edit/Setup/DesignSection.cs
index 4c4755064f..b40f1bea72 100644
--- a/osu.Game/Screens/Edit/Setup/DesignSection.cs
+++ b/osu.Game/Screens/Edit/Setup/DesignSection.cs
@@ -38,7 +38,7 @@ namespace osu.Game.Screens.Edit.Setup
EnableCountdown = new LabelledSwitchButton
{
Label = EditorSetupStrings.EnableCountdown,
- Current = { Value = Beatmap.BeatmapInfo.Countdown != CountdownType.None },
+ Current = { Value = Beatmap.Countdown != CountdownType.None },
Description = EditorSetupStrings.CountdownDescription
},
CountdownSettings = new FillFlowContainer
@@ -52,7 +52,7 @@ namespace osu.Game.Screens.Edit.Setup
CountdownSpeed = new LabelledEnumDropdown
{
Label = EditorSetupStrings.CountdownSpeed,
- Current = { Value = Beatmap.BeatmapInfo.Countdown != CountdownType.None ? Beatmap.BeatmapInfo.Countdown : CountdownType.Normal },
+ Current = { Value = Beatmap.Countdown != CountdownType.None ? Beatmap.Countdown : CountdownType.Normal },
Items = Enum.GetValues().Where(type => type != CountdownType.None)
},
CountdownOffset = new LabelledNumberBox
@@ -118,7 +118,7 @@ namespace osu.Game.Screens.Edit.Setup
private void updateBeatmap()
{
- Beatmap.BeatmapInfo.Countdown = EnableCountdown.Current.Value ? CountdownSpeed.Current.Value : CountdownType.None;
+ Beatmap.Countdown = EnableCountdown.Current.Value ? CountdownSpeed.Current.Value : CountdownType.None;
Beatmap.BeatmapInfo.CountdownOffset = int.TryParse(CountdownOffset.Current.Value, NumberStyles.None, CultureInfo.InvariantCulture, out int offset) ? offset : 0;
Beatmap.WidescreenStoryboard = widescreenSupport.Current.Value;
From dd50d6fa6e61d02e7e0a995a8e47569a5d7564e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Wed, 12 Jun 2024 14:03:02 +0200
Subject: [PATCH 0127/1255] Move `CountdownOffset` out of `BeatmapInfo`
---
osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 4 ++--
osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs | 2 +-
.../Database/RealmSubscriptionRegistrationTests.cs | 2 +-
osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs | 2 +-
osu.Game/Beatmaps/Beatmap.cs | 2 ++
osu.Game/Beatmaps/BeatmapConverter.cs | 1 +
osu.Game/Beatmaps/BeatmapInfo.cs | 5 -----
osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +-
osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 4 ++--
osu.Game/Beatmaps/IBeatmap.cs | 5 +++++
osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 6 ++++++
osu.Game/Screens/Edit/EditorBeatmap.cs | 6 ++++++
osu.Game/Screens/Edit/Setup/DesignSection.cs | 6 +++---
13 files changed, 31 insertions(+), 16 deletions(-)
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index 9cea6ef507..ab0ec7ee39 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -89,7 +89,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.IsFalse(beatmap.WidescreenStoryboard);
Assert.IsFalse(beatmap.SamplesMatchPlaybackRate);
Assert.AreEqual(CountdownType.None, beatmap.Countdown);
- Assert.AreEqual(0, beatmapInfo.CountdownOffset);
+ Assert.AreEqual(0, beatmap.CountdownOffset);
}
}
@@ -958,7 +958,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.That(decoded.EpilepsyWarning, Is.False);
Assert.That(decoded.SamplesMatchPlaybackRate, Is.False);
Assert.That(decoded.Countdown, Is.EqualTo(CountdownType.Normal));
- Assert.That(decoded.BeatmapInfo.CountdownOffset, Is.EqualTo(0));
+ Assert.That(decoded.CountdownOffset, Is.EqualTo(0));
Assert.That(decoded.BeatmapInfo.Metadata.PreviewTime, Is.EqualTo(-1));
Assert.That(decoded.BeatmapInfo.Ruleset.OnlineID, Is.EqualTo(0));
});
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
index bc6628cea0..e57a4fff62 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
@@ -58,7 +58,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(false, beatmap.LetterboxInBreaks);
Assert.AreEqual(false, beatmap.WidescreenStoryboard);
Assert.AreEqual(CountdownType.None, beatmap.Countdown);
- Assert.AreEqual(0, beatmapInfo.CountdownOffset);
+ Assert.AreEqual(0, beatmap.CountdownOffset);
}
[Test]
diff --git a/osu.Game.Tests/Database/RealmSubscriptionRegistrationTests.cs b/osu.Game.Tests/Database/RealmSubscriptionRegistrationTests.cs
index 45842a952a..541f3b0417 100644
--- a/osu.Game.Tests/Database/RealmSubscriptionRegistrationTests.cs
+++ b/osu.Game.Tests/Database/RealmSubscriptionRegistrationTests.cs
@@ -41,7 +41,7 @@ namespace osu.Game.Tests.Database
Assert.That(lastChanges?.ModifiedIndices, Is.Empty);
Assert.That(lastChanges?.NewModifiedIndices, Is.Empty);
- realm.Write(r => r.All().First().Beatmaps.First().CountdownOffset = 5);
+ realm.Write(r => r.All().First().Beatmaps.First().EditorTimestamp = 5);
realm.Run(r => r.Refresh());
Assert.That(collectionChanges, Is.EqualTo(1));
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs b/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs
index c91c22a145..0011a4ceb4 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs
@@ -94,7 +94,7 @@ namespace osu.Game.Tests.Visual.Editing
AddStep("commit text", () => InputManager.Key(Key.Enter));
AddAssert($"displayed value is {expectedFinalValue}", () => designSection.CountdownOffset.Current.Value == expectedFinalValue.ToString(CultureInfo.InvariantCulture));
- AddAssert($"beatmap value is {expectedFinalValue}", () => editorBeatmap.BeatmapInfo.CountdownOffset == expectedFinalValue);
+ AddAssert($"beatmap value is {expectedFinalValue}", () => editorBeatmap.CountdownOffset == expectedFinalValue);
}
private partial class TestDesignSection : DesignSection
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index 54fb1fd3b1..19a7ee3303 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -136,6 +136,8 @@ namespace osu.Game.Beatmaps
public CountdownType Countdown { get; set; } = CountdownType.Normal;
+ public int CountdownOffset { get; set; }
+
IBeatmap IBeatmap.Clone() => Clone();
public Beatmap Clone() => (Beatmap)MemberwiseClone();
diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs
index 140771a5d5..8e917a179e 100644
--- a/osu.Game/Beatmaps/BeatmapConverter.cs
+++ b/osu.Game/Beatmaps/BeatmapConverter.cs
@@ -77,6 +77,7 @@ namespace osu.Game.Beatmaps
beatmap.DistanceSpacing = original.DistanceSpacing;
beatmap.GridSize = original.GridSize;
beatmap.TimelineZoom = original.TimelineZoom;
+ beatmap.CountdownOffset = original.CountdownOffset;
return beatmap;
}
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index 0214ae4c3a..3ed15f52fb 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -148,11 +148,6 @@ namespace osu.Game.Beatmaps
///
public double? EditorTimestamp { get; set; }
- ///
- /// The number of beats to move the countdown backwards (compared to its default location).
- ///
- public int CountdownOffset { get; set; }
-
#endregion
public bool Equals(BeatmapInfo? other)
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 48959025c9..14de31a2a3 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -309,7 +309,7 @@ namespace osu.Game.Beatmaps.Formats
break;
case @"CountdownOffset":
- beatmap.BeatmapInfo.CountdownOffset = Parsing.ParseInt(pair.Value);
+ beatmap.CountdownOffset = Parsing.ParseInt(pair.Value);
break;
}
}
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index 73399e93d0..b924b7aea5 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -97,8 +97,8 @@ namespace osu.Game.Beatmaps.Formats
// writer.WriteLine(@"SkinPreference:" + b.SkinPreference);
if (beatmap.EpilepsyWarning)
writer.WriteLine(@"EpilepsyWarning: 1");
- if (beatmap.BeatmapInfo.CountdownOffset > 0)
- writer.WriteLine(FormattableString.Invariant($@"CountdownOffset: {beatmap.BeatmapInfo.CountdownOffset}"));
+ if (beatmap.CountdownOffset > 0)
+ writer.WriteLine(FormattableString.Invariant($@"CountdownOffset: {beatmap.CountdownOffset}"));
if (onlineRulesetID == 3)
writer.WriteLine(FormattableString.Invariant($"SpecialStyle: {(beatmap.SpecialStyle ? '1' : '0')}"));
writer.WriteLine(FormattableString.Invariant($"WidescreenStoryboard: {(beatmap.WidescreenStoryboard ? '1' : '0')}"));
diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs
index cf7bd29088..f08fdfaf6a 100644
--- a/osu.Game/Beatmaps/IBeatmap.cs
+++ b/osu.Game/Beatmaps/IBeatmap.cs
@@ -101,6 +101,11 @@ namespace osu.Game.Beatmaps
CountdownType Countdown { get; internal set; }
+ ///
+ /// The number of beats to move the countdown backwards (compared to its default location).
+ ///
+ int CountdownOffset { get; internal set; }
+
///
/// Creates a shallow-clone of this beatmap and returns it.
///
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
index a444cc135b..6dd85cefe4 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
@@ -403,6 +403,12 @@ namespace osu.Game.Rulesets.Difficulty
set => baseBeatmap.Countdown = value;
}
+ public int CountdownOffset
+ {
+ get => baseBeatmap.CountdownOffset;
+ set => baseBeatmap.CountdownOffset = value;
+ }
+
#endregion
}
}
diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs
index a86d1fbaef..deb46c3d2e 100644
--- a/osu.Game/Screens/Edit/EditorBeatmap.cs
+++ b/osu.Game/Screens/Edit/EditorBeatmap.cs
@@ -250,6 +250,12 @@ namespace osu.Game.Screens.Edit
set => PlayableBeatmap.Countdown = value;
}
+ public int CountdownOffset
+ {
+ get => PlayableBeatmap.CountdownOffset;
+ set => PlayableBeatmap.CountdownOffset = value;
+ }
+
public IBeatmap Clone() => (EditorBeatmap)MemberwiseClone();
private IList mutableHitObjects => (IList)PlayableBeatmap.HitObjects;
diff --git a/osu.Game/Screens/Edit/Setup/DesignSection.cs b/osu.Game/Screens/Edit/Setup/DesignSection.cs
index b40f1bea72..3ed0a78175 100644
--- a/osu.Game/Screens/Edit/Setup/DesignSection.cs
+++ b/osu.Game/Screens/Edit/Setup/DesignSection.cs
@@ -58,7 +58,7 @@ namespace osu.Game.Screens.Edit.Setup
CountdownOffset = new LabelledNumberBox
{
Label = EditorSetupStrings.CountdownOffset,
- Current = { Value = Beatmap.BeatmapInfo.CountdownOffset.ToString() },
+ Current = { Value = Beatmap.CountdownOffset.ToString() },
Description = EditorSetupStrings.CountdownOffsetDescription,
}
}
@@ -113,13 +113,13 @@ namespace osu.Game.Screens.Edit.Setup
{
updateBeatmap();
// update displayed text to ensure parsed value matches display (i.e. if empty string was provided).
- CountdownOffset.Current.Value = Beatmap.BeatmapInfo.CountdownOffset.ToString(CultureInfo.InvariantCulture);
+ CountdownOffset.Current.Value = Beatmap.CountdownOffset.ToString(CultureInfo.InvariantCulture);
}
private void updateBeatmap()
{
Beatmap.Countdown = EnableCountdown.Current.Value ? CountdownSpeed.Current.Value : CountdownType.None;
- Beatmap.BeatmapInfo.CountdownOffset = int.TryParse(CountdownOffset.Current.Value, NumberStyles.None, CultureInfo.InvariantCulture, out int offset) ? offset : 0;
+ Beatmap.CountdownOffset = int.TryParse(CountdownOffset.Current.Value, NumberStyles.None, CultureInfo.InvariantCulture, out int offset) ? offset : 0;
Beatmap.WidescreenStoryboard = widescreenSupport.Current.Value;
Beatmap.EpilepsyWarning = epilepsyWarning.Current.Value;
From 9fbf2872e1a63547f44adafc790f8bc57a1c18a7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Wed, 12 Jun 2024 14:27:35 +0200
Subject: [PATCH 0128/1255] Remove no longer applicable region marking
---
osu.Game/Beatmaps/BeatmapInfo.cs | 4 ----
1 file changed, 4 deletions(-)
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index 3ed15f52fb..0a6719a96a 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -134,8 +134,6 @@ namespace osu.Game.Beatmaps
Status = BeatmapOnlineStatus.None;
}
- #region Properties we may not want persisted (but also maybe no harm?)
-
///
/// The time at which this beatmap was last played by the local user.
///
@@ -148,8 +146,6 @@ namespace osu.Game.Beatmaps
///
public double? EditorTimestamp { get; set; }
- #endregion
-
public bool Equals(BeatmapInfo? other)
{
if (ReferenceEquals(this, other)) return true;
From 9906ab3449719c0d01b3f847edbabe73359a3d95 Mon Sep 17 00:00:00 2001
From: Aurelian
Date: Wed, 12 Jun 2024 19:25:48 +0200
Subject: [PATCH 0129/1255] Fixed double adjustment of hitobject beatlength
---
osu.Game/Screens/Edit/Timing/TapTimingControl.cs | 3 ---
osu.Game/Screens/Edit/Timing/TimingSection.cs | 2 --
2 files changed, 5 deletions(-)
diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
index 937235c7bc..55404702ac 100644
--- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
+++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
@@ -231,9 +231,6 @@ namespace osu.Game.Screens.Edit.Timing
timing.BeatLength = 60000 / (timing.BPM + adjust);
- if (beatmap.AdjustNotesOnOffsetBPMChange.Value)
- timing.SetHitObjectBPM(beatmap, 60000 / (timing.BPM - adjust));
-
beatmap.UpdateAllHitObjects();
}
diff --git a/osu.Game/Screens/Edit/Timing/TimingSection.cs b/osu.Game/Screens/Edit/Timing/TimingSection.cs
index 1ae85e6c9e..5be1de467d 100644
--- a/osu.Game/Screens/Edit/Timing/TimingSection.cs
+++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs
@@ -48,10 +48,8 @@ namespace osu.Game.Screens.Edit.Timing
if (!Beatmap.AdjustNotesOnOffsetBPMChange.Value || ControlPoint.Value == null)
return;
- ChangeHandler?.BeginChange();
ControlPoint.Value.SetHitObjectBPM(Beatmap, val.OldValue);
Beatmap.UpdateAllHitObjects();
- ChangeHandler?.EndChange();
});
}
From 9b076a8b03f7f7258e2c20b237ab94490c245e42 Mon Sep 17 00:00:00 2001
From: Aurelian
Date: Wed, 12 Jun 2024 19:57:17 +0200
Subject: [PATCH 0130/1255] Moved HitObject adjustment methods to a static
helper class
---
.../ControlPoints/TimingControlPoint.cs | 35 ----------------
osu.Game/Screens/Edit/Timing/GroupSection.cs | 2 +-
.../Screens/Edit/Timing/TapTimingControl.cs | 2 +-
osu.Game/Screens/Edit/Timing/TimingSection.cs | 41 ++++++++++++++++++-
4 files changed, 42 insertions(+), 38 deletions(-)
diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
index 234a34dc87..4e69486e2d 100644
--- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
@@ -2,14 +2,9 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Collections.Generic;
-using System.Linq;
using osu.Framework.Bindables;
-using osu.Framework.Utils;
using osu.Game.Beatmaps.Timing;
using osu.Game.Graphics;
-using osu.Game.Rulesets.Objects;
-using osu.Game.Rulesets.Objects.Types;
using osuTK.Graphics;
namespace osu.Game.Beatmaps.ControlPoints
@@ -110,35 +105,5 @@ namespace osu.Game.Beatmaps.ControlPoints
&& BeatLength.Equals(other.BeatLength);
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), TimeSignature, BeatLength, OmitFirstBarLine);
-
- public List HitObjectsInTimingRange(IBeatmap beatmap)
- {
- // If the first group, we grab all hitobjects prior to the next, if the last group, we grab all remaining hitobjects
- double startTime = beatmap.ControlPointInfo.TimingPoints.Any(x => x.Time < Time) ? Time : double.MinValue;
- double endTime = beatmap.ControlPointInfo.TimingPoints.FirstOrDefault(x => x.Time > Time)?.Time ?? double.MaxValue;
-
- return beatmap.HitObjects.Where(x => Precision.AlmostBigger(x.StartTime, startTime) && Precision.DefinitelyBigger(endTime, x.StartTime)).ToList();
- }
-
- public void AdjustHitObjectOffset(IBeatmap beatmap, double adjust)
- {
- foreach (HitObject hitObject in HitObjectsInTimingRange(beatmap))
- {
- hitObject.StartTime += adjust;
- }
- }
-
- public void SetHitObjectBPM(IBeatmap beatmap, double oldBeatLength)
- {
- foreach (HitObject hitObject in HitObjectsInTimingRange(beatmap))
- {
- double beat = (hitObject.StartTime - Time) / oldBeatLength;
-
- hitObject.StartTime = (beat * BeatLength) + Time;
-
- if (hitObject is not IHasRepeats && hitObject is IHasDuration hitObjectWithDuration)
- hitObjectWithDuration.Duration *= BeatLength / oldBeatLength;
- }
- }
}
}
diff --git a/osu.Game/Screens/Edit/Timing/GroupSection.cs b/osu.Game/Screens/Edit/Timing/GroupSection.cs
index 7d0eab1f7f..c5a4422192 100644
--- a/osu.Game/Screens/Edit/Timing/GroupSection.cs
+++ b/osu.Game/Screens/Edit/Timing/GroupSection.cs
@@ -112,7 +112,7 @@ namespace osu.Game.Screens.Edit.Timing
{
// Only adjust hit object offsets if the group contains a timing control point
if (Beatmap.AdjustNotesOnOffsetBPMChange.Value && cp is TimingControlPoint tp)
- tp.AdjustHitObjectOffset(Beatmap, time - SelectedGroup.Value.Time);
+ TimingSectionAdjustments.AdjustHitObjectOffset(Beatmap, tp, time - SelectedGroup.Value.Time);
Beatmap.ControlPointInfo.Add(time, cp);
}
diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
index 55404702ac..f2e37369d1 100644
--- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
+++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
@@ -209,7 +209,7 @@ namespace osu.Game.Screens.Edit.Timing
foreach (var cp in currentGroupItems)
{
if (beatmap.AdjustNotesOnOffsetBPMChange.Value && cp is TimingControlPoint tp)
- tp.AdjustHitObjectOffset(beatmap, adjust);
+ TimingSectionAdjustments.AdjustHitObjectOffset(beatmap, tp, adjust);
beatmap.ControlPointInfo.Add(newOffset, cp);
}
diff --git a/osu.Game/Screens/Edit/Timing/TimingSection.cs b/osu.Game/Screens/Edit/Timing/TimingSection.cs
index 5be1de467d..ba335d55eb 100644
--- a/osu.Game/Screens/Edit/Timing/TimingSection.cs
+++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs
@@ -1,11 +1,17 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
+using osu.Framework.Utils;
+using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.UserInterfaceV2;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Screens.Edit.Timing
{
@@ -48,7 +54,7 @@ namespace osu.Game.Screens.Edit.Timing
if (!Beatmap.AdjustNotesOnOffsetBPMChange.Value || ControlPoint.Value == null)
return;
- ControlPoint.Value.SetHitObjectBPM(Beatmap, val.OldValue);
+ TimingSectionAdjustments.SetHitObjectBPM(Beatmap, ControlPoint.Value, val.OldValue);
Beatmap.UpdateAllHitObjects();
});
}
@@ -128,4 +134,37 @@ namespace osu.Game.Screens.Edit.Timing
private static double beatLengthToBpm(double beatLength) => 60000 / beatLength;
}
+
+ public static class TimingSectionAdjustments
+ {
+ public static List HitObjectsInTimingRange(IBeatmap beatmap, double time)
+ {
+ // If the first group, we grab all hitobjects prior to the next, if the last group, we grab all remaining hitobjects
+ double startTime = beatmap.ControlPointInfo.TimingPoints.Any(x => x.Time < time) ? time : double.MinValue;
+ double endTime = beatmap.ControlPointInfo.TimingPoints.FirstOrDefault(x => x.Time > time)?.Time ?? double.MaxValue;
+
+ return beatmap.HitObjects.Where(x => Precision.AlmostBigger(x.StartTime, startTime) && Precision.DefinitelyBigger(endTime, x.StartTime)).ToList();
+ }
+
+ public static void AdjustHitObjectOffset(IBeatmap beatmap, TimingControlPoint timingControlPoint, double adjust)
+ {
+ foreach (HitObject hitObject in HitObjectsInTimingRange(beatmap, timingControlPoint.Time))
+ {
+ hitObject.StartTime += adjust;
+ }
+ }
+
+ public static void SetHitObjectBPM(IBeatmap beatmap, TimingControlPoint timingControlPoint, double oldBeatLength)
+ {
+ foreach (HitObject hitObject in HitObjectsInTimingRange(beatmap, timingControlPoint.Time))
+ {
+ double beat = (hitObject.StartTime - timingControlPoint.Time) / oldBeatLength;
+
+ hitObject.StartTime = (beat * timingControlPoint.BeatLength) + timingControlPoint.Time;
+
+ if (hitObject is not IHasRepeats && hitObject is IHasDuration hitObjectWithDuration)
+ hitObjectWithDuration.Duration *= timingControlPoint.BeatLength / oldBeatLength;
+ }
+ }
+ }
}
From c67e2dc301696654132b2d31f9f07abc4ede47c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Wed, 12 Jun 2024 14:51:20 +0200
Subject: [PATCH 0131/1255] Bump schema version
---
osu.Game/Database/RealmAccess.cs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs
index 1ece81be50..33b06f32b1 100644
--- a/osu.Game/Database/RealmAccess.cs
+++ b/osu.Game/Database/RealmAccess.cs
@@ -93,8 +93,9 @@ namespace osu.Game.Database
/// 39 2023-12-19 Migrate any EndTimeObjectCount and TotalObjectCount values of 0 to -1 to better identify non-calculated values.
/// 40 2023-12-21 Add ScoreInfo.Version to keep track of which build scores were set on.
/// 41 2024-04-17 Add ScoreInfo.TotalScoreWithoutMods for future mod multiplier rebalances.
+ /// 42 2024-06-12 Removed several properties from ScoreInfo which did not need to be persisted to realm.
///
- private const int schema_version = 41;
+ private const int schema_version = 42;
///
/// Lock object which is held during sections, blocking realm retrieval during blocking periods.
From 04527f3c9da63b5fc54d9afd3c7304b0634a8434 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Thu, 13 Jun 2024 09:30:00 +0200
Subject: [PATCH 0132/1255] Fix `TestBeatmap` not transferring newly migrated
properties
---
osu.Game/Tests/Beatmaps/TestBeatmap.cs | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs
index de7bcfcfaa..863badbd4a 100644
--- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs
+++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs
@@ -28,6 +28,17 @@ namespace osu.Game.Tests.Beatmaps
ControlPointInfo = baseBeatmap.ControlPointInfo;
Breaks = baseBeatmap.Breaks;
UnhandledEventLines = baseBeatmap.UnhandledEventLines;
+ AudioLeadIn = baseBeatmap.AudioLeadIn;
+ StackLeniency = baseBeatmap.StackLeniency;
+ SpecialStyle = baseBeatmap.SpecialStyle;
+ LetterboxInBreaks = baseBeatmap.LetterboxInBreaks;
+ WidescreenStoryboard = baseBeatmap.WidescreenStoryboard;
+ EpilepsyWarning = baseBeatmap.EpilepsyWarning;
+ SamplesMatchPlaybackRate = baseBeatmap.SamplesMatchPlaybackRate;
+ DistanceSpacing = baseBeatmap.DistanceSpacing;
+ GridSize = baseBeatmap.GridSize;
+ TimelineZoom = baseBeatmap.TimelineZoom;
+ CountdownOffset = baseBeatmap.CountdownOffset;
if (withHitObjects)
HitObjects = baseBeatmap.HitObjects;
From 2fb22f1febef5ad5ab120c24875e69b798ed984e Mon Sep 17 00:00:00 2001
From: Nathen
Date: Sun, 23 Jun 2024 19:17:19 -0400
Subject: [PATCH 0133/1255] Move the return value for deviation below the local
functions
---
.../Difficulty/TaikoPerformanceCalculator.cs | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index ab809a0ade..e42b015176 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -129,14 +129,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
const double z = 2.32634787404; // 99% critical value for the normal distribution (one-tailed).
- double? deviationGreatWindow = calcDeviationGreatWindow();
- double? deviationGoodWindow = calcDeviationGoodWindow();
-
- if (deviationGreatWindow is null)
- return deviationGoodWindow;
-
- return Math.Min(deviationGreatWindow.Value, deviationGoodWindow!.Value);
-
// The upper bound on deviation, calculated with the ratio of 300s to objects, and the great hit window.
double? calcDeviationGreatWindow()
{
@@ -171,6 +163,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
// We can be 99% confident that the deviation is not higher than:
return h100 / (Math.Sqrt(2) * SpecialFunctions.ErfInv(pLowerBound));
}
+
+ double? deviationGreatWindow = calcDeviationGreatWindow();
+ double? deviationGoodWindow = calcDeviationGoodWindow();
+
+ if (deviationGreatWindow is null)
+ return deviationGoodWindow;
+
+ return Math.Min(deviationGreatWindow.Value, deviationGoodWindow!.Value);
}
private int totalHits => countGreat + countOk + countMeh + countMiss;
From 952540024eeb41c039d5a1072d7b1d520ff2cab3 Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Sun, 14 Jul 2024 20:43:16 +0200
Subject: [PATCH 0134/1255] addition bank toggles in compose
---
osu.Game/Rulesets/Edit/HitObjectComposer.cs | 24 +++-
.../Components/ComposeBlueprintContainer.cs | 9 +-
.../Components/EditorSelectionHandler.cs | 131 +++++++++++++++++-
3 files changed, 149 insertions(+), 15 deletions(-)
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index 3c38a7258e..ac9c830f03 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -85,6 +85,7 @@ namespace osu.Game.Rulesets.Edit
private EditorRadioButtonCollection toolboxCollection;
private FillFlowContainer togglesCollection;
private FillFlowContainer sampleBankTogglesCollection;
+ private FillFlowContainer sampleAdditionBankTogglesCollection;
private IBindable hasTiming;
private Bindable autoSeekOnPlacement;
@@ -184,7 +185,17 @@ namespace osu.Game.Rulesets.Edit
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
},
- }
+ },
+ new EditorToolboxGroup("additions (Alt-Q~R)")
+ {
+ Child = sampleAdditionBankTogglesCollection = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(0, 5),
+ },
+ },
}
},
}
@@ -230,6 +241,7 @@ namespace osu.Game.Rulesets.Edit
togglesCollection.AddRange(TernaryStates.Select(b => new DrawableTernaryButton(b)));
sampleBankTogglesCollection.AddRange(BlueprintContainer.SampleBankTernaryStates.Select(b => new DrawableTernaryButton(b)));
+ sampleAdditionBankTogglesCollection.AddRange(BlueprintContainer.SampleAdditionBankTernaryStates.Select(b => new DrawableTernaryButton(b)));
setSelectTool();
@@ -358,7 +370,7 @@ namespace osu.Game.Rulesets.Edit
protected override bool OnKeyDown(KeyDownEvent e)
{
- if (e.ControlPressed || e.AltPressed || e.SuperPressed)
+ if (e.ControlPressed || e.SuperPressed)
return false;
if (checkLeftToggleFromKey(e.Key, out int leftIndex))
@@ -375,9 +387,11 @@ namespace osu.Game.Rulesets.Edit
if (checkRightToggleFromKey(e.Key, out int rightIndex))
{
- var item = e.ShiftPressed
- ? sampleBankTogglesCollection.ElementAtOrDefault(rightIndex)
- : togglesCollection.ElementAtOrDefault(rightIndex);
+ var item = e.AltPressed
+ ? sampleAdditionBankTogglesCollection.ElementAtOrDefault(rightIndex)
+ : e.ShiftPressed
+ ? sampleBankTogglesCollection.ElementAtOrDefault(rightIndex)
+ : togglesCollection.ElementAtOrDefault(rightIndex);
if (item is DrawableTernaryButton button)
{
diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
index fc8bce4c96..671180348d 100644
--- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
@@ -62,7 +62,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
private void load()
{
MainTernaryStates = CreateTernaryButtons().ToArray();
- SampleBankTernaryStates = createSampleBankTernaryButtons().ToArray();
+ SampleBankTernaryStates = createSampleBankTernaryButtons(SelectionHandler.SelectionBankStates).ToArray();
+ SampleAdditionBankTernaryStates = createSampleBankTernaryButtons(SelectionHandler.SelectionAdditionBankStates).ToArray();
AddInternal(new DrawableRulesetDependenciesProvidingContainer(Composer.Ruleset)
{
@@ -219,6 +220,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
public TernaryButton[] SampleBankTernaryStates { get; private set; }
+ public TernaryButton[] SampleAdditionBankTernaryStates { get; private set; }
+
///
/// Create all ternary states required to be displayed to the user.
///
@@ -231,9 +234,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
yield return new TernaryButton(kvp.Value, kvp.Key.Replace("hit", string.Empty).Titleize(), () => GetIconForSample(kvp.Key));
}
- private IEnumerable createSampleBankTernaryButtons()
+ private IEnumerable createSampleBankTernaryButtons(Dictionary> sampleBankStates)
{
- foreach (var kvp in SelectionHandler.SelectionBankStates)
+ foreach (var kvp in sampleBankStates)
yield return new TernaryButton(kvp.Value, kvp.Key.Titleize(), () => getIconForBank(kvp.Key));
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs
index a4efe66bf8..04baaa6134 100644
--- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs
@@ -58,6 +58,11 @@ namespace osu.Game.Screens.Edit.Compose.Components
///
public readonly Dictionary> SelectionBankStates = new Dictionary>();
+ ///
+ /// The state of each sample addition bank type for all selected hitobjects.
+ ///
+ public readonly Dictionary> SelectionAdditionBankStates = new Dictionary>();
+
///
/// Set up ternary state bindables and bind them to selection/hitobject changes (in both directions)
///
@@ -90,7 +95,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
// Never remove a sample bank.
// These are basically radio buttons, not toggles.
- if (SelectedItems.All(h => h.Samples.All(s => s.Bank == bankName)))
+ if (SelectedItems.All(h => h.Samples.Where(o => o.Name == HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName)))
bindable.Value = TernaryState.True;
}
@@ -127,8 +132,70 @@ namespace osu.Game.Screens.Edit.Compose.Components
SelectionBankStates[bankName] = bindable;
}
+ foreach (string bankName in HitSampleInfo.AllBanks.Prepend(HIT_BANK_AUTO))
+ {
+ var bindable = new Bindable
+ {
+ Description = bankName.Titleize()
+ };
+
+ bindable.ValueChanged += state =>
+ {
+ switch (state.NewValue)
+ {
+ case TernaryState.False:
+ if (SelectedItems.Count == 0)
+ {
+ // Ensure that if this is the last selected bank, it should remain selected.
+ if (SelectionAdditionBankStates.Values.All(b => b.Value == TernaryState.False))
+ bindable.Value = TernaryState.True;
+ }
+
+ // Auto should never apply when there is a selection made.
+ // Completely empty selections should be allowed in the case that none of the selected objects have any addition samples.
+ break;
+
+ case TernaryState.True:
+ if (SelectedItems.Count == 0)
+ {
+ // Ensure the user can't stack multiple bank selections when there's no hitobject selection.
+ // Note that in normal scenarios this is sorted out by the feedback from applying the bank to the selected objects.
+ foreach (var other in SelectionAdditionBankStates.Values)
+ {
+ if (other != bindable)
+ other.Value = TernaryState.False;
+ }
+ }
+ else
+ {
+ // Auto should just not apply if there's a selection already made.
+ // Maybe we could make it a disabled button in the future, but right now the editor buttons don't support disabled state.
+ if (bankName == HIT_BANK_AUTO)
+ {
+ bindable.Value = TernaryState.False;
+ break;
+ }
+
+ // If none of the selected objects have any addition samples, we should not apply the bank.
+ if (SelectedItems.All(h => h.Samples.All(o => o.Name == HitSampleInfo.HIT_NORMAL)))
+ {
+ bindable.Value = TernaryState.False;
+ break;
+ }
+
+ SetSampleAdditionBank(bankName);
+ }
+
+ break;
+ }
+ };
+
+ SelectionAdditionBankStates[bankName] = bindable;
+ }
+
// start with normal selected.
SelectionBankStates[SampleControlPoint.DEFAULT_BANK].Value = TernaryState.True;
+ SelectionAdditionBankStates[SampleControlPoint.DEFAULT_BANK].Value = TernaryState.True;
foreach (string sampleName in HitSampleInfo.AllAdditions)
{
@@ -186,7 +253,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
foreach ((string bankName, var bindable) in SelectionBankStates)
{
- bindable.Value = GetStateFromSelection(samplesInSelection.SelectMany(s => s), h => h.Bank == bankName);
+ bindable.Value = GetStateFromSelection(samplesInSelection.SelectMany(s => s).Where(o => o.Name == HitSampleInfo.HIT_NORMAL), h => h.Bank == bankName);
+ }
+
+ foreach ((string bankName, var bindable) in SelectionAdditionBankStates)
+ {
+ bindable.Value = GetStateFromSelection(samplesInSelection.SelectMany(s => s).Where(o => o.Name != HitSampleInfo.HIT_NORMAL), h => h.Bank == bankName);
}
IEnumerable> enumerateAllSamples(HitObject hitObject)
@@ -213,12 +285,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
bool hasRelevantBank(HitObject hitObject)
{
- bool result = hitObject.Samples.All(s => s.Bank == bankName);
+ bool result = hitObject.Samples.Where(o => o.Name == HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName);
if (hitObject is IHasRepeats hasRepeats)
{
foreach (var node in hasRepeats.NodeSamples)
- result &= node.All(s => s.Bank == bankName);
+ result &= node.Where(o => o.Name == HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName);
}
return result;
@@ -229,15 +301,54 @@ namespace osu.Game.Screens.Edit.Compose.Components
EditorBeatmap.PerformOnSelection(h =>
{
- if (h.Samples.All(s => s.Bank == bankName))
+ if (h.Samples.Where(o => o.Name == HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName))
return;
- h.Samples = h.Samples.Select(s => s.With(newBank: bankName)).ToList();
+ h.Samples = h.Samples.Select(s => s.Name == HitSampleInfo.HIT_NORMAL ? s.With(newBank: bankName) : s).ToList();
if (h is IHasRepeats hasRepeats)
{
for (int i = 0; i < hasRepeats.NodeSamples.Count; ++i)
- hasRepeats.NodeSamples[i] = hasRepeats.NodeSamples[i].Select(s => s.With(newBank: bankName)).ToList();
+ hasRepeats.NodeSamples[i] = hasRepeats.NodeSamples[i].Select(s => s.Name == HitSampleInfo.HIT_NORMAL ? s.With(newBank: bankName) : s).ToList();
+ }
+
+ EditorBeatmap.Update(h);
+ });
+ }
+
+ ///
+ /// Sets the sample addition bank for all selected s.
+ ///
+ /// The name of the sample bank.
+ public void SetSampleAdditionBank(string bankName)
+ {
+ bool hasRelevantBank(HitObject hitObject)
+ {
+ bool result = hitObject.Samples.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName);
+
+ if (hitObject is IHasRepeats hasRepeats)
+ {
+ foreach (var node in hasRepeats.NodeSamples)
+ result &= node.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName);
+ }
+
+ return result;
+ }
+
+ if (SelectedItems.All(hasRelevantBank))
+ return;
+
+ EditorBeatmap.PerformOnSelection(h =>
+ {
+ if (h.Samples.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName))
+ return;
+
+ h.Samples = h.Samples.Select(s => s.Name != HitSampleInfo.HIT_NORMAL ? s.With(newBank: bankName) : s).ToList();
+
+ if (h is IHasRepeats hasRepeats)
+ {
+ for (int i = 0; i < hasRepeats.NodeSamples.Count; ++i)
+ hasRepeats.NodeSamples[i] = hasRepeats.NodeSamples[i].Select(s => s.Name != HitSampleInfo.HIT_NORMAL ? s.With(newBank: bankName) : s).ToList();
}
EditorBeatmap.Update(h);
@@ -366,6 +477,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
Items = SelectionBankStates.Select(kvp =>
new TernaryStateToggleMenuItem(kvp.Value.Description) { State = { BindTarget = kvp.Value } }).ToArray()
};
+
+ yield return new OsuMenuItem("Addition bank")
+ {
+ Items = SelectionAdditionBankStates.Select(kvp =>
+ new TernaryStateToggleMenuItem(kvp.Value.Description) { State = { BindTarget = kvp.Value } }).ToArray()
+ };
}
#endregion
From 5e86a01e5e3e91404d5021572cd8170661d59618 Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Sun, 14 Jul 2024 20:59:46 +0200
Subject: [PATCH 0135/1255] allow hotkeys in sample point piece
---
osu.Game/Rulesets/Edit/HitObjectComposer.cs | 28 +++++++++++++------
.../Components/Timeline/SamplePointPiece.cs | 19 +++++++++----
2 files changed, 33 insertions(+), 14 deletions(-)
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index ac9c830f03..f2713739b9 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -373,6 +373,8 @@ namespace osu.Game.Rulesets.Edit
if (e.ControlPressed || e.SuperPressed)
return false;
+ bool handled = false;
+
if (checkLeftToggleFromKey(e.Key, out int leftIndex))
{
var item = toolboxCollection.Items.ElementAtOrDefault(leftIndex);
@@ -387,20 +389,30 @@ namespace osu.Game.Rulesets.Edit
if (checkRightToggleFromKey(e.Key, out int rightIndex))
{
- var item = e.AltPressed
- ? sampleAdditionBankTogglesCollection.ElementAtOrDefault(rightIndex)
- : e.ShiftPressed
- ? sampleBankTogglesCollection.ElementAtOrDefault(rightIndex)
- : togglesCollection.ElementAtOrDefault(rightIndex);
+ if (e.ShiftPressed || e.AltPressed)
+ {
+ if (e.ShiftPressed)
+ attemptToggle(rightIndex, sampleBankTogglesCollection);
+
+ if (e.AltPressed)
+ attemptToggle(rightIndex, sampleAdditionBankTogglesCollection);
+ }
+ else
+ attemptToggle(rightIndex, togglesCollection);
+ }
+
+ return handled || base.OnKeyDown(e);
+
+ void attemptToggle(int index, FillFlowContainer collection)
+ {
+ var item = collection.ElementAtOrDefault(index);
if (item is DrawableTernaryButton button)
{
button.Button.Toggle();
- return true;
+ handled = true;
}
}
-
- return base.OnKeyDown(e);
}
private bool checkLeftToggleFromKey(Key key, out int index)
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs
index 8c7603021a..07c6cab7a1 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs
@@ -381,20 +381,27 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
protected override bool OnKeyDown(KeyDownEvent e)
{
- if (e.ControlPressed || e.AltPressed || e.SuperPressed || !checkRightToggleFromKey(e.Key, out int rightIndex))
+ if (e.ControlPressed || e.SuperPressed || !checkRightToggleFromKey(e.Key, out int rightIndex))
return base.OnKeyDown(e);
- if (e.ShiftPressed)
+ if (e.ShiftPressed || e.AltPressed)
{
string? newBank = banks.ElementAtOrDefault(rightIndex);
if (string.IsNullOrEmpty(newBank))
return true;
- setBank(newBank);
- updatePrimaryBankState();
- setAdditionBank(newBank);
- updateAdditionBankState();
+ if (e.ShiftPressed)
+ {
+ setBank(newBank);
+ updatePrimaryBankState();
+ }
+
+ if (e.AltPressed)
+ {
+ setAdditionBank(newBank);
+ updateAdditionBankState();
+ }
}
else
{
From df7a42a00e95ceae944114fe162f266a5fc7708d Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Sun, 14 Jul 2024 22:33:23 +0200
Subject: [PATCH 0136/1255] fix placement samples
---
osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 37 +++++++++++--------
.../Components/ComposeBlueprintContainer.cs | 18 ++++++++-
2 files changed, 38 insertions(+), 17 deletions(-)
diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs
index 2817e26abd..26b18828d3 100644
--- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs
+++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs
@@ -37,6 +37,11 @@ namespace osu.Game.Rulesets.Edit
///
public bool AutomaticBankAssignment { get; set; }
+ ///
+ /// Whether the sample addition bank should be taken from the previous hit objects.
+ ///
+ public bool AutomaticAdditionBankAssignment { get; set; }
+
///
/// The that is being placed.
///
@@ -157,26 +162,26 @@ namespace osu.Game.Rulesets.Edit
}
var lastHitObject = getPreviousHitObject();
+ var lastHitNormal = lastHitObject?.Samples?.FirstOrDefault(o => o.Name == HitSampleInfo.HIT_NORMAL);
- if (AutomaticBankAssignment)
+ if (AutomaticAdditionBankAssignment)
{
- // Create samples based on the sample settings of the previous hit object
- if (lastHitObject != null)
- {
- for (int i = 0; i < HitObject.Samples.Count; i++)
- HitObject.Samples[i] = lastHitObject.CreateHitSampleInfo(HitObject.Samples[i].Name);
- }
+ // Inherit the addition bank from the previous hit object
+ // If there is no previous addition, inherit from the normal sample
+ var lastAddition = lastHitObject?.Samples?.FirstOrDefault(o => o.Name != HitSampleInfo.HIT_NORMAL) ?? lastHitNormal;
+
+ if (lastAddition != null)
+ HitObject.Samples = HitObject.Samples.Select(s => s.Name != HitSampleInfo.HIT_NORMAL ? s.With(newBank: lastAddition.Bank) : s).ToList();
}
- else
- {
- var lastHitNormal = lastHitObject?.Samples?.FirstOrDefault(o => o.Name == HitSampleInfo.HIT_NORMAL);
- if (lastHitNormal != null)
- {
- // Only inherit the volume from the previous hit object
- for (int i = 0; i < HitObject.Samples.Count; i++)
- HitObject.Samples[i] = HitObject.Samples[i].With(newVolume: lastHitNormal.Volume);
- }
+ if (lastHitNormal != null)
+ {
+ if (AutomaticBankAssignment)
+ // Inherit the bank from the previous hit object
+ HitObject.Samples = HitObject.Samples.Select(s => s.Name == HitSampleInfo.HIT_NORMAL ? s.With(newBank: lastHitNormal.Bank) : s).ToList();
+
+ // Inherit the volume from the previous hit object
+ HitObject.Samples = HitObject.Samples.Select(s => s.With(newVolume: lastHitNormal.Volume)).ToList();
}
if (HitObject is IHasRepeats hasRepeats)
diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
index 671180348d..fc81bc5706 100644
--- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
@@ -89,6 +89,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
foreach (var kvp in SelectionHandler.SelectionBankStates)
kvp.Value.BindValueChanged(_ => updatePlacementSamples());
+
+ foreach (var kvp in SelectionHandler.SelectionAdditionBankStates)
+ kvp.Value.BindValueChanged(_ => updatePlacementSamples());
}
protected override void TransferBlueprintFor(HitObject hitObject, DrawableHitObject drawableObject)
@@ -177,6 +180,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
foreach (var kvp in SelectionHandler.SelectionBankStates)
bankChanged(kvp.Key, kvp.Value.Value);
+
+ foreach (var kvp in SelectionHandler.SelectionAdditionBankStates)
+ additionBankChanged(kvp.Key, kvp.Value.Value);
}
private void sampleChanged(string sampleName, TernaryState state)
@@ -208,7 +214,17 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (bankName == EditorSelectionHandler.HIT_BANK_AUTO)
CurrentPlacement.AutomaticBankAssignment = state == TernaryState.True;
else if (state == TernaryState.True)
- CurrentPlacement.HitObject.Samples = CurrentPlacement.HitObject.Samples.Select(s => s.With(newBank: bankName)).ToList();
+ CurrentPlacement.HitObject.Samples = CurrentPlacement.HitObject.Samples.Select(s => s.Name == HitSampleInfo.HIT_NORMAL ? s.With(newBank: bankName) : s).ToList();
+ }
+
+ private void additionBankChanged(string bankName, TernaryState state)
+ {
+ if (CurrentPlacement == null) return;
+
+ if (bankName == EditorSelectionHandler.HIT_BANK_AUTO)
+ CurrentPlacement.AutomaticAdditionBankAssignment = state == TernaryState.True;
+ else if (state == TernaryState.True)
+ CurrentPlacement.HitObject.Samples = CurrentPlacement.HitObject.Samples.Select(s => s.Name != HitSampleInfo.HIT_NORMAL ? s.With(newBank: bankName) : s).ToList();
}
public readonly Bindable NewCombo = new Bindable { Description = "New Combo" };
From 9f02a1f1bf248a3ea395f63e6da6eb4ce19ef639 Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Sun, 14 Jul 2024 22:50:15 +0200
Subject: [PATCH 0137/1255] fix addition on node sample not working
---
.../Components/EditorSelectionHandler.cs | 22 +++++++++----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs
index 04baaa6134..3a0428e7fc 100644
--- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs
@@ -176,8 +176,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
break;
}
- // If none of the selected objects have any addition samples, we should not apply the bank.
- if (SelectedItems.All(h => h.Samples.All(o => o.Name == HitSampleInfo.HIT_NORMAL)))
+ // If none of the selected objects have any addition samples, we should not apply the addition bank.
+ if (SelectedItems.SelectMany(enumerateAllSamples).All(h => h.All(o => o.Name == HitSampleInfo.HIT_NORMAL)))
{
bindable.Value = TernaryState.False;
break;
@@ -260,16 +260,16 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
bindable.Value = GetStateFromSelection(samplesInSelection.SelectMany(s => s).Where(o => o.Name != HitSampleInfo.HIT_NORMAL), h => h.Bank == bankName);
}
+ }
- IEnumerable> enumerateAllSamples(HitObject hitObject)
+ private IEnumerable> enumerateAllSamples(HitObject hitObject)
+ {
+ yield return hitObject.Samples;
+
+ if (hitObject is IHasRepeats withRepeats)
{
- yield return hitObject.Samples;
-
- if (hitObject is IHasRepeats withRepeats)
- {
- foreach (var node in withRepeats.NodeSamples)
- yield return node;
- }
+ foreach (var node in withRepeats.NodeSamples)
+ yield return node;
}
}
@@ -340,7 +340,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
EditorBeatmap.PerformOnSelection(h =>
{
- if (h.Samples.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName))
+ if (enumerateAllSamples(h).SelectMany(o => o).Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName))
return;
h.Samples = h.Samples.Select(s => s.Name != HitSampleInfo.HIT_NORMAL ? s.With(newBank: bankName) : s).ToList();
From 29c2e821ea3e66f9b7846bae4a19a419cdf38d95 Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Sun, 14 Jul 2024 22:50:25 +0200
Subject: [PATCH 0138/1255] extend tests
---
.../TestSceneHitObjectSampleAdjustments.cs | 136 ++++++++++++++++--
1 file changed, 123 insertions(+), 13 deletions(-)
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs
index 558d8dce94..e962d3975c 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs
@@ -321,6 +321,12 @@ namespace osu.Game.Tests.Visual.Editing
}
});
+ AddStep("add whistle addition", () =>
+ {
+ foreach (var h in EditorBeatmap.HitObjects)
+ h.Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_WHISTLE, HitSampleInfo.BANK_SOFT));
+ });
+
AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
hitObjectHasSampleBank(0, HitSampleInfo.BANK_SOFT);
@@ -333,8 +339,10 @@ namespace osu.Game.Tests.Visual.Editing
InputManager.ReleaseKey(Key.ShiftLeft);
});
- hitObjectHasSampleBank(0, HitSampleInfo.BANK_NORMAL);
- hitObjectHasSampleBank(1, HitSampleInfo.BANK_NORMAL);
+ hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL);
+ hitObjectHasSampleNormalBank(1, HitSampleInfo.BANK_NORMAL);
+ hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_SOFT);
+ hitObjectHasSampleAdditionBank(1, HitSampleInfo.BANK_SOFT);
AddStep("Press drum bank shortcut", () =>
{
@@ -343,8 +351,10 @@ namespace osu.Game.Tests.Visual.Editing
InputManager.ReleaseKey(Key.ShiftLeft);
});
- hitObjectHasSampleBank(0, HitSampleInfo.BANK_DRUM);
- hitObjectHasSampleBank(1, HitSampleInfo.BANK_DRUM);
+ hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_DRUM);
+ hitObjectHasSampleNormalBank(1, HitSampleInfo.BANK_DRUM);
+ hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_SOFT);
+ hitObjectHasSampleAdditionBank(1, HitSampleInfo.BANK_SOFT);
AddStep("Press auto bank shortcut", () =>
{
@@ -354,8 +364,47 @@ namespace osu.Game.Tests.Visual.Editing
});
// Should be a noop.
- hitObjectHasSampleBank(0, HitSampleInfo.BANK_DRUM);
- hitObjectHasSampleBank(1, HitSampleInfo.BANK_DRUM);
+ hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_DRUM);
+ hitObjectHasSampleNormalBank(1, HitSampleInfo.BANK_DRUM);
+ hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_SOFT);
+ hitObjectHasSampleAdditionBank(1, HitSampleInfo.BANK_SOFT);
+
+ AddStep("Press addition normal bank shortcut", () =>
+ {
+ InputManager.PressKey(Key.AltLeft);
+ InputManager.Key(Key.W);
+ InputManager.ReleaseKey(Key.AltLeft);
+ });
+
+ hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_DRUM);
+ hitObjectHasSampleNormalBank(1, HitSampleInfo.BANK_DRUM);
+ hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_NORMAL);
+ hitObjectHasSampleAdditionBank(1, HitSampleInfo.BANK_NORMAL);
+
+ AddStep("Press addition drum bank shortcut", () =>
+ {
+ InputManager.PressKey(Key.AltLeft);
+ InputManager.Key(Key.R);
+ InputManager.ReleaseKey(Key.AltLeft);
+ });
+
+ hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_DRUM);
+ hitObjectHasSampleNormalBank(1, HitSampleInfo.BANK_DRUM);
+ hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_DRUM);
+ hitObjectHasSampleAdditionBank(1, HitSampleInfo.BANK_DRUM);
+
+ AddStep("Press auto bank shortcut", () =>
+ {
+ InputManager.PressKey(Key.AltLeft);
+ InputManager.Key(Key.Q);
+ InputManager.ReleaseKey(Key.AltLeft);
+ });
+
+ // Should be a noop.
+ hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_DRUM);
+ hitObjectHasSampleNormalBank(1, HitSampleInfo.BANK_DRUM);
+ hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_DRUM);
+ hitObjectHasSampleAdditionBank(1, HitSampleInfo.BANK_DRUM);
}
[Test]
@@ -373,7 +422,21 @@ namespace osu.Game.Tests.Visual.Editing
InputManager.ReleaseKey(Key.ShiftLeft);
});
- checkPlacementSample(HitSampleInfo.BANK_NORMAL);
+ AddStep("Press soft addition bank shortcut", () =>
+ {
+ InputManager.PressKey(Key.AltLeft);
+ InputManager.Key(Key.E);
+ InputManager.ReleaseKey(Key.AltLeft);
+ });
+
+ checkPlacementSampleBank(HitSampleInfo.BANK_NORMAL);
+
+ AddStep("Press finish sample shortcut", () =>
+ {
+ InputManager.Key(Key.E);
+ });
+
+ checkPlacementSampleAdditionBank(HitSampleInfo.BANK_SOFT);
AddStep("Press drum bank shortcut", () =>
{
@@ -382,7 +445,18 @@ namespace osu.Game.Tests.Visual.Editing
InputManager.ReleaseKey(Key.ShiftLeft);
});
- checkPlacementSample(HitSampleInfo.BANK_DRUM);
+ checkPlacementSampleBank(HitSampleInfo.BANK_DRUM);
+ checkPlacementSampleAdditionBank(HitSampleInfo.BANK_SOFT);
+
+ AddStep("Press drum addition bank shortcut", () =>
+ {
+ InputManager.PressKey(Key.AltLeft);
+ InputManager.Key(Key.R);
+ InputManager.ReleaseKey(Key.AltLeft);
+ });
+
+ checkPlacementSampleBank(HitSampleInfo.BANK_DRUM);
+ checkPlacementSampleAdditionBank(HitSampleInfo.BANK_DRUM);
AddStep("Press auto bank shortcut", () =>
{
@@ -391,15 +465,29 @@ namespace osu.Game.Tests.Visual.Editing
InputManager.ReleaseKey(Key.ShiftLeft);
});
- checkPlacementSample(HitSampleInfo.BANK_NORMAL);
+ checkPlacementSampleBank(HitSampleInfo.BANK_NORMAL);
+ checkPlacementSampleAdditionBank(HitSampleInfo.BANK_DRUM);
+
+ AddStep("Press auto addition bank shortcut", () =>
+ {
+ InputManager.PressKey(Key.AltLeft);
+ InputManager.Key(Key.Q);
+ InputManager.ReleaseKey(Key.AltLeft);
+ });
+
+ checkPlacementSampleBank(HitSampleInfo.BANK_NORMAL);
+ checkPlacementSampleAdditionBank(HitSampleInfo.BANK_NORMAL);
AddStep("Move after second object", () => EditorClock.Seek(750));
- checkPlacementSample(HitSampleInfo.BANK_SOFT);
+ checkPlacementSampleBank(HitSampleInfo.BANK_SOFT);
+ checkPlacementSampleAdditionBank(HitSampleInfo.BANK_SOFT);
AddStep("Move to first object", () => EditorClock.Seek(0));
- checkPlacementSample(HitSampleInfo.BANK_NORMAL);
+ checkPlacementSampleBank(HitSampleInfo.BANK_NORMAL);
+ checkPlacementSampleAdditionBank(HitSampleInfo.BANK_NORMAL);
- void checkPlacementSample(string expected) => AddAssert($"Placement sample is {expected}", () => EditorBeatmap.PlacementObject.Value.Samples.First().Bank, () => Is.EqualTo(expected));
+ void checkPlacementSampleBank(string expected) => AddAssert($"Placement sample is {expected}", () => EditorBeatmap.PlacementObject.Value.Samples.First(s => s.Name == HitSampleInfo.HIT_NORMAL).Bank, () => Is.EqualTo(expected));
+ void checkPlacementSampleAdditionBank(string expected) => AddAssert($"Placement sample addition is {expected}", () => EditorBeatmap.PlacementObject.Value.Samples.First(s => s.Name != HitSampleInfo.HIT_NORMAL).Bank, () => Is.EqualTo(expected));
}
[Test]
@@ -480,7 +568,29 @@ namespace osu.Game.Tests.Visual.Editing
hitObjectHasSamples(2, HitSampleInfo.HIT_NORMAL);
hitObjectNodeHasSampleBank(2, 0, HitSampleInfo.BANK_DRUM);
hitObjectNodeHasSamples(2, 0, HitSampleInfo.HIT_NORMAL);
- hitObjectNodeHasSampleBank(2, 1, HitSampleInfo.BANK_DRUM);
+ hitObjectNodeHasSampleNormalBank(2, 1, HitSampleInfo.BANK_DRUM);
+ hitObjectNodeHasSampleAdditionBank(2, 1, HitSampleInfo.BANK_SOFT);
+ hitObjectNodeHasSamples(2, 1, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_WHISTLE);
+
+ AddStep("set normal addition bank", () =>
+ {
+ InputManager.PressKey(Key.LAlt);
+ InputManager.Key(Key.W);
+ InputManager.ReleaseKey(Key.LAlt);
+ });
+
+ hitObjectHasSampleBank(0, HitSampleInfo.BANK_DRUM);
+ hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL);
+
+ hitObjectHasSampleBank(1, HitSampleInfo.BANK_DRUM);
+ hitObjectHasSamples(1, HitSampleInfo.HIT_NORMAL);
+
+ hitObjectHasSampleBank(2, HitSampleInfo.BANK_DRUM);
+ hitObjectHasSamples(2, HitSampleInfo.HIT_NORMAL);
+ hitObjectNodeHasSampleBank(2, 0, HitSampleInfo.BANK_DRUM);
+ hitObjectNodeHasSamples(2, 0, HitSampleInfo.HIT_NORMAL);
+ hitObjectNodeHasSampleNormalBank(2, 1, HitSampleInfo.BANK_DRUM);
+ hitObjectNodeHasSampleAdditionBank(2, 1, HitSampleInfo.BANK_NORMAL);
hitObjectNodeHasSamples(2, 1, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_WHISTLE);
}
From e5bc359b8811fcd0281dd6052031e05a950644b5 Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Sun, 14 Jul 2024 23:39:19 +0200
Subject: [PATCH 0139/1255] fix test
---
osu.Game.Tests/Visual/Editing/TestScenePlacementBlueprint.cs | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/osu.Game.Tests/Visual/Editing/TestScenePlacementBlueprint.cs b/osu.Game.Tests/Visual/Editing/TestScenePlacementBlueprint.cs
index e9b442f8dd..fba84108d0 100644
--- a/osu.Game.Tests/Visual/Editing/TestScenePlacementBlueprint.cs
+++ b/osu.Game.Tests/Visual/Editing/TestScenePlacementBlueprint.cs
@@ -123,7 +123,9 @@ namespace osu.Game.Tests.Visual.Editing
AddStep("enable automatic bank assignment", () =>
{
InputManager.PressKey(Key.LShift);
+ InputManager.PressKey(Key.LAlt);
InputManager.Key(Key.Q);
+ InputManager.ReleaseKey(Key.LAlt);
InputManager.ReleaseKey(Key.LShift);
});
AddStep("select circle placement tool", () => InputManager.Key(Key.Number2));
@@ -186,7 +188,9 @@ namespace osu.Game.Tests.Visual.Editing
AddStep("select drum bank", () =>
{
InputManager.PressKey(Key.LShift);
+ InputManager.PressKey(Key.LAlt);
InputManager.Key(Key.R);
+ InputManager.ReleaseKey(Key.LAlt);
InputManager.ReleaseKey(Key.LShift);
});
AddStep("enable clap addition", () => InputManager.Key(Key.R));
From e25642b48472789aaffa5254106d763a4cee9cad Mon Sep 17 00:00:00 2001
From: StanR
Date: Mon, 15 Jul 2024 14:45:31 +0500
Subject: [PATCH 0140/1255] Implement a bunch of rhythm difficulty calculation
fixes
---
.../Difficulty/Evaluators/RhythmEvaluator.cs | 103 ++++++++++++++----
.../Preprocessing/OsuDifficultyHitObject.cs | 11 +-
2 files changed, 90 insertions(+), 24 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
index f2218a89a7..39c7572c85 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
@@ -2,6 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Collections.Generic;
+using System.Linq;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
using osu.Game.Rulesets.Osu.Objects;
@@ -10,21 +12,65 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
{
public static class RhythmEvaluator
{
- private const int history_time_max = 5000; // 5 seconds of calculatingRhythmBonus max.
- private const double rhythm_multiplier = 0.75;
+ private readonly struct Island : IEquatable
+ {
+ public Island()
+ {
+ }
+
+ public Island(int firstDelta, double epsilon)
+ {
+ AddDelta(firstDelta, epsilon);
+ }
+
+ public List Deltas { get; } = new List();
+
+ public void AddDelta(int delta, double epsilon)
+ {
+ int existingDelta = Deltas.FirstOrDefault(x => Math.Abs(x - delta) >= epsilon);
+
+ Deltas.Add(existingDelta == default ? delta : existingDelta);
+ }
+
+ public double AverageDelta() => Math.Max(Deltas.Average(), OsuDifficultyHitObject.MIN_DELTA_TIME);
+
+ public override int GetHashCode()
+ {
+ // we need to compare all deltas and they must be in the exact same order we added them
+ string joinedDeltas = string.Join(string.Empty, Deltas);
+ return joinedDeltas.GetHashCode();
+ }
+
+ public bool Equals(Island other)
+ {
+ return other.GetHashCode() == GetHashCode();
+ }
+
+ public override bool Equals(object? obj)
+ {
+ return obj?.GetHashCode() == GetHashCode();
+ }
+ }
+
+ private const int history_time_max = 5 * 1000; // 5 seconds of calculatingRhythmBonus max.
+ private const double rhythm_multiplier = 1.14;
+ private const int max_island_size = 7;
///
/// Calculates a rhythm multiplier for the difficulty of the tap associated with historic data of the current .
///
public static double EvaluateDifficultyOf(DifficultyHitObject current)
{
+ Dictionary islandCounts = new Dictionary();
+
if (current.BaseObject is Spinner)
return 0;
- int previousIslandSize = 0;
-
double rhythmComplexitySum = 0;
- int islandSize = 1;
+
+ var island = new Island();
+ var previousIsland = new Island();
+
double startRatio = 0; // store the ratio of the current start of an island to buff for tighter rhythms
bool firstDeltaSwitch = false;
@@ -50,6 +96,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
double currDelta = currObj.StrainTime;
double prevDelta = prevObj.StrainTime;
double lastDelta = lastObj.StrainTime;
+
double currRatio = 1.0 + 6.0 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses.
double windowPenalty = Math.Min(1, Math.Max(0, Math.Abs(prevDelta - currDelta) - currObj.HitWindowGreat * 0.3) / (currObj.HitWindowGreat * 0.3));
@@ -58,12 +105,17 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
double effectiveRatio = windowPenalty * currRatio;
+ double deltaDifferenceEpsilon = currObj.HitWindowGreat * 0.3;
+
if (firstDeltaSwitch)
{
- if (!(prevDelta > 1.25 * currDelta || prevDelta * 1.25 < currDelta))
+ if (!(Math.Abs(prevDelta - currDelta) > deltaDifferenceEpsilon))
{
- if (islandSize < 7)
- islandSize++; // island is still progressing, count size.
+ if (island.Deltas.Count < max_island_size)
+ {
+ // island is still progressing
+ island.AddDelta((int)currDelta, deltaDifferenceEpsilon);
+ }
}
else
{
@@ -73,33 +125,44 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
if (prevObj.BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle
effectiveRatio *= 0.25;
- if (previousIslandSize == islandSize) // repeated island size (ex: triplet -> triplet)
- effectiveRatio *= 0.25;
-
- if (previousIslandSize % 2 == islandSize % 2) // repeated island polartiy (2 -> 4, 3 -> 5)
+ if (previousIsland.Deltas.Count % 2 == island.Deltas.Count % 2) // repeated island polartiy (2 -> 4, 3 -> 5)
effectiveRatio *= 0.50;
- if (lastDelta > prevDelta + 10 && prevDelta > currDelta + 10) // previous increase happened a note ago, 1/1->1/2-1/4, dont want to buff this.
+ if (lastDelta > prevDelta + deltaDifferenceEpsilon && prevDelta > currDelta + deltaDifferenceEpsilon) // previous increase happened a note ago, 1/1->1/2-1/4, dont want to buff this.
effectiveRatio *= 0.125;
- rhythmComplexitySum += Math.Sqrt(effectiveRatio * startRatio) * currHistoricalDecay * Math.Sqrt(4 + islandSize) / 2 * Math.Sqrt(4 + previousIslandSize) / 2;
+ if (islandCounts.ContainsKey(island))
+ {
+ islandCounts[island]++;
+
+ // repeated island (ex: triplet -> triplet)
+ double power = Math.Max(0.75, logistic(island.AverageDelta(), 3, 0.15, 9));
+ effectiveRatio *= Math.Pow(1.0 / islandCounts[island], power);
+ }
+ else
+ {
+ islandCounts.Add(island, 1);
+ }
+
+ rhythmComplexitySum += Math.Sqrt(effectiveRatio * startRatio) * currHistoricalDecay;
startRatio = effectiveRatio;
- previousIslandSize = islandSize; // log the last island size.
+ previousIsland = island;
- if (prevDelta * 1.25 < currDelta) // we're slowing down, stop counting
+ if (prevDelta + deltaDifferenceEpsilon < currDelta) // we're slowing down, stop counting
firstDeltaSwitch = false; // if we're speeding up, this stays true and we keep counting island size.
- islandSize = 1;
+ island = new Island((int)currDelta, deltaDifferenceEpsilon);
}
}
- else if (prevDelta > 1.25 * currDelta) // we want to be speeding up.
+ else if (prevDelta > currDelta + deltaDifferenceEpsilon) // we want to be speeding up.
{
// Begin counting island until we change speed again.
firstDeltaSwitch = true;
startRatio = effectiveRatio;
- islandSize = 1;
+
+ island = new Island((int)currDelta, deltaDifferenceEpsilon);
}
lastObj = prevObj;
@@ -108,5 +171,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
return Math.Sqrt(4 + rhythmComplexitySum * rhythm_multiplier) / 2; //produces multiplier that can be applied to strain. range [1, infinity) (not really though)
}
+
+ private static double logistic(double x, double maxValue, double multiplier, double offset) => (maxValue / (1 + Math.Pow(Math.E, offset - multiplier * x)));
}
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
index 0e537632b1..95535274c1 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
@@ -20,7 +20,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
///
public const int NORMALISED_RADIUS = 50; // Change radius to 50 to make 100 the diameter. Easier for mental maths.
- private const int min_delta_time = 25;
+ public const int MIN_DELTA_TIME = 25;
+
private const float maximum_slider_radius = NORMALISED_RADIUS * 2.4f;
private const float assumed_slider_radius = NORMALISED_RADIUS * 1.8f;
@@ -93,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
this.lastObject = (OsuHitObject)lastObject;
// Capped to 25ms to prevent difficulty calculation breaking from simultaneous objects.
- StrainTime = Math.Max(DeltaTime, min_delta_time);
+ StrainTime = Math.Max(DeltaTime, MIN_DELTA_TIME);
if (BaseObject is Slider sliderObject)
{
@@ -143,7 +144,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
computeSliderCursorPosition(currentSlider);
// Bonus for repeat sliders until a better per nested object strain system can be achieved.
TravelDistance = currentSlider.LazyTravelDistance * (float)Math.Pow(1 + currentSlider.RepeatCount / 2.5, 1.0 / 2.5);
- TravelTime = Math.Max(currentSlider.LazyTravelTime / clockRate, min_delta_time);
+ TravelTime = Math.Max(currentSlider.LazyTravelTime / clockRate, MIN_DELTA_TIME);
}
// We don't need to calculate either angle or distance when one of the last->curr objects is a spinner
@@ -167,8 +168,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
if (lastObject is Slider lastSlider)
{
- double lastTravelTime = Math.Max(lastSlider.LazyTravelTime / clockRate, min_delta_time);
- MinimumJumpTime = Math.Max(StrainTime - lastTravelTime, min_delta_time);
+ double lastTravelTime = Math.Max(lastSlider.LazyTravelTime / clockRate, MIN_DELTA_TIME);
+ MinimumJumpTime = Math.Max(StrainTime - lastTravelTime, MIN_DELTA_TIME);
//
// There are two types of slider-to-object patterns to consider in order to better approximate the real movement a player will take to jump between the hitobjects.
From 67cb4a2d02e47a2c8bfd493c4def3c19538057e8 Mon Sep 17 00:00:00 2001
From: StanR
Date: Mon, 15 Jul 2024 22:54:25 +0500
Subject: [PATCH 0141/1255] InspectCode
---
osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
index 39c7572c85..60bc53e7a1 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
@@ -172,6 +172,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
return Math.Sqrt(4 + rhythmComplexitySum * rhythm_multiplier) / 2; //produces multiplier that can be applied to strain. range [1, infinity) (not really though)
}
- private static double logistic(double x, double maxValue, double multiplier, double offset) => (maxValue / (1 + Math.Pow(Math.E, offset - multiplier * x)));
+ private static double logistic(double x, double maxValue, double multiplier, double offset) => (maxValue / (1 + Math.Pow(Math.E, offset - (multiplier * x))));
}
}
From fcc8e7be8a711c9356f0f6df725a1aad6aa86162 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Tue, 16 Jul 2024 12:09:09 +0900
Subject: [PATCH 0142/1255] Invert condition to reduce number of brain flips
required
---
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 7b54d0ae54..0aec0a9d4f 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -192,7 +192,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
// 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;
- if (!score.Mods.Any(h => h is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value))
+ if (score.Mods.All(h => h is not OsuModClassic cl || !cl.NoSliderHeadAccuracy.Value))
amountHitObjectsWithAccuracy += attributes.SliderCount;
if (amountHitObjectsWithAccuracy > 0)
From ced11e69496eb4cdd6790d8bcf4689420a24c7e1 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Tue, 16 Jul 2024 12:23:46 +0900
Subject: [PATCH 0143/1255] Even better readability
---
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 0aec0a9d4f..a4ebcd15a4 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -192,7 +192,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
// 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;
- if (score.Mods.All(h => h is not OsuModClassic cl || !cl.NoSliderHeadAccuracy.Value))
+ if (score.Mods.OfType().All(m => !m.NoSliderHeadAccuracy.Value))
amountHitObjectsWithAccuracy += attributes.SliderCount;
if (amountHitObjectsWithAccuracy > 0)
From ba501a8eb80cddea92a77cbedea8cfc4c8a540e0 Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Tue, 16 Jul 2024 11:30:52 +0200
Subject: [PATCH 0144/1255] fix addition bank toggle can be switched off with
selection
---
.../Components/EditorSelectionHandler.cs | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs
index 3a0428e7fc..df8c0176e1 100644
--- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs
@@ -150,9 +150,23 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (SelectionAdditionBankStates.Values.All(b => b.Value == TernaryState.False))
bindable.Value = TernaryState.True;
}
+ else
+ {
+ // Auto should never apply when there is a selection made.
+ if (bankName == HIT_BANK_AUTO)
+ break;
+
+ // Completely empty selections should be allowed in the case that none of the selected objects have any addition samples.
+ // This is also required to stop a bindable feedback loop when a HitObject has zero addition samples (and LINQ `All` below becomes true).
+ if (SelectedItems.SelectMany(enumerateAllSamples).All(h => h.All(o => o.Name == HitSampleInfo.HIT_NORMAL)))
+ break;
+
+ // Never remove a sample bank.
+ // These are basically radio buttons, not toggles.
+ if (SelectedItems.SelectMany(enumerateAllSamples).All(h => h.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName)))
+ bindable.Value = TernaryState.True;
+ }
- // Auto should never apply when there is a selection made.
- // Completely empty selections should be allowed in the case that none of the selected objects have any addition samples.
break;
case TernaryState.True:
From bae9625b0b4785b83124bfd38ccd8ff85d74947b Mon Sep 17 00:00:00 2001
From: StanR
Date: Fri, 19 Jul 2024 10:13:50 +0500
Subject: [PATCH 0145/1255] Make repetition nerf harsher, buff initial rhythm
ratio, small refactoring
---
.../Difficulty/Evaluators/RhythmEvaluator.cs | 28 ++++++++++---------
1 file changed, 15 insertions(+), 13 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
index 60bc53e7a1..9c48af80a7 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
@@ -32,7 +32,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
Deltas.Add(existingDelta == default ? delta : existingDelta);
}
- public double AverageDelta() => Math.Max(Deltas.Average(), OsuDifficultyHitObject.MIN_DELTA_TIME);
+ public double AverageDelta() => Deltas.Count > 0 ? Math.Max(Deltas.Average(), OsuDifficultyHitObject.MIN_DELTA_TIME) : 0;
+
+ public bool IsSimilarPolarity(Island other, double epsilon)
+ {
+ // consider islands to be of similar polarity only if they're having the same average delta (we don't want to consider 3 singletaps similar to a triple)
+ return Math.Abs(AverageDelta() - other.AverageDelta()) < epsilon &&
+ Deltas.Count % 2 == other.Deltas.Count % 2;
+ }
public override int GetHashCode()
{
@@ -53,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
}
private const int history_time_max = 5 * 1000; // 5 seconds of calculatingRhythmBonus max.
- private const double rhythm_multiplier = 1.14;
+ private const double rhythm_multiplier = 1.05;
private const int max_island_size = 7;
///
@@ -61,8 +68,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
///
public static double EvaluateDifficultyOf(DifficultyHitObject current)
{
- Dictionary islandCounts = new Dictionary();
-
if (current.BaseObject is Spinner)
return 0;
@@ -70,6 +75,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
var island = new Island();
var previousIsland = new Island();
+ Dictionary islandCounts = new Dictionary();
double startRatio = 0; // store the ratio of the current start of an island to buff for tighter rhythms
@@ -97,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
double prevDelta = prevObj.StrainTime;
double lastDelta = lastObj.StrainTime;
- double currRatio = 1.0 + 6.0 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses.
+ double currRatio = 1.0 + 10.0 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses.
double windowPenalty = Math.Min(1, Math.Max(0, Math.Abs(prevDelta - currDelta) - currObj.HitWindowGreat * 0.3) / (currObj.HitWindowGreat * 0.3));
@@ -125,23 +131,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
if (prevObj.BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle
effectiveRatio *= 0.25;
- if (previousIsland.Deltas.Count % 2 == island.Deltas.Count % 2) // repeated island polartiy (2 -> 4, 3 -> 5)
+ if (island.IsSimilarPolarity(previousIsland, deltaDifferenceEpsilon)) // repeated island polartiy (2 -> 4, 3 -> 5)
effectiveRatio *= 0.50;
if (lastDelta > prevDelta + deltaDifferenceEpsilon && prevDelta > currDelta + deltaDifferenceEpsilon) // previous increase happened a note ago, 1/1->1/2-1/4, dont want to buff this.
effectiveRatio *= 0.125;
- if (islandCounts.ContainsKey(island))
+ if (!islandCounts.TryAdd(island, 1))
{
islandCounts[island]++;
// repeated island (ex: triplet -> triplet)
- double power = Math.Max(0.75, logistic(island.AverageDelta(), 3, 0.15, 9));
- effectiveRatio *= Math.Pow(1.0 / islandCounts[island], power);
- }
- else
- {
- islandCounts.Add(island, 1);
+ double power = logistic(island.AverageDelta(), 4, 0.165, 10);
+ effectiveRatio *= Math.Min(1.0 / islandCounts[island], Math.Pow(1.0 / islandCounts[island], power));
}
rhythmComplexitySum += Math.Sqrt(effectiveRatio * startRatio) * currHistoricalDecay;
From c1532bcb570ae36d7a73d5b42e451b8c7325b111 Mon Sep 17 00:00:00 2001
From: StanR
Date: Fri, 19 Jul 2024 11:01:42 +0500
Subject: [PATCH 0146/1255] Reduce base ratio a bit
---
osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
index 9c48af80a7..ba77bc6a61 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
@@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
double prevDelta = prevObj.StrainTime;
double lastDelta = lastObj.StrainTime;
- double currRatio = 1.0 + 10.0 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses.
+ double currRatio = 1.0 + 8.0 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses.
double windowPenalty = Math.Min(1, Math.Max(0, Math.Abs(prevDelta - currDelta) - currObj.HitWindowGreat * 0.3) / (currObj.HitWindowGreat * 0.3));
From 1d4d8063622dbc552fd695bfb6561fbe14b63e57 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Tue, 23 Jul 2024 12:19:45 +0200
Subject: [PATCH 0147/1255] Fix `WidescreenStoryboard` breakage after moving
out of `BeatmapInfo`
---
osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 4 ++--
.../Beatmaps/Formats/LegacyStoryboardDecoder.cs | 15 +++++++++++++++
osu.Game/Beatmaps/WorkingBeatmap.cs | 7 ++++++-
3 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index f873eaf535..bd81892d95 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -80,7 +80,7 @@ namespace osu.Game.Beatmaps.Formats
this.beatmap = beatmap;
this.beatmap.BeatmapInfo.BeatmapVersion = FormatVersion;
- applyLegacyDefaults(this.beatmap);
+ ApplyLegacyDefaults(this.beatmap);
base.ParseStreamInto(stream, beatmap);
@@ -186,7 +186,7 @@ namespace osu.Game.Beatmaps.Formats
/// This method's intention is to restore those legacy defaults.
/// See also: https://osu.ppy.sh/wiki/en/Client/File_formats/Osu_%28file_format%29
///
- private static void applyLegacyDefaults(Beatmap beatmap)
+ internal static void ApplyLegacyDefaults(Beatmap beatmap)
{
beatmap.WidescreenStoryboard = false;
beatmap.SamplesMatchPlaybackRate = false;
diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
index 2f9a256d31..dc96c2ff82 100644
--- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
@@ -37,6 +37,17 @@ namespace osu.Game.Beatmaps.Formats
SetFallbackDecoder(() => new LegacyStoryboardDecoder());
}
+ protected override Storyboard CreateTemplateObject()
+ {
+ var sb = base.CreateTemplateObject();
+
+ var beatmap = new Beatmap();
+ LegacyBeatmapDecoder.ApplyLegacyDefaults(beatmap);
+ sb.Beatmap = beatmap;
+
+ return sb;
+ }
+
protected override void ParseStreamInto(LineBufferedReader stream, Storyboard storyboard)
{
this.storyboard = storyboard;
@@ -72,6 +83,10 @@ namespace osu.Game.Beatmaps.Formats
case "UseSkinSprites":
storyboard.UseSkinSprites = pair.Value == "1";
break;
+
+ case @"WidescreenStoryboard":
+ storyboard.Beatmap.WidescreenStoryboard = Parsing.ParseInt(pair.Value) == 1;
+ break;
}
}
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index 25159996f3..8b0d3dda6a 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -62,7 +62,12 @@ namespace osu.Game.Beatmaps
#region Resource getters
protected virtual Waveform GetWaveform() => new Waveform(null);
- protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo };
+
+ protected virtual Storyboard GetStoryboard() => new Storyboard
+ {
+ BeatmapInfo = BeatmapInfo,
+ Beatmap = Beatmap,
+ };
protected abstract IBeatmap GetBeatmap();
public abstract Texture GetBackground();
From 3acd00b9b703a0a5a7445192eabac94bfe385594 Mon Sep 17 00:00:00 2001
From: StanR
Date: Sat, 10 Aug 2024 22:30:24 +0500
Subject: [PATCH 0148/1255] Tweak some values
---
.../Difficulty/Evaluators/RhythmEvaluator.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
index ba77bc6a61..d2f58925cd 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
@@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
}
private const int history_time_max = 5 * 1000; // 5 seconds of calculatingRhythmBonus max.
- private const double rhythm_multiplier = 1.05;
+ private const double rhythm_multiplier = 1.1;
private const int max_island_size = 7;
///
@@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
double prevDelta = prevObj.StrainTime;
double lastDelta = lastObj.StrainTime;
- double currRatio = 1.0 + 8.0 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses.
+ double currRatio = 1.0 + 8.4 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses.
double windowPenalty = Math.Min(1, Math.Max(0, Math.Abs(prevDelta - currDelta) - currObj.HitWindowGreat * 0.3) / (currObj.HitWindowGreat * 0.3));
@@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
effectiveRatio *= 0.25;
if (island.IsSimilarPolarity(previousIsland, deltaDifferenceEpsilon)) // repeated island polartiy (2 -> 4, 3 -> 5)
- effectiveRatio *= 0.50;
+ effectiveRatio *= 0.35;
if (lastDelta > prevDelta + deltaDifferenceEpsilon && prevDelta > currDelta + deltaDifferenceEpsilon) // previous increase happened a note ago, 1/1->1/2-1/4, dont want to buff this.
effectiveRatio *= 0.125;
From 2abcdd006466fdb291d9b727fb85b34a505155f7 Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Tue, 13 Aug 2024 22:05:01 +0200
Subject: [PATCH 0149/1255] Redesign sample bank toggles
---
.../Rulesets/Edit/ExpandableSpriteText.cs | 34 ++++++++
osu.Game/Rulesets/Edit/HitObjectComposer.cs | 83 ++++++++++++++-----
.../DoubleDrawableTernaryButton.cs | 78 +++++++++++++++++
.../TernaryButtons/DrawableTernaryButton.cs | 10 +--
.../Components/ComposeBlueprintContainer.cs | 27 ++----
5 files changed, 185 insertions(+), 47 deletions(-)
create mode 100644 osu.Game/Rulesets/Edit/ExpandableSpriteText.cs
create mode 100644 osu.Game/Screens/Edit/Components/TernaryButtons/DoubleDrawableTernaryButton.cs
diff --git a/osu.Game/Rulesets/Edit/ExpandableSpriteText.cs b/osu.Game/Rulesets/Edit/ExpandableSpriteText.cs
new file mode 100644
index 0000000000..b45fce1d22
--- /dev/null
+++ b/osu.Game/Rulesets/Edit/ExpandableSpriteText.cs
@@ -0,0 +1,34 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+
+namespace osu.Game.Rulesets.Edit
+{
+ internal partial class ExpandableSpriteText : OsuSpriteText, IExpandable
+ {
+ public BindableBool Expanded { get; } = new BindableBool();
+
+ [Resolved(canBeNull: true)]
+ private IExpandingContainer? expandingContainer { get; set; }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ expandingContainer?.Expanded.BindValueChanged(containerExpanded =>
+ {
+ Expanded.Value = containerExpanded.NewValue;
+ }, true);
+
+ Expanded.BindValueChanged(expanded =>
+ {
+ this.FadeTo(expanded.NewValue ? 1 : 0, 150, Easing.OutQuint);
+ }, true);
+ }
+ }
+}
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index 1319fb3352..e5b118004c 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -18,6 +18,7 @@ using osu.Framework.Input.Events;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
+using osu.Game.Graphics;
using osu.Game.Overlays;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Edit.Tools;
@@ -85,7 +86,6 @@ namespace osu.Game.Rulesets.Edit
private EditorRadioButtonCollection toolboxCollection;
private FillFlowContainer togglesCollection;
private FillFlowContainer sampleBankTogglesCollection;
- private FillFlowContainer sampleAdditionBankTogglesCollection;
private IBindable hasTiming;
private Bindable autoSeekOnPlacement;
@@ -176,25 +176,55 @@ namespace osu.Game.Rulesets.Edit
Spacing = new Vector2(0, 5),
},
},
- new EditorToolboxGroup("bank (Shift-Q~R)")
+ new EditorToolboxGroup("bank (Shift/Alt-Q~R)")
{
- Child = sampleBankTogglesCollection = new FillFlowContainer
+ Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
- },
- },
- new EditorToolboxGroup("additions (Alt-Q~R)")
- {
- Child = sampleAdditionBankTogglesCollection = new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Direction = FillDirection.Vertical,
- Spacing = new Vector2(0, 5),
- },
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ new ExpandableSpriteText
+ {
+ Text = "Normal",
+ AlwaysPresent = true,
+ AllowMultiline = false,
+ RelativePositionAxes = Axes.X,
+ X = 0.25f,
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopLeft,
+ Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 17),
+ },
+ new ExpandableSpriteText
+ {
+ Text = "Addition",
+ AlwaysPresent = true,
+ AllowMultiline = false,
+ RelativePositionAxes = Axes.X,
+ X = 0.75f,
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopLeft,
+ Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 17),
+ },
+ }
+ },
+ sampleBankTogglesCollection = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(0, 5),
+ },
+ }
+ }
},
}
},
@@ -240,8 +270,7 @@ namespace osu.Game.Rulesets.Edit
TernaryStates = CreateTernaryButtons().ToArray();
togglesCollection.AddRange(TernaryStates.Select(b => new DrawableTernaryButton(b)));
- sampleBankTogglesCollection.AddRange(BlueprintContainer.SampleBankTernaryStates.Select(b => new DrawableTernaryButton(b)));
- sampleAdditionBankTogglesCollection.AddRange(BlueprintContainer.SampleAdditionBankTernaryStates.Select(b => new DrawableTernaryButton(b)));
+ sampleBankTogglesCollection.AddRange(BlueprintContainer.SampleBankTernaryStates.Zip(BlueprintContainer.SampleAdditionBankTernaryStates).Select(b => new DoubleDrawableTernaryButton(b.First, b.Second)));
setSelectTool();
@@ -397,7 +426,7 @@ namespace osu.Game.Rulesets.Edit
attemptToggle(rightIndex, sampleBankTogglesCollection);
if (e.AltPressed)
- attemptToggle(rightIndex, sampleAdditionBankTogglesCollection);
+ attemptToggle(rightIndex, sampleBankTogglesCollection, true);
}
else
attemptToggle(rightIndex, togglesCollection);
@@ -405,14 +434,26 @@ namespace osu.Game.Rulesets.Edit
return handled || base.OnKeyDown(e);
- void attemptToggle(int index, FillFlowContainer collection)
+ void attemptToggle(int index, FillFlowContainer collection, bool second = false)
{
var item = collection.ElementAtOrDefault(index);
- if (item is DrawableTernaryButton button)
+ switch (item)
{
- button.Button.Toggle();
- handled = true;
+ case DrawableTernaryButton button:
+ button.Button.Toggle();
+ handled = true;
+ break;
+
+ case DoubleDrawableTernaryButton doubleButton:
+ {
+ if (second)
+ doubleButton.Button2.Toggle();
+ else
+ doubleButton.Button1.Toggle();
+ handled = true;
+ break;
+ }
}
}
}
diff --git a/osu.Game/Screens/Edit/Components/TernaryButtons/DoubleDrawableTernaryButton.cs b/osu.Game/Screens/Edit/Components/TernaryButtons/DoubleDrawableTernaryButton.cs
new file mode 100644
index 0000000000..44b5d10b9e
--- /dev/null
+++ b/osu.Game/Screens/Edit/Components/TernaryButtons/DoubleDrawableTernaryButton.cs
@@ -0,0 +1,78 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Graphics.Sprites;
+
+namespace osu.Game.Screens.Edit.Components.TernaryButtons
+{
+ public partial class DoubleDrawableTernaryButton : CompositeDrawable
+ {
+ public readonly TernaryButton Button1;
+ public readonly TernaryButton Button2;
+
+ public DoubleDrawableTernaryButton(TernaryButton button1, TernaryButton button2)
+ {
+ Button1 = button1;
+ Button2 = button2;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+ Masking = true;
+ CornerRadius = 5;
+
+ InternalChildren = new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Width = 0.5f,
+ Padding = new MarginPadding { Right = 1 },
+ Child = new InlineDrawableTernaryButton(Button1),
+ },
+ new Container
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Width = 0.5f,
+ Padding = new MarginPadding { Left = 1 },
+ Child = new InlineDrawableTernaryButton(Button2),
+ },
+ };
+ }
+ }
+
+ public partial class InlineDrawableTernaryButton : DrawableTernaryButton
+ {
+ public InlineDrawableTernaryButton(TernaryButton button)
+ : base(button)
+ {
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Content.Masking = false;
+ Content.CornerRadius = 0;
+ Icon.X = 4.5f;
+ }
+
+ protected override SpriteText CreateText() => new OsuSpriteText
+ {
+ Depth = -1,
+ Origin = Anchor.CentreLeft,
+ Anchor = Anchor.CentreLeft,
+ X = 31f
+ };
+ }
+}
diff --git a/osu.Game/Screens/Edit/Components/TernaryButtons/DrawableTernaryButton.cs b/osu.Game/Screens/Edit/Components/TernaryButtons/DrawableTernaryButton.cs
index 95d5dd36d8..d3fd701406 100644
--- a/osu.Game/Screens/Edit/Components/TernaryButtons/DrawableTernaryButton.cs
+++ b/osu.Game/Screens/Edit/Components/TernaryButtons/DrawableTernaryButton.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons
private Color4 selectedBackgroundColour;
private Color4 selectedIconColour;
- private Drawable icon = null!;
+ protected Drawable Icon = null!;
public readonly TernaryButton Button;
@@ -43,7 +43,7 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons
defaultIconColour = defaultBackgroundColour.Darken(0.5f);
selectedIconColour = selectedBackgroundColour.Lighten(0.5f);
- Add(icon = (Button.CreateIcon?.Invoke() ?? new Circle()).With(b =>
+ Add(Icon = (Button.CreateIcon?.Invoke() ?? new Circle()).With(b =>
{
b.Blending = BlendingParameters.Additive;
b.Anchor = Anchor.CentreLeft;
@@ -75,17 +75,17 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons
switch (Button.Bindable.Value)
{
case TernaryState.Indeterminate:
- icon.Colour = selectedIconColour.Darken(0.5f);
+ Icon.Colour = selectedIconColour.Darken(0.5f);
BackgroundColour = selectedBackgroundColour.Darken(0.5f);
break;
case TernaryState.False:
- icon.Colour = defaultIconColour;
+ Icon.Colour = defaultIconColour;
BackgroundColour = defaultBackgroundColour;
break;
case TernaryState.True:
- icon.Colour = selectedIconColour;
+ Icon.Colour = selectedIconColour;
BackgroundColour = selectedBackgroundColour;
break;
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
index 4c1610902e..9f2fff9ca3 100644
--- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
@@ -259,28 +259,13 @@ namespace osu.Game.Screens.Edit.Compose.Components
private Drawable getIconForBank(string sampleName)
{
- return new Container
+ return new OsuSpriteText
{
- Size = new Vector2(30, 20),
- Children = new Drawable[]
- {
- new SpriteIcon
- {
- Size = new Vector2(8),
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- Icon = FontAwesome.Solid.VolumeOff
- },
- new OsuSpriteText
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- X = 10,
- Y = -1,
- Font = OsuFont.Default.With(weight: FontWeight.Bold, size: 20),
- Text = $"{char.ToUpperInvariant(sampleName.First())}"
- }
- }
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Y = -1,
+ Font = OsuFont.Default.With(weight: FontWeight.Bold, size: 20),
+ Text = $"{char.ToUpperInvariant(sampleName.First())}"
};
}
From 10f2ac64905784c22db6d36d8a9f7ae571e8164d Mon Sep 17 00:00:00 2001
From: OliBomby
Date: Tue, 13 Aug 2024 22:13:17 +0200
Subject: [PATCH 0150/1255] fix anti-aliased white leaking out of the button
---
.../Components/TernaryButtons/DoubleDrawableTernaryButton.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/osu.Game/Screens/Edit/Components/TernaryButtons/DoubleDrawableTernaryButton.cs b/osu.Game/Screens/Edit/Components/TernaryButtons/DoubleDrawableTernaryButton.cs
index 44b5d10b9e..2cf3b760f7 100644
--- a/osu.Game/Screens/Edit/Components/TernaryButtons/DoubleDrawableTernaryButton.cs
+++ b/osu.Game/Screens/Edit/Components/TernaryButtons/DoubleDrawableTernaryButton.cs
@@ -5,7 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
-using osu.Game.Graphics.Sprites;
+using osu.Game.Rulesets.Edit;
namespace osu.Game.Screens.Edit.Components.TernaryButtons
{
@@ -67,7 +67,7 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons
Icon.X = 4.5f;
}
- protected override SpriteText CreateText() => new OsuSpriteText
+ protected override SpriteText CreateText() => new ExpandableSpriteText
{
Depth = -1,
Origin = Anchor.CentreLeft,
From 55e9bb6a5da9ccbfed949890b492f2620e5dcfab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Mon, 19 Aug 2024 10:59:47 +0200
Subject: [PATCH 0151/1255] Privatise setter
---
.../Edit/Components/TernaryButtons/DrawableTernaryButton.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game/Screens/Edit/Components/TernaryButtons/DrawableTernaryButton.cs b/osu.Game/Screens/Edit/Components/TernaryButtons/DrawableTernaryButton.cs
index d3fd701406..9e528aa21b 100644
--- a/osu.Game/Screens/Edit/Components/TernaryButtons/DrawableTernaryButton.cs
+++ b/osu.Game/Screens/Edit/Components/TernaryButtons/DrawableTernaryButton.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons
private Color4 selectedBackgroundColour;
private Color4 selectedIconColour;
- protected Drawable Icon = null!;
+ protected Drawable Icon { get; private set; } = null!;
public readonly TernaryButton Button;
From 32821be0462a56f7b64b037f8e6ab291e584205e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Mon, 19 Aug 2024 11:07:34 +0200
Subject: [PATCH 0152/1255] Make "double ternary button" specific to samples
We can generalise *when* there is the need to generalise. So far the
generalisation only looked like *obfuscation*.
---
osu.Game/Rulesets/Edit/HitObjectComposer.cs | 42 +++++---------
...ryButton.cs => SampleBankTernaryButton.cs} | 58 +++++++++----------
2 files changed, 44 insertions(+), 56 deletions(-)
rename osu.Game/Screens/Edit/Components/TernaryButtons/{DoubleDrawableTernaryButton.cs => SampleBankTernaryButton.cs} (52%)
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index e5b118004c..f9b218a429 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -270,7 +270,7 @@ namespace osu.Game.Rulesets.Edit
TernaryStates = CreateTernaryButtons().ToArray();
togglesCollection.AddRange(TernaryStates.Select(b => new DrawableTernaryButton(b)));
- sampleBankTogglesCollection.AddRange(BlueprintContainer.SampleBankTernaryStates.Zip(BlueprintContainer.SampleAdditionBankTernaryStates).Select(b => new DoubleDrawableTernaryButton(b.First, b.Second)));
+ sampleBankTogglesCollection.AddRange(BlueprintContainer.SampleBankTernaryStates.Zip(BlueprintContainer.SampleAdditionBankTernaryStates).Select(b => new SampleBankTernaryButton(b.First, b.Second)));
setSelectTool();
@@ -422,40 +422,28 @@ namespace osu.Game.Rulesets.Edit
{
if (e.ShiftPressed || e.AltPressed)
{
- if (e.ShiftPressed)
- attemptToggle(rightIndex, sampleBankTogglesCollection);
+ if (sampleBankTogglesCollection.ElementAtOrDefault(rightIndex) is SampleBankTernaryButton sampleBankTernaryButton)
+ {
+ if (e.ShiftPressed)
+ sampleBankTernaryButton.NormalButton.Toggle();
- if (e.AltPressed)
- attemptToggle(rightIndex, sampleBankTogglesCollection, true);
+ if (e.AltPressed)
+ sampleBankTernaryButton.AdditionsButton.Toggle();
+
+ return true;
+ }
}
else
- attemptToggle(rightIndex, togglesCollection);
- }
-
- return handled || base.OnKeyDown(e);
-
- void attemptToggle(int index, FillFlowContainer collection, bool second = false)
- {
- var item = collection.ElementAtOrDefault(index);
-
- switch (item)
{
- case DrawableTernaryButton button:
- button.Button.Toggle();
- handled = true;
- break;
-
- case DoubleDrawableTernaryButton doubleButton:
+ if (togglesCollection.ElementAtOrDefault(rightIndex) is DrawableTernaryButton button)
{
- if (second)
- doubleButton.Button2.Toggle();
- else
- doubleButton.Button1.Toggle();
- handled = true;
- break;
+ button.Button.Toggle();
+ return true;
}
}
}
+
+ return base.OnKeyDown(e);
}
private bool checkLeftToggleFromKey(Key key, out int index)
diff --git a/osu.Game/Screens/Edit/Components/TernaryButtons/DoubleDrawableTernaryButton.cs b/osu.Game/Screens/Edit/Components/TernaryButtons/SampleBankTernaryButton.cs
similarity index 52%
rename from osu.Game/Screens/Edit/Components/TernaryButtons/DoubleDrawableTernaryButton.cs
rename to osu.Game/Screens/Edit/Components/TernaryButtons/SampleBankTernaryButton.cs
index 2cf3b760f7..33eb2ac0b4 100644
--- a/osu.Game/Screens/Edit/Components/TernaryButtons/DoubleDrawableTernaryButton.cs
+++ b/osu.Game/Screens/Edit/Components/TernaryButtons/SampleBankTernaryButton.cs
@@ -9,15 +9,15 @@ using osu.Game.Rulesets.Edit;
namespace osu.Game.Screens.Edit.Components.TernaryButtons
{
- public partial class DoubleDrawableTernaryButton : CompositeDrawable
+ public partial class SampleBankTernaryButton : CompositeDrawable
{
- public readonly TernaryButton Button1;
- public readonly TernaryButton Button2;
+ public readonly TernaryButton NormalButton;
+ public readonly TernaryButton AdditionsButton;
- public DoubleDrawableTernaryButton(TernaryButton button1, TernaryButton button2)
+ public SampleBankTernaryButton(TernaryButton normalButton, TernaryButton additionsButton)
{
- Button1 = button1;
- Button2 = button2;
+ NormalButton = normalButton;
+ AdditionsButton = additionsButton;
}
[BackgroundDependencyLoader]
@@ -36,7 +36,7 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons
AutoSizeAxes = Axes.Y,
Width = 0.5f,
Padding = new MarginPadding { Right = 1 },
- Child = new InlineDrawableTernaryButton(Button1),
+ Child = new InlineDrawableTernaryButton(NormalButton),
},
new Container
{
@@ -46,33 +46,33 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons
AutoSizeAxes = Axes.Y,
Width = 0.5f,
Padding = new MarginPadding { Left = 1 },
- Child = new InlineDrawableTernaryButton(Button2),
+ Child = new InlineDrawableTernaryButton(AdditionsButton),
},
};
}
- }
- public partial class InlineDrawableTernaryButton : DrawableTernaryButton
- {
- public InlineDrawableTernaryButton(TernaryButton button)
- : base(button)
+ private partial class InlineDrawableTernaryButton : DrawableTernaryButton
{
+ public InlineDrawableTernaryButton(TernaryButton button)
+ : base(button)
+ {
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Content.Masking = false;
+ Content.CornerRadius = 0;
+ Icon.X = 4.5f;
+ }
+
+ protected override SpriteText CreateText() => new ExpandableSpriteText
+ {
+ Depth = -1,
+ Origin = Anchor.CentreLeft,
+ Anchor = Anchor.CentreLeft,
+ X = 31f
+ };
}
-
- [BackgroundDependencyLoader]
- private void load()
- {
- Content.Masking = false;
- Content.CornerRadius = 0;
- Icon.X = 4.5f;
- }
-
- protected override SpriteText CreateText() => new ExpandableSpriteText
- {
- Depth = -1,
- Origin = Anchor.CentreLeft,
- Anchor = Anchor.CentreLeft,
- X = 31f
- };
}
}
From 588a36cba39293497ec46c1106cd33525a87999c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Mon, 19 Aug 2024 14:58:12 +0200
Subject: [PATCH 0153/1255] Remove unused variable
---
osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 --
1 file changed, 2 deletions(-)
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index f9b218a429..085740d3eb 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -404,8 +404,6 @@ namespace osu.Game.Rulesets.Edit
if (e.ControlPressed || e.SuperPressed)
return false;
- bool handled = false;
-
if (checkLeftToggleFromKey(e.Key, out int leftIndex))
{
var item = toolboxCollection.Items.ElementAtOrDefault(leftIndex);
From f1adc6f98cc48024a0bb35811955fb5653c3fdf6 Mon Sep 17 00:00:00 2001
From: StanR
Date: Thu, 22 Aug 2024 15:59:13 +0500
Subject: [PATCH 0154/1255] Don't cap max island size, make repetition nerf
more lenient on high bpm, adjust balancing
---
.../Difficulty/Evaluators/RhythmEvaluator.cs | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
index d2f58925cd..9ffb4fc3d7 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
@@ -60,8 +60,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
}
private const int history_time_max = 5 * 1000; // 5 seconds of calculatingRhythmBonus max.
- private const double rhythm_multiplier = 1.1;
- private const int max_island_size = 7;
+ private const double rhythm_multiplier = 1.05;
///
/// Calculates a rhythm multiplier for the difficulty of the tap associated with historic data of the current .
@@ -103,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
double prevDelta = prevObj.StrainTime;
double lastDelta = lastObj.StrainTime;
- double currRatio = 1.0 + 8.4 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses.
+ double currRatio = 1.0 + 8.8 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses.
double windowPenalty = Math.Min(1, Math.Max(0, Math.Abs(prevDelta - currDelta) - currObj.HitWindowGreat * 0.3) / (currObj.HitWindowGreat * 0.3));
@@ -117,11 +116,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
{
if (!(Math.Abs(prevDelta - currDelta) > deltaDifferenceEpsilon))
{
- if (island.Deltas.Count < max_island_size)
- {
- // island is still progressing
- island.AddDelta((int)currDelta, deltaDifferenceEpsilon);
- }
+ // island is still progressing
+ island.AddDelta((int)currDelta, deltaDifferenceEpsilon);
}
else
{
@@ -143,7 +139,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
// repeated island (ex: triplet -> triplet)
double power = logistic(island.AverageDelta(), 4, 0.165, 10);
- effectiveRatio *= Math.Min(1.0 / islandCounts[island], Math.Pow(1.0 / islandCounts[island], power));
+ effectiveRatio *= Math.Min(2.0 / islandCounts[island], Math.Pow(1.0 / islandCounts[island], power));
}
rhythmComplexitySum += Math.Sqrt(effectiveRatio * startRatio) * currHistoricalDecay;
From ce8286d299f11a9e7c96baf67ca38d5a143239d5 Mon Sep 17 00:00:00 2001
From: StanR
Date: Sat, 24 Aug 2024 04:37:58 +0500
Subject: [PATCH 0155/1255] Scale difficulty with doubletapness, make
kicksliders not reduce the difficulty of the next object, adjust balancing
---
.../Difficulty/Evaluators/RhythmEvaluator.cs | 28 +++++++++++++------
.../Difficulty/Evaluators/SpeedEvaluator.cs | 14 +---------
.../Preprocessing/OsuDifficultyHitObject.cs | 18 ++++++++++++
3 files changed, 38 insertions(+), 22 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
index 9ffb4fc3d7..22b330bcac 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
@@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
}
private const int history_time_max = 5 * 1000; // 5 seconds of calculatingRhythmBonus max.
- private const double rhythm_multiplier = 1.05;
+ private const double rhythm_multiplier = 0.95;
///
/// Calculates a rhythm multiplier for the difficulty of the tap associated with historic data of the current .
@@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
double prevDelta = prevObj.StrainTime;
double lastDelta = lastObj.StrainTime;
- double currRatio = 1.0 + 8.8 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses.
+ double currRatio = 1.0 + 9.8 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses.
double windowPenalty = Math.Min(1, Math.Max(0, Math.Abs(prevDelta - currDelta) - currObj.HitWindowGreat * 0.3) / (currObj.HitWindowGreat * 0.3));
@@ -121,16 +121,22 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
}
else
{
- if (currObj.BaseObject is Slider) // bpm change is into slider, this is easy acc window
+ // bpm change is into slider, this is easy acc window
+ if (currObj.BaseObject is Slider)
effectiveRatio *= 0.125;
- if (prevObj.BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle
- effectiveRatio *= 0.25;
+ // bpm change was from a slider, this is easier typically than circle -> circle
+ // unintentional side effect is that bursts with kicksliders at the ends might have lower difficulty than bursts without sliders
+ // therefore we're checking for quick sliders and don't lower the difficulty for them since they don't really make tapping easier (no time to adjust)
+ if (prevObj.BaseObject is Slider && prevObj.TravelTime > prevDelta * 1.5)
+ effectiveRatio *= 0.15;
- if (island.IsSimilarPolarity(previousIsland, deltaDifferenceEpsilon)) // repeated island polartiy (2 -> 4, 3 -> 5)
- effectiveRatio *= 0.35;
+ // repeated island polartiy (2 -> 4, 3 -> 5)
+ if (island.IsSimilarPolarity(previousIsland, deltaDifferenceEpsilon))
+ effectiveRatio *= 0.3;
- if (lastDelta > prevDelta + deltaDifferenceEpsilon && prevDelta > currDelta + deltaDifferenceEpsilon) // previous increase happened a note ago, 1/1->1/2-1/4, dont want to buff this.
+ // previous increase happened a note ago, 1/1->1/2-1/4, dont want to buff this.
+ if (lastDelta > prevDelta + deltaDifferenceEpsilon && prevDelta > currDelta + deltaDifferenceEpsilon)
effectiveRatio *= 0.125;
if (!islandCounts.TryAdd(island, 1))
@@ -142,6 +148,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
effectiveRatio *= Math.Min(2.0 / islandCounts[island], Math.Pow(1.0 / islandCounts[island], power));
}
+ // scale down the difficulty if the object is doubletappable
+ double doubletapness = prevObj.GetDoubletapness((OsuDifficultyHitObject?)prevObj.Next(0));
+ effectiveRatio *= 1 - doubletapness * 0.75;
+
rhythmComplexitySum += Math.Sqrt(effectiveRatio * startRatio) * currHistoricalDecay;
startRatio = effectiveRatio;
@@ -167,7 +177,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
prevObj = currObj;
}
- return Math.Sqrt(4 + rhythmComplexitySum * rhythm_multiplier) / 2; //produces multiplier that can be applied to strain. range [1, infinity) (not really though)
+ return Math.Sqrt(4 + rhythmComplexitySum * rhythm_multiplier) / 2.0; //produces multiplier that can be applied to strain. range [1, infinity) (not really though)
}
private static double logistic(double x, double maxValue, double multiplier, double offset) => (maxValue / (1 + Math.Pow(Math.E, offset - (multiplier * x))));
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs
index 2df383aaa8..3a264188f6 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs
@@ -30,21 +30,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
// derive strainTime for calculation
var osuCurrObj = (OsuDifficultyHitObject)current;
var osuPrevObj = current.Index > 0 ? (OsuDifficultyHitObject)current.Previous(0) : null;
- var osuNextObj = (OsuDifficultyHitObject?)current.Next(0);
double strainTime = osuCurrObj.StrainTime;
- double doubletapness = 1;
-
- // Nerf doubletappable doubles.
- if (osuNextObj != null)
- {
- double currDeltaTime = Math.Max(1, osuCurrObj.DeltaTime);
- double nextDeltaTime = Math.Max(1, osuNextObj.DeltaTime);
- double deltaDifference = Math.Abs(nextDeltaTime - currDeltaTime);
- double speedRatio = currDeltaTime / Math.Max(currDeltaTime, deltaDifference);
- double windowRatio = Math.Pow(Math.Min(1, currDeltaTime / osuCurrObj.HitWindowGreat), 2);
- doubletapness = Math.Pow(speedRatio, 1 - windowRatio);
- }
+ double doubletapness = 1.0 - osuCurrObj.GetDoubletapness((OsuDifficultyHitObject?)osuCurrObj.Next(0));
// Cap deltatime to the OD 300 hitwindow.
// 0.93 is derived from making sure 260bpm OD8 streams aren't nerfed harshly, whilst 0.92 limits the effect of the cap.
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
index 95535274c1..3eaf500ad7 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
@@ -137,6 +137,24 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
return Math.Clamp((time - fadeInStartTime) / fadeInDuration, 0.0, 1.0);
}
+ ///
+ /// Returns how possible is it to doubletap this object together with the next one and get perfect judgement in range from 0 to 1
+ ///
+ public double GetDoubletapness(OsuDifficultyHitObject? osuNextObj)
+ {
+ if (osuNextObj != null)
+ {
+ double currDeltaTime = Math.Max(1, DeltaTime);
+ double nextDeltaTime = Math.Max(1, osuNextObj.DeltaTime);
+ double deltaDifference = Math.Abs(nextDeltaTime - currDeltaTime);
+ double speedRatio = currDeltaTime / Math.Max(currDeltaTime, deltaDifference);
+ double windowRatio = Math.Pow(Math.Min(1, currDeltaTime / HitWindowGreat), 2);
+ return 1.0 - Math.Pow(speedRatio, 1 - windowRatio);
+ }
+
+ return 0;
+ }
+
private void setDistances(double clockRate)
{
if (BaseObject is Slider currentSlider)
From ed45c947fca29fd07439e1004ce17ec3069f9021 Mon Sep 17 00:00:00 2001
From: StanR
Date: Tue, 27 Aug 2024 15:50:08 +0500
Subject: [PATCH 0156/1255] Adjust max history by clockrate to make rhythm
calculation more consistent between rates
---
.../Difficulty/Evaluators/RhythmEvaluator.cs | 28 ++++++++++---------
.../Difficulty/OsuDifficultyCalculator.cs | 2 +-
.../Difficulty/Skills/Speed.cs | 6 ++--
3 files changed, 20 insertions(+), 16 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
index 22b330bcac..26f50efc72 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
@@ -60,12 +60,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
}
private const int history_time_max = 5 * 1000; // 5 seconds of calculatingRhythmBonus max.
- private const double rhythm_multiplier = 0.95;
+ private const int history_objects_max = 32;
+ private const double rhythm_multiplier = 1.25;
///
/// Calculates a rhythm multiplier for the difficulty of the tap associated with historic data of the current .
///
- public static double EvaluateDifficultyOf(DifficultyHitObject current)
+ public static double EvaluateDifficultyOf(DifficultyHitObject current, double clockRate)
{
if (current.BaseObject is Spinner)
return 0;
@@ -76,15 +77,18 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
var previousIsland = new Island();
Dictionary islandCounts = new Dictionary();
+ int historyTimeMaxAdjusted = (int)Math.Ceiling(history_time_max / clockRate);
+ int historyObjectsMaxAdjusted = (int)Math.Ceiling(history_objects_max / clockRate);
+
double startRatio = 0; // store the ratio of the current start of an island to buff for tighter rhythms
bool firstDeltaSwitch = false;
- int historicalNoteCount = Math.Min(current.Index, 32);
+ int historicalNoteCount = Math.Min(current.Index, historyObjectsMaxAdjusted);
int rhythmStart = 0;
- while (rhythmStart < historicalNoteCount - 2 && current.StartTime - current.Previous(rhythmStart).StartTime < history_time_max)
+ while (rhythmStart < historicalNoteCount - 2 && current.StartTime - current.Previous(rhythmStart).StartTime < historyTimeMaxAdjusted)
rhythmStart++;
OsuDifficultyHitObject prevObj = (OsuDifficultyHitObject)current.Previous(rhythmStart);
@@ -94,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
{
OsuDifficultyHitObject currObj = (OsuDifficultyHitObject)current.Previous(i - 1);
- double currHistoricalDecay = (history_time_max - (current.StartTime - currObj.StartTime)) / history_time_max; // scales note 0 to 1 from history to now
+ double currHistoricalDecay = (historyTimeMaxAdjusted - (current.StartTime - currObj.StartTime)) / historyTimeMaxAdjusted; // scales note 0 to 1 from history to now
currHistoricalDecay = Math.Min((double)(historicalNoteCount - i) / historicalNoteCount, currHistoricalDecay); // either we're limited by time or limited by object count.
@@ -102,16 +106,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
double prevDelta = prevObj.StrainTime;
double lastDelta = lastObj.StrainTime;
- double currRatio = 1.0 + 9.8 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses.
-
- double windowPenalty = Math.Min(1, Math.Max(0, Math.Abs(prevDelta - currDelta) - currObj.HitWindowGreat * 0.3) / (currObj.HitWindowGreat * 0.3));
-
- windowPenalty = Math.Min(1, windowPenalty);
-
- double effectiveRatio = windowPenalty * currRatio;
+ double currRatio = 1.0 + 7.27 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses.
double deltaDifferenceEpsilon = currObj.HitWindowGreat * 0.3;
+ double windowPenalty = Math.Min(1, Math.Max(0, Math.Abs(prevDelta - currDelta) - deltaDifferenceEpsilon) / deltaDifferenceEpsilon);
+
+ double effectiveRatio = windowPenalty * currRatio;
+
if (firstDeltaSwitch)
{
if (!(Math.Abs(prevDelta - currDelta) > deltaDifferenceEpsilon))
@@ -145,7 +147,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
// repeated island (ex: triplet -> triplet)
double power = logistic(island.AverageDelta(), 4, 0.165, 10);
- effectiveRatio *= Math.Min(2.0 / islandCounts[island], Math.Pow(1.0 / islandCounts[island], power));
+ effectiveRatio *= Math.Min(3.0 / islandCounts[island], Math.Pow(1.0 / islandCounts[island], power));
}
// scale down the difficulty if the object is doubletappable
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index 007cd977e5..a5ef0764bb 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
new Aim(mods, true),
new Aim(mods, false),
- new Speed(mods)
+ new Speed(mods, clockRate)
};
if (mods.Any(h => h is OsuModFlashlight))
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
index 40aac013ab..ae85980815 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
@@ -16,6 +16,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
///
public class Speed : OsuStrainSkill
{
+ private readonly double clockRate;
private double skillMultiplier => 1375;
private double strainDecayBase => 0.3;
@@ -27,9 +28,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
private readonly List objectStrains = new List();
- public Speed(Mod[] mods)
+ public Speed(Mod[] mods, double clockRate)
: base(mods)
{
+ this.clockRate = clockRate;
}
private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000);
@@ -41,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
currentStrain *= strainDecay(((OsuDifficultyHitObject)current).StrainTime);
currentStrain += SpeedEvaluator.EvaluateDifficultyOf(current) * skillMultiplier;
- currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current);
+ currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current, clockRate);
double totalStrain = currentStrain * currentRhythm;
From 7fda8bc95b8092124820306cab26561e66cda8fe Mon Sep 17 00:00:00 2001
From: StanR
Date: Tue, 27 Aug 2024 23:48:15 +0500
Subject: [PATCH 0157/1255] Reduce repetition nerf for non-consecutive islands
---
.../Difficulty/Evaluators/RhythmEvaluator.cs | 37 +++++++++++--------
1 file changed, 22 insertions(+), 15 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
index 26f50efc72..9e851f11a6 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs
@@ -14,19 +14,24 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
{
private readonly struct Island : IEquatable
{
- public Island()
+ private readonly double deltaDifferenceEpsilon;
+
+ public Island(double epsilon)
{
+ deltaDifferenceEpsilon = epsilon;
}
public Island(int firstDelta, double epsilon)
{
- AddDelta(firstDelta, epsilon);
+ deltaDifferenceEpsilon = epsilon;
+ AddDelta(firstDelta);
}
public List Deltas { get; } = new List();
- public void AddDelta(int delta, double epsilon)
+ public void AddDelta(int delta)
{
+ double epsilon = deltaDifferenceEpsilon;
int existingDelta = Deltas.FirstOrDefault(x => Math.Abs(x - delta) >= epsilon);
Deltas.Add(existingDelta == default ? delta : existingDelta);
@@ -34,10 +39,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
public double AverageDelta() => Deltas.Count > 0 ? Math.Max(Deltas.Average(), OsuDifficultyHitObject.MIN_DELTA_TIME) : 0;
- public bool IsSimilarPolarity(Island other, double epsilon)
+ public bool IsSimilarPolarity(Island other)
{
// consider islands to be of similar polarity only if they're having the same average delta (we don't want to consider 3 singletaps similar to a triple)
- return Math.Abs(AverageDelta() - other.AverageDelta()) < epsilon &&
+ return Math.Abs(AverageDelta() - other.AverageDelta()) < deltaDifferenceEpsilon &&
Deltas.Count % 2 == other.Deltas.Count % 2;
}
@@ -61,7 +66,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
private const int history_time_max = 5 * 1000; // 5 seconds of calculatingRhythmBonus max.
private const int history_objects_max = 32;
- private const double rhythm_multiplier = 1.25;
+ private const double rhythm_multiplier = 1.2;
///
/// Calculates a rhythm multiplier for the difficulty of the tap associated with historic data of the current .
@@ -73,8 +78,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
double rhythmComplexitySum = 0;
- var island = new Island();
- var previousIsland = new Island();
+ double deltaDifferenceEpsilon = ((OsuDifficultyHitObject)current).HitWindowGreat * 0.3;
+
+ var island = new Island(deltaDifferenceEpsilon);
+ var previousIsland = new Island(deltaDifferenceEpsilon);
Dictionary islandCounts = new Dictionary();
int historyTimeMaxAdjusted = (int)Math.Ceiling(history_time_max / clockRate);
@@ -106,9 +113,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
double prevDelta = prevObj.StrainTime;
double lastDelta = lastObj.StrainTime;
- double currRatio = 1.0 + 7.27 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses.
-
- double deltaDifferenceEpsilon = currObj.HitWindowGreat * 0.3;
+ double currRatio = 1.0 + 5.8 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses.
double windowPenalty = Math.Min(1, Math.Max(0, Math.Abs(prevDelta - currDelta) - deltaDifferenceEpsilon) / deltaDifferenceEpsilon);
@@ -119,7 +124,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
if (!(Math.Abs(prevDelta - currDelta) > deltaDifferenceEpsilon))
{
// island is still progressing
- island.AddDelta((int)currDelta, deltaDifferenceEpsilon);
+ island.AddDelta((int)currDelta);
}
else
{
@@ -131,10 +136,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
// unintentional side effect is that bursts with kicksliders at the ends might have lower difficulty than bursts without sliders
// therefore we're checking for quick sliders and don't lower the difficulty for them since they don't really make tapping easier (no time to adjust)
if (prevObj.BaseObject is Slider && prevObj.TravelTime > prevDelta * 1.5)
- effectiveRatio *= 0.15;
+ effectiveRatio *= 0.2;
// repeated island polartiy (2 -> 4, 3 -> 5)
- if (island.IsSimilarPolarity(previousIsland, deltaDifferenceEpsilon))
+ if (island.IsSimilarPolarity(previousIsland))
effectiveRatio *= 0.3;
// previous increase happened a note ago, 1/1->1/2-1/4, dont want to buff this.
@@ -143,7 +148,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
if (!islandCounts.TryAdd(island, 1))
{
- islandCounts[island]++;
+ // only add island to island counts if they're going one after another
+ if (previousIsland.Equals(island))
+ islandCounts[island]++;
// repeated island (ex: triplet -> triplet)
double power = logistic(island.AverageDelta(), 4, 0.165, 10);
From d4bfbe59174a7669d6027d704092ff829cb990d5 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Thu, 29 Aug 2024 20:21:32 +0900
Subject: [PATCH 0158/1255] Add test
---
.../ManiaScoreProcessorTest.cs | 39 +++++++++++++++++++
1 file changed, 39 insertions(+)
create mode 100644 osu.Game.Rulesets.Mania.Tests/ManiaScoreProcessorTest.cs
diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaScoreProcessorTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaScoreProcessorTest.cs
new file mode 100644
index 0000000000..1516aaf058
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/ManiaScoreProcessorTest.cs
@@ -0,0 +1,39 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using NUnit.Framework;
+using osu.Game.Rulesets.Mania.Scoring;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Scoring;
+
+namespace osu.Game.Rulesets.Mania.Tests
+{
+ [TestFixture]
+ public class ManiaScoreProcessorTest
+ {
+ [TestCase(ScoreRank.X, 1, HitResult.Perfect)]
+ [TestCase(ScoreRank.X, 0.99, HitResult.Great)]
+ [TestCase(ScoreRank.D, 0.1, HitResult.Great)]
+ [TestCase(ScoreRank.X, 0.99, HitResult.Perfect, HitResult.Great)]
+ [TestCase(ScoreRank.X, 0.99, HitResult.Great, HitResult.Great)]
+ [TestCase(ScoreRank.S, 0.99, HitResult.Perfect, HitResult.Good)]
+ [TestCase(ScoreRank.S, 0.99, HitResult.Perfect, HitResult.Ok)]
+ [TestCase(ScoreRank.S, 0.99, HitResult.Perfect, HitResult.Meh)]
+ [TestCase(ScoreRank.S, 0.99, HitResult.Perfect, HitResult.Miss)]
+ [TestCase(ScoreRank.S, 0.99, HitResult.Great, HitResult.Good)]
+ [TestCase(ScoreRank.S, 0.99, HitResult.Great, HitResult.Ok)]
+ [TestCase(ScoreRank.S, 0.99, HitResult.Great, HitResult.Meh)]
+ [TestCase(ScoreRank.S, 0.99, HitResult.Great, HitResult.Miss)]
+ public void TestRanks(ScoreRank expected, double accuracy, params HitResult[] results)
+ {
+ var scoreProcessor = new ManiaScoreProcessor();
+
+ Dictionary resultsDict = new Dictionary();
+ foreach (var result in results)
+ resultsDict[result] = resultsDict.GetValueOrDefault(result) + 1;
+
+ Assert.That(scoreProcessor.RankFromScore(accuracy, resultsDict), Is.EqualTo(expected));
+ }
+ }
+}
From d59d5685d08ef99f15beba32e60f2ebe0c2b9c18 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Thu, 29 Aug 2024 20:21:59 +0900
Subject: [PATCH 0159/1255] Make mania award SS even if there are GREAT
judgements
---
.../Scoring/ManiaScoreProcessor.cs | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
index 0444394d87..dfd6ed6dd2 100644
--- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
+++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
@@ -9,6 +9,7 @@ using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
+using osu.Game.Scoring;
namespace osu.Game.Rulesets.Mania.Scoring
{
@@ -58,6 +59,24 @@ namespace osu.Game.Rulesets.Mania.Scoring
return GetBaseScoreForResult(result);
}
+ public override ScoreRank RankFromScore(double accuracy, IReadOnlyDictionary results)
+ {
+ ScoreRank rank = base.RankFromScore(accuracy, results);
+
+ if (rank != ScoreRank.S)
+ return rank;
+
+ // SS is expected as long as all hitobjects have been hit with either a GREAT or PERFECT result.
+
+ bool anyImperfect =
+ results.GetValueOrDefault(HitResult.Good) > 0
+ || results.GetValueOrDefault(HitResult.Ok) > 0
+ || results.GetValueOrDefault(HitResult.Meh) > 0
+ || results.GetValueOrDefault(HitResult.Miss) > 0;
+
+ return anyImperfect ? rank : ScoreRank.X;
+ }
+
private class JudgementOrderComparer : IComparer
{
public static readonly JudgementOrderComparer DEFAULT = new JudgementOrderComparer();
From d97a94aadd68aada70a8d808fdb7622e96267e44 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Thu, 29 Aug 2024 20:22:54 +0900
Subject: [PATCH 0160/1255] Change accuracy circle to show badges based on
score rank
---
osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs
index cebc54f490..319a87fdfc 100644
--- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs
+++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs
@@ -298,7 +298,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy
{
foreach (var badge in badges)
{
- if (badge.Accuracy > score.Accuracy)
+ if (badge.Rank > score.Rank)
continue;
using (BeginDelayedSequence(
From ca2dc702e612986ccb40d7dcfe9fdb5acdb2d79b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Mon, 2 Sep 2024 09:52:00 +0200
Subject: [PATCH 0161/1255] Move helper class out to separate file
---
osu.Game/Screens/Edit/Timing/TimingSection.cs | 39 ----------------
.../Edit/Timing/TimingSectionAdjustments.cs | 46 +++++++++++++++++++
2 files changed, 46 insertions(+), 39 deletions(-)
create mode 100644 osu.Game/Screens/Edit/Timing/TimingSectionAdjustments.cs
diff --git a/osu.Game/Screens/Edit/Timing/TimingSection.cs b/osu.Game/Screens/Edit/Timing/TimingSection.cs
index 4b47ecaab6..139f53a961 100644
--- a/osu.Game/Screens/Edit/Timing/TimingSection.cs
+++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs
@@ -1,17 +1,11 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.Collections.Generic;
-using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
-using osu.Framework.Utils;
-using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.UserInterfaceV2;
-using osu.Game.Rulesets.Objects;
-using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Screens.Edit.Timing
{
@@ -135,37 +129,4 @@ namespace osu.Game.Screens.Edit.Timing
private static double beatLengthToBpm(double beatLength) => 60000 / beatLength;
}
-
- public static class TimingSectionAdjustments
- {
- public static List HitObjectsInTimingRange(IBeatmap beatmap, double time)
- {
- // If the first group, we grab all hitobjects prior to the next, if the last group, we grab all remaining hitobjects
- double startTime = beatmap.ControlPointInfo.TimingPoints.Any(x => x.Time < time) ? time : double.MinValue;
- double endTime = beatmap.ControlPointInfo.TimingPoints.FirstOrDefault(x => x.Time > time)?.Time ?? double.MaxValue;
-
- return beatmap.HitObjects.Where(x => Precision.AlmostBigger(x.StartTime, startTime) && Precision.DefinitelyBigger(endTime, x.StartTime)).ToList();
- }
-
- public static void AdjustHitObjectOffset(IBeatmap beatmap, TimingControlPoint timingControlPoint, double adjust)
- {
- foreach (HitObject hitObject in HitObjectsInTimingRange(beatmap, timingControlPoint.Time))
- {
- hitObject.StartTime += adjust;
- }
- }
-
- public static void SetHitObjectBPM(IBeatmap beatmap, TimingControlPoint timingControlPoint, double oldBeatLength)
- {
- foreach (HitObject hitObject in HitObjectsInTimingRange(beatmap, timingControlPoint.Time))
- {
- double beat = (hitObject.StartTime - timingControlPoint.Time) / oldBeatLength;
-
- hitObject.StartTime = (beat * timingControlPoint.BeatLength) + timingControlPoint.Time;
-
- if (hitObject is not IHasRepeats && hitObject is IHasDuration hitObjectWithDuration)
- hitObjectWithDuration.Duration *= timingControlPoint.BeatLength / oldBeatLength;
- }
- }
- }
}
diff --git a/osu.Game/Screens/Edit/Timing/TimingSectionAdjustments.cs b/osu.Game/Screens/Edit/Timing/TimingSectionAdjustments.cs
new file mode 100644
index 0000000000..11c4be4790
--- /dev/null
+++ b/osu.Game/Screens/Edit/Timing/TimingSectionAdjustments.cs
@@ -0,0 +1,46 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using System.Linq;
+using osu.Framework.Utils;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Types;
+
+namespace osu.Game.Screens.Edit.Timing
+{
+ public static class TimingSectionAdjustments
+ {
+ public static List HitObjectsInTimingRange(IBeatmap beatmap, double time)
+ {
+ // If the first group, we grab all hitobjects prior to the next, if the last group, we grab all remaining hitobjects
+ double startTime = beatmap.ControlPointInfo.TimingPoints.Any(x => x.Time < time) ? time : double.MinValue;
+ double endTime = beatmap.ControlPointInfo.TimingPoints.FirstOrDefault(x => x.Time > time)?.Time ?? double.MaxValue;
+
+ return beatmap.HitObjects.Where(x => Precision.AlmostBigger(x.StartTime, startTime) && Precision.DefinitelyBigger(endTime, x.StartTime)).ToList();
+ }
+
+ public static void AdjustHitObjectOffset(IBeatmap beatmap, TimingControlPoint timingControlPoint, double adjust)
+ {
+ foreach (HitObject hitObject in HitObjectsInTimingRange(beatmap, timingControlPoint.Time))
+ {
+ hitObject.StartTime += adjust;
+ }
+ }
+
+ public static void SetHitObjectBPM(IBeatmap beatmap, TimingControlPoint timingControlPoint, double oldBeatLength)
+ {
+ foreach (HitObject hitObject in HitObjectsInTimingRange(beatmap, timingControlPoint.Time))
+ {
+ double beat = (hitObject.StartTime - timingControlPoint.Time) / oldBeatLength;
+
+ hitObject.StartTime = (beat * timingControlPoint.BeatLength) + timingControlPoint.Time;
+
+ if (hitObject is not IHasRepeats && hitObject is IHasDuration hitObjectWithDuration)
+ hitObjectWithDuration.Duration *= timingControlPoint.BeatLength / oldBeatLength;
+ }
+ }
+ }
+}
From e61fd080c14602c489d3ef5b4937bc441b2bbc86 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Mon, 2 Sep 2024 09:59:19 +0200
Subject: [PATCH 0162/1255] Retouch & document helper methods
---
.../Editing/TimingSectionAdjustmentsTest.cs | 10 ++++++++
.../Edit/Timing/TimingSectionAdjustments.cs | 23 +++++++++++++------
2 files changed, 26 insertions(+), 7 deletions(-)
create mode 100644 osu.Game.Tests/Editing/TimingSectionAdjustmentsTest.cs
diff --git a/osu.Game.Tests/Editing/TimingSectionAdjustmentsTest.cs b/osu.Game.Tests/Editing/TimingSectionAdjustmentsTest.cs
new file mode 100644
index 0000000000..2cb9e72c1f
--- /dev/null
+++ b/osu.Game.Tests/Editing/TimingSectionAdjustmentsTest.cs
@@ -0,0 +1,10 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+namespace osu.Game.Tests.Editing
+{
+ public class TimingSectionAdjustmentsTest
+ {
+
+ }
+}
diff --git a/osu.Game/Screens/Edit/Timing/TimingSectionAdjustments.cs b/osu.Game/Screens/Edit/Timing/TimingSectionAdjustments.cs
index 11c4be4790..65edc47ff5 100644
--- a/osu.Game/Screens/Edit/Timing/TimingSectionAdjustments.cs
+++ b/osu.Game/Screens/Edit/Timing/TimingSectionAdjustments.cs
@@ -13,26 +13,35 @@ namespace osu.Game.Screens.Edit.Timing
{
public static class TimingSectionAdjustments
{
- public static List HitObjectsInTimingRange(IBeatmap beatmap, double time)
+ ///
+ /// Returns all objects from which are affected by the supplied .
+ ///
+ public static List HitObjectsInTimingRange(IBeatmap beatmap, TimingControlPoint timingControlPoint)
{
// If the first group, we grab all hitobjects prior to the next, if the last group, we grab all remaining hitobjects
- double startTime = beatmap.ControlPointInfo.TimingPoints.Any(x => x.Time < time) ? time : double.MinValue;
- double endTime = beatmap.ControlPointInfo.TimingPoints.FirstOrDefault(x => x.Time > time)?.Time ?? double.MaxValue;
+ double startTime = beatmap.ControlPointInfo.TimingPoints.Any(x => x.Time < timingControlPoint.Time) ? timingControlPoint.Time : double.MinValue;
+ double endTime = beatmap.ControlPointInfo.TimingPoints.FirstOrDefault(x => x.Time > timingControlPoint.Time)?.Time ?? double.MaxValue;
return beatmap.HitObjects.Where(x => Precision.AlmostBigger(x.StartTime, startTime) && Precision.DefinitelyBigger(endTime, x.StartTime)).ToList();
}
- public static void AdjustHitObjectOffset(IBeatmap beatmap, TimingControlPoint timingControlPoint, double adjust)
+ ///
+ /// Moves all relevant objects after 's offset has been changed by .
+ ///
+ public static void AdjustHitObjectOffset(IBeatmap beatmap, TimingControlPoint timingControlPoint, double adjustment)
{
- foreach (HitObject hitObject in HitObjectsInTimingRange(beatmap, timingControlPoint.Time))
+ foreach (HitObject hitObject in HitObjectsInTimingRange(beatmap, timingControlPoint))
{
- hitObject.StartTime += adjust;
+ hitObject.StartTime += adjustment;
}
}
+ ///
+ /// Ensures all relevant objects are still snapped to the same beats after 's beat length / BPM has been changed.
+ ///
public static void SetHitObjectBPM(IBeatmap beatmap, TimingControlPoint timingControlPoint, double oldBeatLength)
{
- foreach (HitObject hitObject in HitObjectsInTimingRange(beatmap, timingControlPoint.Time))
+ foreach (HitObject hitObject in HitObjectsInTimingRange(beatmap, timingControlPoint))
{
double beat = (hitObject.StartTime - timingControlPoint.Time) / oldBeatLength;
From db608159cf6a6b1dcbcd56a668765d94a9593c77 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Mon, 2 Sep 2024 10:27:40 +0200
Subject: [PATCH 0163/1255] Add test coverage
---
.../Editing/TimingSectionAdjustmentsTest.cs | 153 +++++++++++++++++-
1 file changed, 152 insertions(+), 1 deletion(-)
diff --git a/osu.Game.Tests/Editing/TimingSectionAdjustmentsTest.cs b/osu.Game.Tests/Editing/TimingSectionAdjustmentsTest.cs
index 2cb9e72c1f..5f5a1760ea 100644
--- a/osu.Game.Tests/Editing/TimingSectionAdjustmentsTest.cs
+++ b/osu.Game.Tests/Editing/TimingSectionAdjustmentsTest.cs
@@ -1,10 +1,161 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Screens.Edit.Timing;
+
namespace osu.Game.Tests.Editing
{
+ [TestFixture]
public class TimingSectionAdjustmentsTest
{
-
+ [Test]
+ public void TestOffsetAdjustment()
+ {
+ var controlPoints = new ControlPointInfo();
+
+ controlPoints.Add(100, new TimingControlPoint { BeatLength = 100 });
+ controlPoints.Add(50_000, new TimingControlPoint { BeatLength = 200 });
+ controlPoints.Add(100_000, new TimingControlPoint { BeatLength = 50 });
+
+ var beatmap = new Beatmap
+ {
+ ControlPointInfo = controlPoints,
+ HitObjects = new List
+ {
+ new HitCircle { StartTime = 0 },
+ new HitCircle { StartTime = 200 },
+ new HitCircle { StartTime = 49_900 },
+ new HitCircle { StartTime = 50_000 },
+ new HitCircle { StartTime = 50_200 },
+ new HitCircle { StartTime = 99_800 },
+ new HitCircle { StartTime = 100_000 },
+ new HitCircle { StartTime = 100_050 },
+ new HitCircle { StartTime = 100_550 },
+ }
+ };
+
+ moveTimingPoint(beatmap, 100, -50);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(beatmap.HitObjects[0].StartTime, Is.EqualTo(-50));
+ Assert.That(beatmap.HitObjects[1].StartTime, Is.EqualTo(150));
+ Assert.That(beatmap.HitObjects[2].StartTime, Is.EqualTo(49_850));
+ Assert.That(beatmap.HitObjects[3].StartTime, Is.EqualTo(50_000));
+ });
+
+ moveTimingPoint(beatmap, 50_000, 1_000);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(beatmap.HitObjects[2].StartTime, Is.EqualTo(49_850));
+ Assert.That(beatmap.HitObjects[3].StartTime, Is.EqualTo(51_000));
+ Assert.That(beatmap.HitObjects[4].StartTime, Is.EqualTo(51_200));
+ Assert.That(beatmap.HitObjects[5].StartTime, Is.EqualTo(100_800));
+ Assert.That(beatmap.HitObjects[6].StartTime, Is.EqualTo(100_000));
+ });
+
+ moveTimingPoint(beatmap, 100_000, 10_000);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(beatmap.HitObjects[4].StartTime, Is.EqualTo(51_200));
+ Assert.That(beatmap.HitObjects[5].StartTime, Is.EqualTo(110_800));
+ Assert.That(beatmap.HitObjects[6].StartTime, Is.EqualTo(110_000));
+ Assert.That(beatmap.HitObjects[7].StartTime, Is.EqualTo(110_050));
+ Assert.That(beatmap.HitObjects[8].StartTime, Is.EqualTo(110_550));
+ });
+ }
+
+ [Test]
+ public void TestBPMAdjustment()
+ {
+ var controlPoints = new ControlPointInfo();
+
+ controlPoints.Add(100, new TimingControlPoint { BeatLength = 100 });
+ controlPoints.Add(50_000, new TimingControlPoint { BeatLength = 200 });
+ controlPoints.Add(100_000, new TimingControlPoint { BeatLength = 50 });
+
+ var beatmap = new Beatmap
+ {
+ ControlPointInfo = controlPoints,
+ HitObjects = new List
+ {
+ new HitCircle { StartTime = 0 },
+ new HitCircle { StartTime = 200 },
+ new Spinner { StartTime = 500, EndTime = 1000 },
+ new HitCircle { StartTime = 49_900 },
+ new HitCircle { StartTime = 50_000 },
+ new HitCircle { StartTime = 50_200 },
+ new HitCircle { StartTime = 99_800 },
+ new HitCircle { StartTime = 100_000 },
+ new HitCircle { StartTime = 100_050 },
+ new HitCircle { StartTime = 100_550 },
+ }
+ };
+
+ adjustBeatLength(beatmap, 100, 50);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(beatmap.HitObjects[0].StartTime, Is.EqualTo(50));
+ Assert.That(beatmap.HitObjects[1].StartTime, Is.EqualTo(150));
+ Assert.That(beatmap.HitObjects[2].StartTime, Is.EqualTo(300));
+ Assert.That(beatmap.HitObjects[2].GetEndTime(), Is.EqualTo(550));
+ Assert.That(beatmap.HitObjects[3].StartTime, Is.EqualTo(25_000));
+ Assert.That(beatmap.HitObjects[4].StartTime, Is.EqualTo(50_000));
+ });
+
+ adjustBeatLength(beatmap, 50_000, 400);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(beatmap.HitObjects[2].StartTime, Is.EqualTo(300));
+ Assert.That(beatmap.HitObjects[2].GetEndTime(), Is.EqualTo(550));
+ Assert.That(beatmap.HitObjects[3].StartTime, Is.EqualTo(25_000));
+ Assert.That(beatmap.HitObjects[4].StartTime, Is.EqualTo(50_000));
+ Assert.That(beatmap.HitObjects[5].StartTime, Is.EqualTo(50_400));
+ Assert.That(beatmap.HitObjects[6].StartTime, Is.EqualTo(149_600));
+ Assert.That(beatmap.HitObjects[7].StartTime, Is.EqualTo(100_000));
+ });
+
+ adjustBeatLength(beatmap, 100_000, 100);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(beatmap.HitObjects[5].StartTime, Is.EqualTo(50_400));
+ Assert.That(beatmap.HitObjects[6].StartTime, Is.EqualTo(199_200));
+ Assert.That(beatmap.HitObjects[7].StartTime, Is.EqualTo(100_000));
+ Assert.That(beatmap.HitObjects[8].StartTime, Is.EqualTo(100_100));
+ Assert.That(beatmap.HitObjects[9].StartTime, Is.EqualTo(101_100));
+ });
+ }
+
+ private static void moveTimingPoint(IBeatmap beatmap, double originalTime, double adjustment)
+ {
+ var controlPoints = beatmap.ControlPointInfo;
+ var controlPointGroup = controlPoints.GroupAt(originalTime);
+ var timingPoint = controlPointGroup.ControlPoints.OfType().Single();
+ controlPoints.RemoveGroup(controlPointGroup);
+ TimingSectionAdjustments.AdjustHitObjectOffset(beatmap, timingPoint, adjustment);
+ controlPoints.Add(originalTime - adjustment, timingPoint);
+ }
+
+ private static void adjustBeatLength(IBeatmap beatmap, double groupTime, double newBeatLength)
+ {
+ var controlPoints = beatmap.ControlPointInfo;
+ var controlPointGroup = controlPoints.GroupAt(groupTime);
+ var timingPoint = controlPointGroup.ControlPoints.OfType().Single();
+ double oldBeatLength = timingPoint.BeatLength;
+ timingPoint.BeatLength = newBeatLength;
+ TimingSectionAdjustments.SetHitObjectBPM(beatmap, timingPoint, oldBeatLength);
+ }
}
}
From 3eaffbb70a0ca053fb3c3443b8a846d817968a70 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Mon, 2 Sep 2024 10:40:55 +0200
Subject: [PATCH 0164/1255] Make application of offset/BPM object adjustments
more sane
---
osu.Game/Screens/Edit/Timing/GroupSection.cs | 6 ++++--
.../Screens/Edit/Timing/TapTimingControl.cs | 17 ++++++++++++++---
osu.Game/Screens/Edit/Timing/TimingSection.cs | 2 ++
3 files changed, 20 insertions(+), 5 deletions(-)
diff --git a/osu.Game/Screens/Edit/Timing/GroupSection.cs b/osu.Game/Screens/Edit/Timing/GroupSection.cs
index 18a222f414..abcdf7e4ff 100644
--- a/osu.Game/Screens/Edit/Timing/GroupSection.cs
+++ b/osu.Game/Screens/Edit/Timing/GroupSection.cs
@@ -113,15 +113,17 @@ namespace osu.Game.Screens.Edit.Timing
{
// Only adjust hit object offsets if the group contains a timing control point
if (Beatmap.AdjustNotesOnOffsetBPMChange.Value && cp is TimingControlPoint tp)
+ {
TimingSectionAdjustments.AdjustHitObjectOffset(Beatmap, tp, time - SelectedGroup.Value.Time);
+ Beatmap.UpdateAllHitObjects();
+ }
+
Beatmap.ControlPointInfo.Add(time, cp);
}
// the control point might not necessarily exist yet, if currentGroupItems was empty.
SelectedGroup.Value = Beatmap.ControlPointInfo.GroupAt(time, true);
- Beatmap.UpdateAllHitObjects();
-
changeHandler?.EndChange();
}
}
diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
index f2e37369d1..91a0a43d62 100644
--- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
+++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs
@@ -202,6 +202,7 @@ namespace osu.Game.Screens.Edit.Timing
// VERY TEMPORARY
var currentGroupItems = selectedGroup.Value.ControlPoints.ToArray();
+ beatmap.BeginChange();
beatmap.ControlPointInfo.RemoveGroup(selectedGroup.Value);
double newOffset = selectedGroup.Value.Time + adjust;
@@ -209,17 +210,20 @@ namespace osu.Game.Screens.Edit.Timing
foreach (var cp in currentGroupItems)
{
if (beatmap.AdjustNotesOnOffsetBPMChange.Value && cp is TimingControlPoint tp)
+ {
TimingSectionAdjustments.AdjustHitObjectOffset(beatmap, tp, adjust);
+ beatmap.UpdateAllHitObjects();
+ }
+
beatmap.ControlPointInfo.Add(newOffset, cp);
}
// the control point might not necessarily exist yet, if currentGroupItems was empty.
selectedGroup.Value = beatmap.ControlPointInfo.GroupAt(newOffset, true);
+ beatmap.EndChange();
if (!editorClock.IsRunning && wasAtStart)
editorClock.Seek(newOffset);
-
- beatmap.UpdateAllHitObjects();
}
private void adjustBpm(double adjust)
@@ -229,9 +233,16 @@ namespace osu.Game.Screens.Edit.Timing
if (timing == null)
return;
+ double oldBeatLength = timing.BeatLength;
timing.BeatLength = 60000 / (timing.BPM + adjust);
- beatmap.UpdateAllHitObjects();
+ if (beatmap.AdjustNotesOnOffsetBPMChange.Value)
+ {
+ beatmap.BeginChange();
+ TimingSectionAdjustments.SetHitObjectBPM(beatmap, timing, oldBeatLength);
+ beatmap.UpdateAllHitObjects();
+ beatmap.EndChange();
+ }
}
private partial class InlineButton : OsuButton
diff --git a/osu.Game/Screens/Edit/Timing/TimingSection.cs b/osu.Game/Screens/Edit/Timing/TimingSection.cs
index 139f53a961..e1567d65aa 100644
--- a/osu.Game/Screens/Edit/Timing/TimingSection.cs
+++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs
@@ -48,8 +48,10 @@ namespace osu.Game.Screens.Edit.Timing
if (!Beatmap.AdjustNotesOnOffsetBPMChange.Value || ControlPoint.Value == null)
return;
+ Beatmap.BeginChange();
TimingSectionAdjustments.SetHitObjectBPM(Beatmap, ControlPoint.Value, val.OldValue);
Beatmap.UpdateAllHitObjects();
+ Beatmap.EndChange();
});
}
From 57f1259a336163aac5439e34c88f5c7a1d35b8ee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Mon, 2 Sep 2024 10:49:31 +0200
Subject: [PATCH 0165/1255] Fix weirdness around spurious adjustments firing
due to overloaded bindable
---
osu.Game/Screens/Edit/Timing/TimingSection.cs | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/osu.Game/Screens/Edit/Timing/TimingSection.cs b/osu.Game/Screens/Edit/Timing/TimingSection.cs
index e1567d65aa..e668120d0d 100644
--- a/osu.Game/Screens/Edit/Timing/TimingSection.cs
+++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs
@@ -1,6 +1,7 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@@ -43,16 +44,16 @@ namespace osu.Game.Screens.Edit.Timing
if (!isRebinding) ChangeHandler?.SaveState();
}
- bpmTextEntry.Bindable.BindValueChanged(val =>
+ bpmTextEntry.OnCommit = (oldBeatLength, _) =>
{
if (!Beatmap.AdjustNotesOnOffsetBPMChange.Value || ControlPoint.Value == null)
return;
Beatmap.BeginChange();
- TimingSectionAdjustments.SetHitObjectBPM(Beatmap, ControlPoint.Value, val.OldValue);
+ TimingSectionAdjustments.SetHitObjectBPM(Beatmap, ControlPoint.Value, oldBeatLength);
Beatmap.UpdateAllHitObjects();
Beatmap.EndChange();
- });
+ };
}
private bool isRebinding;
@@ -85,6 +86,8 @@ namespace osu.Game.Screens.Edit.Timing
private partial class BPMTextBox : LabelledTextBox
{
+ public new Action? OnCommit { get; set; }
+
private readonly BindableNumber beatLengthBindable = new TimingControlPoint().BeatLengthBindable;
public BPMTextBox()
@@ -92,10 +95,12 @@ namespace osu.Game.Screens.Edit.Timing
Label = "BPM";
SelectAllOnFocus = true;
- OnCommit += (_, isNew) =>
+ base.OnCommit += (_, isNew) =>
{
if (!isNew) return;
+ double oldBeatLength = beatLengthBindable.Value;
+
try
{
if (double.TryParse(Current.Value, out double doubleVal) && doubleVal > 0)
@@ -109,6 +114,7 @@ namespace osu.Game.Screens.Edit.Timing
// This is run regardless of parsing success as the parsed number may not actually trigger a change
// due to bindable clamping. Even in such a case we want to update the textbox to a sane visual state.
beatLengthBindable.TriggerChange();
+ OnCommit?.Invoke(oldBeatLength, beatLengthBindable.Value);
};
beatLengthBindable.BindValueChanged(val =>
From 3531f646f232eb21ed14b27f31a43884e425d547 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Thu, 18 Jul 2024 09:46:06 +0200
Subject: [PATCH 0166/1255] Refactor `DrawableOsuMenuItem` to remove a hack
---
.../UserInterface/DrawableOsuMenuItem.cs | 41 +++++++++++--------
.../UserInterface/DrawableStatefulMenuItem.cs | 10 +----
2 files changed, 25 insertions(+), 26 deletions(-)
diff --git a/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs b/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs
index 06ef75cf58..703dcbf3b7 100644
--- a/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs
+++ b/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs
@@ -124,7 +124,7 @@ namespace osu.Game.Graphics.UserInterface
protected sealed override Drawable CreateContent() => text = CreateTextContainer();
protected virtual TextContainer CreateTextContainer() => new TextContainer();
- protected partial class TextContainer : Container, IHasText
+ protected partial class TextContainer : FillFlowContainer, IHasText
{
public LocalisableString Text
{
@@ -145,25 +145,32 @@ namespace osu.Game.Graphics.UserInterface
Origin = Anchor.CentreLeft;
AutoSizeAxes = Axes.Both;
+ Spacing = new Vector2(10);
+ Direction = FillDirection.Horizontal;
- Children = new Drawable[]
+ Child = new Container
{
- NormalText = new OsuSpriteText
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ AutoSizeAxes = Axes.Both,
+ Margin = new MarginPadding { Horizontal = MARGIN_HORIZONTAL, Vertical = MARGIN_VERTICAL },
+ Children = new Drawable[]
{
- AlwaysPresent = true, // ensures that the menu item does not change width when switching between normal and bold text.
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- Font = OsuFont.GetFont(size: text_size),
- Margin = new MarginPadding { Horizontal = MARGIN_HORIZONTAL, Vertical = MARGIN_VERTICAL },
- },
- BoldText = new OsuSpriteText
- {
- AlwaysPresent = true, // ensures that the menu item does not change width when switching between normal and bold text.
- Alpha = 0,
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold),
- Margin = new MarginPadding { Horizontal = MARGIN_HORIZONTAL, Vertical = MARGIN_VERTICAL },
+ NormalText = new OsuSpriteText
+ {
+ AlwaysPresent = true, // ensures that the menu item does not change width when switching between normal and bold text.
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Font = OsuFont.GetFont(size: text_size),
+ },
+ BoldText = new OsuSpriteText
+ {
+ AlwaysPresent = true, // ensures that the menu item does not change width when switching between normal and bold text.
+ Alpha = 0,
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold),
+ }
}
};
}
diff --git a/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs b/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs
index b9e81e1bf2..6888c2c71b 100644
--- a/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs
+++ b/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs
@@ -51,7 +51,7 @@ namespace osu.Game.Graphics.UserInterface
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(10),
- Margin = new MarginPadding { Horizontal = MARGIN_HORIZONTAL },
+ Margin = new MarginPadding { Left = -MARGIN_HORIZONTAL, Right = MARGIN_HORIZONTAL },
AlwaysPresent = true,
});
}
@@ -62,14 +62,6 @@ namespace osu.Game.Graphics.UserInterface
state.BindValueChanged(updateState, true);
}
- protected override void Update()
- {
- base.Update();
-
- // Todo: This is bad. This can maybe be done better with a refactor of DrawableOsuMenuItem.
- stateIcon.X = BoldText.DrawWidth + 10;
- }
-
private void updateState(ValueChangedEvent