1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-24 02:09:54 +08:00

Disallow adjusting scroll speed during gameplay

Matches stable. Addresses https://github.com/ppy/osu/discussions/32670.
This commit is contained in:
Dean Herbert
2025-04-04 19:31:16 +09:00
Unverified
parent 2df3dfb99c
commit 32c60bfb36
6 changed files with 112 additions and 36 deletions
@@ -60,8 +60,9 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly BindableDouble configScrollSpeed = new BindableDouble();
private readonly Bindable<ManiaMobileLayout> mobileLayout = new Bindable<ManiaMobileLayout>();
public double TargetTimeRange { get; protected set; }
private double currentTimeRange;
protected double TargetTimeRange;
// Stores the current speed adjustment active in gameplay.
private readonly Track speedAdjustmentTrack = new TrackVirtual(0);
@@ -109,7 +110,13 @@ namespace osu.Game.Rulesets.Mania.UI
configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true);
Config.BindWith(ManiaRulesetSetting.ScrollSpeed, configScrollSpeed);
configScrollSpeed.BindValueChanged(speed => TargetTimeRange = ComputeScrollTime(speed.NewValue));
configScrollSpeed.BindValueChanged(speed =>
{
if (!AllowScrollSpeedAdjustment)
return;
TargetTimeRange = ComputeScrollTime(speed.NewValue);
});
TimeRange.Value = TargetTimeRange = currentTimeRange = ComputeScrollTime(configScrollSpeed.Value);
@@ -33,6 +33,10 @@ using osu.Game.Overlays.BeatmapListing;
using osu.Game.Overlays.Mods;
using osu.Game.Overlays.Notifications;
using osu.Game.Overlays.Toolbar;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Scoring;
@@ -394,6 +398,60 @@ namespace osu.Game.Tests.Visual.Navigation
}
}
[Test]
public void TestScrollSpeedAdjustDuringGameplay()
{
Player player = null;
Screens.Select.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
AddStep("import beatmap", () => BeatmapImportHelper.LoadOszIntoOsu(Game).WaitSafely());
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
AddStep("switch to mania ruleset", () =>
{
InputManager.PressKey(Key.LControl);
InputManager.Key(Key.Number4);
InputManager.ReleaseKey(Key.LControl);
});
AddStep("set mods", () => Game.SelectedMods.Value = new Mod[] { new OsuModNoFail() });
AddStep("press enter", () => InputManager.Key(Key.Enter));
AddUntilStep("wait for player", () =>
{
DismissAnyNotifications();
player = Game.ScreenStack.CurrentScreen as Player;
return player?.IsLoaded == true;
});
AddUntilStep("wait for track playing", () => Game.Beatmap.Value.Track.IsRunning);
checkScrollSpeed(8, 8);
AddStep("adjust scroll speed via keyboard", () => InputManager.Key(Key.F4));
checkScrollSpeed(9, 9);
AddStep("seek beyond 10 seconds", () => player.ChildrenOfType<GameplayClockContainer>().First().Seek(10500));
AddUntilStep("wait for seek", () => player.ChildrenOfType<GameplayClockContainer>().First().CurrentTime, () => Is.GreaterThan(10600));
AddStep("attempt adjust offset via keyboard", () => InputManager.Key(Key.F4));
checkScrollSpeed(9, 9);
AddStep("attempt adjust offset via config change", () => getConfigManager().SetValue(ManiaRulesetSetting.ScrollSpeed, 10.0));
checkScrollSpeed(10, 9);
void checkScrollSpeed(double configValue, double gameplayValue)
{
AddUntilStep($"config value is {configValue}", () => getConfigManager().Get<double>(ManiaRulesetSetting.ScrollSpeed), () => Is.EqualTo(configValue));
AddUntilStep($"gameplay value is {gameplayValue}", () => this.ChildrenOfType<DrawableManiaRuleset>().Single().TargetTimeRange,
() => Is.EqualTo(DrawableManiaRuleset.ComputeScrollTime(gameplayValue)));
}
ManiaRulesetConfigManager getConfigManager() => ((ManiaRulesetConfigManager)Game.Dependencies.Get<IRulesetConfigCache>().GetConfigFor(new ManiaRuleset())!);
}
[Test]
public void TestOffsetAdjustDuringGameplay()
{
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
@@ -21,6 +19,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Timing;
using osu.Game.Rulesets.UI.Scrolling.Algorithms;
using osu.Game.Screens.Play;
namespace osu.Game.Rulesets.UI.Scrolling
{
@@ -69,6 +68,12 @@ namespace osu.Game.Rulesets.UI.Scrolling
/// </summary>
protected virtual bool UserScrollSpeedAdjustment => true;
/// <summary>
/// Whether at the current point in time, whether scroll speed adjustments should be applied to gameplay.
/// This can potentially become false at some point during gameplay for game balance reasons.
/// </summary>
protected bool AllowScrollSpeedAdjustment => UserScrollSpeedAdjustment && player?.AllowCriticalSettingsAdjustment != false;
/// <summary>
/// Whether <see cref="TimingControlPoint"/> beat lengths should scale relative to the most common beat length in the <see cref="Beatmap"/>.
/// </summary>
@@ -84,7 +89,10 @@ namespace osu.Game.Rulesets.UI.Scrolling
[Cached(Type = typeof(IScrollingInfo))]
private readonly LocalScrollingInfo scrollingInfo;
protected DrawableScrollingRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
[Resolved]
private Player? player { get; set; }
protected DrawableScrollingRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod>? mods = null)
: base(ruleset, beatmap, mods)
{
scrollingInfo = new LocalScrollingInfo();
@@ -195,28 +203,30 @@ namespace osu.Game.Rulesets.UI.Scrolling
/// Adjusts the scroll speed of <see cref="HitObject"/>s.
/// </summary>
/// <param name="amount">The amount to adjust by. Greater than 0 if the scroll speed should be increased, less than 0 if it should be decreased.</param>
protected virtual void AdjustScrollSpeed(int amount) => this.TransformBindableTo(TimeRange, TimeRange.Value - amount * time_span_step, 200, Easing.OutQuint);
protected virtual void AdjustScrollSpeed(int amount)
{
this.TransformBindableTo(TimeRange, TimeRange.Value - amount * time_span_step, 200, Easing.OutQuint);
}
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
{
if (!UserScrollSpeedAdjustment)
return false;
switch (e.Action)
{
case GlobalAction.IncreaseScrollSpeed:
AdjustScrollSpeed(1);
if (AllowScrollSpeedAdjustment)
AdjustScrollSpeed(1);
return true;
case GlobalAction.DecreaseScrollSpeed:
AdjustScrollSpeed(-1);
if (AllowScrollSpeedAdjustment)
AdjustScrollSpeed(-1);
return true;
}
return false;
}
private ScheduledDelegate scheduledScrollSpeedAdjustment;
private ScheduledDelegate? scheduledScrollSpeedAdjustment;
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
{
+6
View File
@@ -112,6 +112,12 @@ namespace osu.Game.Screens.Play
/// </summary>
public IBindable<bool> ShowingOverlayComponents = new Bindable<bool>();
/// <summary>
/// A flag which can be checked to decide whether we are in a state where settings that affect
/// game balance should be allowed to be applied at the current point in time.
/// </summary>
public virtual bool AllowCriticalSettingsAdjustment { get; } = true;
// Should match PlayerLoader for consistency. Cached here for the rare case we push a Player
// without the loading screen (one such usage is the skin editor's scene library).
[Cached]
@@ -60,9 +60,6 @@ namespace osu.Game.Screens.Play.PlayerSettings
[Resolved]
private Player? player { get; set; }
[Resolved]
private IGameplayClock? gameplayClock { get; set; }
private double lastPlayMedian;
private double lastPlayBeatmapOffset;
private HitEventTimingDistributionGraph? lastPlayGraph;
@@ -287,27 +284,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
Current.Disabled = !allow;
}
private bool allowOffsetAdjust
{
get
{
// General limitations to ensure players don't do anything too weird.
// These match stable for now.
if (player is SubmittingPlayer)
{
Debug.Assert(gameplayClock != null);
// TODO: the blocking conditions should probably display a message.
if (!player.IsBreakTime.Value && gameplayClock.CurrentTime - gameplayClock.GameplayStartTime > 10000)
return false;
if (gameplayClock.IsPaused.Value)
return false;
}
return true;
}
}
private bool allowOffsetAdjust => player?.AllowCriticalSettingsAdjustment != false;
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
{
+18
View File
@@ -186,6 +186,24 @@ namespace osu.Game.Screens.Play
/// <returns>Whether gameplay should be immediately exited as a result. Returning false allows the gameplay session to continue. Defaults to true.</returns>
protected virtual bool ShouldExitOnTokenRetrievalFailure(Exception exception) => true;
public override bool AllowCriticalSettingsAdjustment
{
get
{
// General limitations to ensure players don't do anything too weird.
// These match stable for now.
// TODO: the blocking conditions should probably display a message.
if (!IsBreakTime.Value && GameplayClockContainer.CurrentTime - GameplayClockContainer.GameplayStartTime > 10000)
return false;
if (GameplayClockContainer.IsPaused.Value)
return false;
return base.AllowCriticalSettingsAdjustment;
}
}
protected override async Task PrepareScoreForResultsAsync(Score score)
{
await base.PrepareScoreForResultsAsync(score).ConfigureAwait(false);