1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-26 03:49:56 +08:00

Allow using previous valid score for offset calibration when subsequent retries are too short

As proposed in https://github.com/ppy/osu/discussions/33572.

Note that this won't work if you leave to song select. We could make
that work but it would require further global faffing and I don't think
it's worth the effort.
This commit is contained in:
Dean Herbert
2025-06-11 15:26:02 +09:00
Unverified
parent 4b8ac8a565
commit cdb2f216f2
2 changed files with 51 additions and 7 deletions
@@ -45,6 +45,44 @@ namespace osu.Game.Tests.Visual.Gameplay
AddAssert("No calibration button", () => !offsetControl.ChildrenOfType<SettingsButton>().Any());
}
/// <summary>
/// When a beatmap offset was already set, the calibration should take it into account.
/// </summary>
[Test]
public void TestTooShortToDisplay_HasPreviousValidScore()
{
const double average_error = -4.5;
const double initial_offset = -2;
AddStep("Set offset non-neutral", () => offsetControl.Current.Value = initial_offset);
AddAssert("No calibration button", () => !offsetControl.ChildrenOfType<SettingsButton>().Any());
AddStep("Set reference score", () =>
{
offsetControl.ReferenceScore.Value = new ScoreInfo
{
HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents(average_error),
BeatmapInfo = Beatmap.Value.BeatmapInfo,
};
});
AddUntilStep("Has calibration button", () => offsetControl.ChildrenOfType<SettingsButton>().Any());
AddStep("Set short reference score", () =>
{
offsetControl.ReferenceScore.Value = new ScoreInfo
{
HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents(0, 2),
BeatmapInfo = Beatmap.Value.BeatmapInfo,
};
});
AddUntilStep("Still calibration button", () => offsetControl.ChildrenOfType<SettingsButton>().Any());
AddStep("Press button", () => offsetControl.ChildrenOfType<SettingsButton>().Single().TriggerClick());
AddAssert("Offset is adjusted", () => offsetControl.Current.Value == initial_offset - average_error);
}
[Test]
public void TestNotEnoughTimedHitEvents()
{
@@ -69,6 +69,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
private IDisposable? beatmapOffsetSubscription;
private Task? realmWriteTask;
private ScoreInfo? lastValidScore;
public BeatmapOffsetControl()
{
@@ -177,8 +178,6 @@ namespace osu.Game.Screens.Play.PlayerSettings
private void scoreChanged(ValueChangedEvent<ScoreInfo?> score)
{
referenceScoreContainer.Clear();
if (score.NewValue == null)
return;
@@ -196,6 +195,15 @@ namespace osu.Game.Screens.Play.PlayerSettings
if (!(hitEvents.CalculateMedianHitError() is double median))
return;
// affecting unstable rate here is used as a substitute of determining if a hit event represents a *timed* hit event,
// i.e. an user input that the user had to *time to the track*,
// i.e. one that it *makes sense to use* when doing anything with timing and offsets.
bool hasEnoughUsableEvents = hitEvents.Count(HitEventExtensions.AffectsUnstableRate) >= 50;
// If we are already displaying a score, continue displaying it rather than showing the user "play too short" message.
if (lastValidScore != null && !hasEnoughUsableEvents)
return;
referenceScoreContainer.Children = new Drawable[]
{
new OsuSpriteText
@@ -204,10 +212,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
},
};
// affecting unstable rate here is used as a substitute of determining if a hit event represents a *timed* hit event,
// i.e. an user input that the user had to *time to the track*,
// i.e. one that it *makes sense to use* when doing anything with timing and offsets.
if (hitEvents.Count(HitEventExtensions.AffectsUnstableRate) < 50)
if (!hasEnoughUsableEvents)
{
referenceScoreContainer.AddRange(new Drawable[]
{
@@ -223,6 +228,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
return;
}
lastValidScore = score.NewValue!;
lastPlayMedian = median;
lastPlayBeatmapOffset = Current.Value;
@@ -245,7 +251,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
return;
Current.Value = lastPlayBeatmapOffset - lastPlayMedian;
lastAppliedScore.Value = ReferenceScore.Value;
lastAppliedScore.Value = lastValidScore;
},
},
globalOffsetText = new LinkFlowContainer