diff --git a/osu.Android.props b/osu.Android.props
index ca10943728..efaf7241cb 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -54,6 +54,6 @@
-
+
diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs
index b876c774b2..3c237c86be 100644
--- a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs
@@ -47,55 +47,53 @@ namespace osu.Game.Rulesets.Catch.Difficulty
return 0;
// We are heavily relying on aim in catch the beat
- double value = Math.Pow(5.0f * Math.Max(1.0f, Attributes.StarRating / 0.0049f) - 4.0f, 2.0f) / 100000.0f;
+ double value = Math.Pow(5.0 * Math.Max(1.0, Attributes.StarRating / 0.0049) - 4.0, 2.0) / 100000.0;
// Longer maps are worth more. "Longer" means how many hits there are which can contribute to combo
int numTotalHits = totalComboHits();
// Longer maps are worth more
- float lengthBonus =
- 0.95f + 0.4f * Math.Min(1.0f, numTotalHits / 3000.0f) +
- (numTotalHits > 3000 ? MathF.Log10(numTotalHits / 3000.0f) * 0.5f : 0.0f);
+ double lengthBonus =
+ 0.95 + 0.4 * Math.Min(1.0, numTotalHits / 3000.0) +
+ (numTotalHits > 3000 ? Math.Log10(numTotalHits / 3000.0) * 0.5 : 0.0);
// Longer maps are worth more
value *= lengthBonus;
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
- value *= Math.Pow(0.97f, misses);
+ value *= Math.Pow(0.97, misses);
// Combo scaling
- float beatmapMaxCombo = Attributes.MaxCombo;
- if (beatmapMaxCombo > 0)
- value *= Math.Min(Math.Pow(Attributes.MaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
+ if (Attributes.MaxCombo > 0)
+ value *= Math.Min(Math.Pow(Attributes.MaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
- float approachRate = (float)Attributes.ApproachRate;
- float approachRateFactor = 1.0f;
- if (approachRate > 9.0f)
- approachRateFactor += 0.1f * (approachRate - 9.0f); // 10% for each AR above 9
- else if (approachRate < 8.0f)
- approachRateFactor += 0.025f * (8.0f - approachRate); // 2.5% for each AR below 8
+ double approachRateFactor = 1.0;
+ if (Attributes.ApproachRate > 9.0)
+ approachRateFactor += 0.1 * (Attributes.ApproachRate - 9.0); // 10% for each AR above 9
+ else if (Attributes.ApproachRate < 8.0)
+ approachRateFactor += 0.025 * (8.0 - Attributes.ApproachRate); // 2.5% for each AR below 8
value *= approachRateFactor;
if (mods.Any(m => m is ModHidden))
// Hiddens gives nothing on max approach rate, and more the lower it is
- value *= 1.05f + 0.075f * (10.0f - Math.Min(10.0f, approachRate)); // 7.5% for each AR below 10
+ value *= 1.05 + 0.075 * (10.0 - Math.Min(10.0, Attributes.ApproachRate)); // 7.5% for each AR below 10
if (mods.Any(m => m is ModFlashlight))
// Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps.
- value *= 1.35f * lengthBonus;
+ value *= 1.35 * lengthBonus;
// Scale the aim value with accuracy _slightly_
- value *= Math.Pow(accuracy(), 5.5f);
+ value *= Math.Pow(accuracy(), 5.5);
// Custom multipliers for NoFail. SpunOut is not applicable.
if (mods.Any(m => m is ModNoFail))
- value *= 0.90f;
+ value *= 0.90;
return value;
}
- private float accuracy() => totalHits() == 0 ? 0 : Math.Clamp((float)totalSuccessfulHits() / totalHits(), 0f, 1f);
+ private float accuracy() => totalHits() == 0 ? 0 : Math.Clamp((float)totalSuccessfulHits() / totalHits(), 0, 1);
private int totalHits() => tinyTicksHit + ticksHit + fruitsHit + misses + tinyTicksMissed;
private int totalSuccessfulHits() => tinyTicksHit + ticksHit + fruitsHit;
private int totalComboHits() => misses + ticksHit + fruitsHit;
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index 093081b6a1..05c78cbc95 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -55,22 +55,22 @@ namespace osu.Game.Rulesets.Osu.Difficulty
return 0;
// Custom multipliers for NoFail and SpunOut.
- double multiplier = 1.12f; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things
+ double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things
if (mods.Any(m => m is OsuModNoFail))
- multiplier *= 0.90f;
+ multiplier *= 0.90;
if (mods.Any(m => m is OsuModSpunOut))
- multiplier *= 0.95f;
+ multiplier *= 0.95;
double aimValue = computeAimValue();
double speedValue = computeSpeedValue();
double accuracyValue = computeAccuracyValue();
double totalValue =
Math.Pow(
- Math.Pow(aimValue, 1.1f) +
- Math.Pow(speedValue, 1.1f) +
- Math.Pow(accuracyValue, 1.1f), 1.0f / 1.1f
+ Math.Pow(aimValue, 1.1) +
+ Math.Pow(speedValue, 1.1) +
+ Math.Pow(accuracyValue, 1.1), 1.0 / 1.1
) * multiplier;
if (categoryRatings != null)
@@ -93,82 +93,82 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (mods.Any(m => m is OsuModTouchDevice))
rawAim = Math.Pow(rawAim, 0.8);
- double aimValue = Math.Pow(5.0f * Math.Max(1.0f, rawAim / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
+ 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.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
- (totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f);
+ 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;
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
- aimValue *= Math.Pow(0.97f, countMiss);
+ aimValue *= Math.Pow(0.97, countMiss);
// Combo scaling
if (beatmapMaxCombo > 0)
- aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
+ aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(beatmapMaxCombo, 0.8), 1.0);
- double approachRateFactor = 1.0f;
+ double approachRateFactor = 1.0;
- if (Attributes.ApproachRate > 10.33f)
- approachRateFactor += 0.3f * (Attributes.ApproachRate - 10.33f);
- else if (Attributes.ApproachRate < 8.0f)
+ if (Attributes.ApproachRate > 10.33)
+ approachRateFactor += 0.3 * (Attributes.ApproachRate - 10.33);
+ else if (Attributes.ApproachRate < 8.0)
{
- approachRateFactor += 0.01f * (8.0f - Attributes.ApproachRate);
+ approachRateFactor += 0.01 * (8.0 - Attributes.ApproachRate);
}
aimValue *= approachRateFactor;
// We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
if (mods.Any(h => h is OsuModHidden))
- aimValue *= 1.0f + 0.04f * (12.0f - Attributes.ApproachRate);
+ aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate);
if (mods.Any(h => h is OsuModFlashlight))
{
// Apply object-based bonus for flashlight.
- aimValue *= 1.0f + 0.35f * Math.Min(1.0f, totalHits / 200.0f) +
+ aimValue *= 1.0 + 0.35 * Math.Min(1.0, totalHits / 200.0) +
(totalHits > 200
- ? 0.3f * Math.Min(1.0f, (totalHits - 200) / 300.0f) +
- (totalHits > 500 ? (totalHits - 500) / 1200.0f : 0.0f)
- : 0.0f);
+ ? 0.3 * Math.Min(1.0, (totalHits - 200) / 300.0) +
+ (totalHits > 500 ? (totalHits - 500) / 1200.0 : 0.0)
+ : 0.0);
}
// Scale the aim value with accuracy _slightly_
- aimValue *= 0.5f + accuracy / 2.0f;
+ aimValue *= 0.5 + accuracy / 2.0;
// It is important to also consider accuracy difficulty when doing that
- aimValue *= 0.98f + Math.Pow(Attributes.OverallDifficulty, 2) / 2500;
+ aimValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500;
return aimValue;
}
private double computeSpeedValue()
{
- double speedValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes.SpeedStrain / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
+ 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
- speedValue *= 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
- (totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f);
+ speedValue *= 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
+ (totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
- speedValue *= Math.Pow(0.97f, countMiss);
+ speedValue *= Math.Pow(0.97, countMiss);
// Combo scaling
if (beatmapMaxCombo > 0)
- speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
+ speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(beatmapMaxCombo, 0.8), 1.0);
- double approachRateFactor = 1.0f;
- if (Attributes.ApproachRate > 10.33f)
- approachRateFactor += 0.3f * (Attributes.ApproachRate - 10.33f);
+ double approachRateFactor = 1.0;
+ if (Attributes.ApproachRate > 10.33)
+ approachRateFactor += 0.3 * (Attributes.ApproachRate - 10.33);
speedValue *= approachRateFactor;
if (mods.Any(m => m is OsuModHidden))
- speedValue *= 1.0f + 0.04f * (12.0f - Attributes.ApproachRate);
+ speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate);
// Scale the speed value with accuracy _slightly_
- speedValue *= 0.02f + accuracy;
+ speedValue *= 0.02 + accuracy;
// It is important to also consider accuracy difficulty when doing that
- speedValue *= 0.96f + Math.Pow(Attributes.OverallDifficulty, 2) / 1600;
+ speedValue *= 0.96 + Math.Pow(Attributes.OverallDifficulty, 2) / 1600;
return speedValue;
}
@@ -190,15 +190,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty
// Lots of arbitrary values from testing.
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
- double accuracyValue = Math.Pow(1.52163f, Attributes.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83f;
+ double accuracyValue = Math.Pow(1.52163, Attributes.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83;
// Bonus for many hitcircles - it's harder to keep good accuracy up for longer
- accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f));
+ accuracyValue *= Math.Min(1.15, Math.Pow(amountHitObjectsWithAccuracy / 1000.0, 0.3));
if (mods.Any(m => m is OsuModHidden))
- accuracyValue *= 1.08f;
+ accuracyValue *= 1.08;
if (mods.Any(m => m is OsuModFlashlight))
- accuracyValue *= 1.02f;
+ accuracyValue *= 1.02;
return accuracyValue;
}
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index 70249db0f6..c3638253e4 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
double strainValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.StarRating / 0.0075) - 4.0, 2.0) / 100000.0;
// Longer maps are worth more
- double lengthBonus = 1 + 0.1f * Math.Min(1.0, totalHits / 1500.0);
+ double lengthBonus = 1 + 0.1 * Math.Min(1.0, totalHits / 1500.0);
strainValue *= lengthBonus;
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs
index 879e15c548..19dce303ea 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs
@@ -95,6 +95,19 @@ namespace osu.Game.Tests.Visual.Gameplay
seekAndAssertBreak("seek to break after end", testBreaks[1].EndTime + 500, false);
}
+ [TestCase(true)]
+ [TestCase(false)]
+ public void TestBeforeGameplayStart(bool withBreaks)
+ {
+ setClock(true);
+
+ if (withBreaks)
+ loadBreaksStep("multiple breaks", testBreaks);
+
+ seekAndAssertBreak("seek to break intro time", -100, true);
+ seekAndAssertBreak("seek to break intro time", 0, false);
+ }
+
private void addShowBreakStep(double seconds)
{
AddStep($"show '{seconds}s' break", () => breakOverlay.Breaks = new List
diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs
index 6fdee85f45..ee8be87352 100644
--- a/osu.Game/Screens/Play/BreakOverlay.cs
+++ b/osu.Game/Screens/Play/BreakOverlay.cs
@@ -1,4 +1,4 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// 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;
@@ -16,6 +16,8 @@ namespace osu.Game.Screens.Play
{
public class BreakOverlay : Container
{
+ private readonly ScoreProcessor scoreProcessor;
+
///
/// The duration of the break overlay fading.
///
@@ -60,9 +62,12 @@ namespace osu.Game.Screens.Play
private readonly RemainingTimeCounter remainingTimeCounter;
private readonly BreakInfo info;
private readonly BreakArrows breakArrows;
+ private readonly double gameplayStartTime;
- public BreakOverlay(bool letterboxing, ScoreProcessor scoreProcessor = null)
+ public BreakOverlay(bool letterboxing, double gameplayStartTime = 0, ScoreProcessor scoreProcessor = null)
{
+ this.gameplayStartTime = gameplayStartTime;
+ this.scoreProcessor = scoreProcessor;
RelativeSizeAxes = Axes.Both;
Child = fadeContainer = new Container
{
@@ -135,26 +140,34 @@ namespace osu.Game.Screens.Play
updateBreakTimeBindable();
}
- private void updateBreakTimeBindable()
+ private void updateBreakTimeBindable() =>
+ isBreakTime.Value = getCurrentBreak()?.HasEffect == true
+ || Clock.CurrentTime < gameplayStartTime
+ || scoreProcessor?.HasCompleted == true;
+
+ private BreakPeriod getCurrentBreak()
{
- if (breaks == null || breaks.Count == 0)
- return;
-
- var time = Clock.CurrentTime;
-
- if (time > breaks[CurrentBreakIndex].EndTime)
+ if (breaks?.Count > 0)
{
- while (time > breaks[CurrentBreakIndex].EndTime && CurrentBreakIndex < breaks.Count - 1)
- CurrentBreakIndex++;
- }
- else
- {
- while (time < breaks[CurrentBreakIndex].StartTime && CurrentBreakIndex > 0)
- CurrentBreakIndex--;
+ var time = Clock.CurrentTime;
+
+ if (time > breaks[CurrentBreakIndex].EndTime)
+ {
+ while (time > breaks[CurrentBreakIndex].EndTime && CurrentBreakIndex < breaks.Count - 1)
+ CurrentBreakIndex++;
+ }
+ else
+ {
+ while (time < breaks[CurrentBreakIndex].StartTime && CurrentBreakIndex > 0)
+ CurrentBreakIndex--;
+ }
+
+ var closest = breaks[CurrentBreakIndex];
+
+ return closest.Contains(time) ? closest : null;
}
- var currentBreak = breaks[CurrentBreakIndex];
- isBreakTime.Value = currentBreak.HasEffect && currentBreak.Contains(time);
+ return null;
}
private void initializeBreaks()
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index d6488dc209..7f32db2ebf 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -179,7 +179,7 @@ namespace osu.Game.Screens.Play
{
target.AddRange(new[]
{
- breakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
+ breakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, DrawableRuleset.GameplayStartTime, ScoreProcessor)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -468,7 +468,7 @@ namespace osu.Game.Screens.Play
PauseOverlay.Hide();
// breaks and time-based conditions may allow instant resume.
- if (breakOverlay.IsBreakTime.Value || GameplayClockContainer.GameplayClock.CurrentTime < Beatmap.Value.Beatmap.HitObjects.First().StartTime)
+ if (breakOverlay.IsBreakTime.Value)
completeResume();
else
DrawableRuleset.RequestResume(completeResume);
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 449b4dc4e3..2f633bdbc1 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -21,7 +21,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index b51f24c37c..b213d641db 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -74,7 +74,7 @@
-
+
@@ -82,7 +82,7 @@
-
+