diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapOffsetControl.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapOffsetControl.cs new file mode 100644 index 0000000000..67f5db548b --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapOffsetControl.cs @@ -0,0 +1,74 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Game.Overlays.Settings; +using osu.Game.Scoring; +using osu.Game.Screens.Play.PlayerSettings; +using osu.Game.Tests.Visual.Ranking; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneBeatmapOffsetControl : OsuTestScene + { + private BeatmapOffsetControl offsetControl; + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Create control", () => + { + Child = new PlayerSettingsGroup("Some settings") + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] + { + offsetControl = new BeatmapOffsetControl() + } + }; + }); + } + + [Test] + public void TestTooShortToDisplay() + { + AddStep("Set short reference score", () => + { + offsetControl.ReferenceScore.Value = new ScoreInfo + { + HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents(0, 2) + }; + }); + + AddAssert("No calibration button", () => !offsetControl.ChildrenOfType().Any()); + } + + [Test] + public void TestDisplay() + { + const double average_error = -4.5; + + AddAssert("Offset is neutral", () => offsetControl.Current.Value == 0); + AddAssert("No calibration button", () => !offsetControl.ChildrenOfType().Any()); + AddStep("Set reference score", () => + { + offsetControl.ReferenceScore.Value = new ScoreInfo + { + HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents(average_error) + }; + }); + + AddAssert("Has calibration button", () => offsetControl.ChildrenOfType().Any()); + AddStep("Press button", () => offsetControl.ChildrenOfType().Single().TriggerClick()); + AddAssert("Offset is adjusted", () => offsetControl.Current.Value == -average_error); + + AddAssert("Button is disabled", () => !offsetControl.ChildrenOfType().Single().Enabled.Value); + AddStep("Remove reference score", () => offsetControl.ReferenceScore.Value = null); + AddAssert("No calibration button", () => !offsetControl.ChildrenOfType().Any()); + } + } +} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs index e03c8d7561..b195d2aa74 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs @@ -131,9 +131,9 @@ namespace osu.Game.Tests.Visual.Gameplay public double GameplayClockTime => GameplayClockContainer.GameplayClock.CurrentTime; - protected override void Update() + protected override void UpdateAfterChildren() { - base.Update(); + base.UpdateAfterChildren(); if (!FirstFrameClockTime.HasValue) { diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs b/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs index 4bc843096f..221001e40b 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs @@ -71,16 +71,16 @@ namespace osu.Game.Tests.Visual.Ranking }; }); - public static List CreateDistributedHitEvents() + public static List CreateDistributedHitEvents(double centre = 0, double range = 25) { var hitEvents = new List(); - for (int i = 0; i < 50; i++) + for (int i = 0; i < range * 2; i++) { - int count = (int)(Math.Pow(25 - Math.Abs(i - 25), 2)); + int count = (int)(Math.Pow(range - Math.Abs(i - range), 2)); for (int j = 0; j < count; j++) - hitEvents.Add(new HitEvent(i - 25, HitResult.Perfect, new HitCircle(), new HitCircle(), null)); + hitEvents.Add(new HitEvent(centre + i - range, HitResult.Perfect, new HitCircle(), new HitCircle(), null)); } return hitEvents; diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 305b3979a0..c6f69286cd 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -40,6 +40,8 @@ namespace osu.Game.Beatmaps [Backlink(nameof(ScoreInfo.BeatmapInfo))] public IQueryable Scores { get; } = null!; + public BeatmapUserSettings UserSettings { get; set; } = null!; + public BeatmapInfo(RulesetInfo? ruleset = null, BeatmapDifficulty? difficulty = null, BeatmapMetadata? metadata = null) { ID = Guid.NewGuid(); @@ -51,6 +53,7 @@ namespace osu.Game.Beatmaps }; Difficulty = difficulty ?? new BeatmapDifficulty(); Metadata = metadata ?? new BeatmapMetadata(); + UserSettings = new BeatmapUserSettings(); } [UsedImplicitly] diff --git a/osu.Game/Beatmaps/BeatmapUserSettings.cs b/osu.Game/Beatmaps/BeatmapUserSettings.cs new file mode 100644 index 0000000000..5c71bf34b1 --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapUserSettings.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable +using Realms; + +namespace osu.Game.Beatmaps +{ + /// + /// User settings overrides that are attached to a beatmap. + /// + public class BeatmapUserSettings : EmbeddedObject + { + /// + /// An audio offset that can be used for timing adjustments. + /// + public double Offset { get; set; } + } +} diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index bf2b48ea52..fb3052d850 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -54,8 +54,9 @@ namespace osu.Game.Database /// 11 2021-11-22 Use ShortName instead of RulesetID for ruleset key bindings. /// 12 2021-11-24 Add Status to RealmBeatmapSet. /// 13 2022-01-13 Final migration of beatmaps and scores to realm (multiple new storage fields). + /// 14 2022-03-01 Added BeatmapUserSettings to BeatmapInfo. /// - private const int schema_version = 13; + private const int schema_version = 14; /// /// Lock object which is held during sections, blocking realm retrieval during blocking periods. @@ -564,6 +565,11 @@ namespace osu.Game.Database } break; + + case 14: + foreach (var beatmap in migration.NewRealm.All()) + beatmap.UserSettings = new BeatmapUserSettings(); + break; } } diff --git a/osu.Game/Database/RealmObjectExtensions.cs b/osu.Game/Database/RealmObjectExtensions.cs index f89bbbe19d..6dc18df9e0 100644 --- a/osu.Game/Database/RealmObjectExtensions.cs +++ b/osu.Game/Database/RealmObjectExtensions.cs @@ -38,6 +38,7 @@ namespace osu.Game.Database c.CreateMap() .ForMember(s => s.Ruleset, cc => cc.Ignore()) .ForMember(s => s.Metadata, cc => cc.Ignore()) + .ForMember(s => s.UserSettings, cc => cc.Ignore()) .ForMember(s => s.Difficulty, cc => cc.Ignore()) .ForMember(s => s.BeatmapSet, cc => cc.Ignore()) .AfterMap((s, d) => @@ -154,6 +155,7 @@ namespace osu.Game.Database c.CreateMap(); c.CreateMap(); + c.CreateMap(); c.CreateMap(); c.CreateMap(); c.CreateMap(); diff --git a/osu.Game/Localisation/BeatmapOffsetControlStrings.cs b/osu.Game/Localisation/BeatmapOffsetControlStrings.cs new file mode 100644 index 0000000000..7b2a9e50b2 --- /dev/null +++ b/osu.Game/Localisation/BeatmapOffsetControlStrings.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.Localisation; + +namespace osu.Game.Localisation +{ + public static class BeatmapOffsetControlStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.BeatmapOffsetControl"; + + /// + /// "Beatmap offset" + /// + public static LocalisableString BeatmapOffset => new TranslatableString(getKey(@"beatmap_offset"), @"Beatmap offset"); + + /// + /// "Previous play:" + /// + public static LocalisableString PreviousPlay => new TranslatableString(getKey(@"previous_play"), @"Previous play:"); + + /// + /// "Previous play too short to use for calibration" + /// + public static LocalisableString PreviousPlayTooShortToUseForCalibration => new TranslatableString(getKey(@"previous_play_too_short_to_use_for_calibration"), @"Previous play too short to use for calibration"); + + /// + /// "Calibrate using last play" + /// + public static LocalisableString CalibrateUsingLastPlay => new TranslatableString(getKey(@"calibrate_using_last_play"), @"Calibrate using last play"); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Scoring/HitEventExtensions.cs b/osu.Game/Rulesets/Scoring/HitEventExtensions.cs index 637d0a872a..fea13cf4b6 100644 --- a/osu.Game/Rulesets/Scoring/HitEventExtensions.cs +++ b/osu.Game/Rulesets/Scoring/HitEventExtensions.cs @@ -29,8 +29,15 @@ namespace osu.Game.Rulesets.Scoring /// A non-null value if unstable rate could be calculated, /// and if unstable rate cannot be calculated due to being empty. /// - public static double? CalculateAverageHitError(this IEnumerable hitEvents) => - hitEvents.Where(affectsUnstableRate).Select(ev => ev.TimeOffset).Average(); + public static double? CalculateAverageHitError(this IEnumerable hitEvents) + { + double[] timeOffsets = hitEvents.Where(affectsUnstableRate).Select(ev => ev.TimeOffset).ToArray(); + + if (timeOffsets.Length == 0) + return null; + + return timeOffsets.Average(); + } private static bool affectsUnstableRate(HitEvent e) => !(e.HitObject.HitWindows is HitWindows.EmptyHitWindows) && e.Result.IsHit(); diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 200921680e..2b6db5f59e 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using osu.Framework; using osu.Framework.Allocation; @@ -13,6 +14,7 @@ using osu.Framework.Graphics; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Database; namespace osu.Game.Screens.Play { @@ -43,7 +45,7 @@ namespace osu.Game.Screens.Play Precision = 0.1, }; - private double totalAppliedOffset => userOffsetClock.RateAdjustedOffset + platformOffsetClock.RateAdjustedOffset; + private double totalAppliedOffset => userBeatmapOffsetClock.RateAdjustedOffset + userGlobalOffsetClock.RateAdjustedOffset + platformOffsetClock.RateAdjustedOffset; private readonly BindableDouble pauseFreqAdjust = new BindableDouble(1); @@ -52,12 +54,21 @@ namespace osu.Game.Screens.Play private readonly bool startAtGameplayStart; private readonly double firstHitObjectTime; - private HardwareCorrectionOffsetClock userOffsetClock; + private HardwareCorrectionOffsetClock userGlobalOffsetClock; + private HardwareCorrectionOffsetClock userBeatmapOffsetClock; private HardwareCorrectionOffsetClock platformOffsetClock; private MasterGameplayClock masterGameplayClock; private Bindable userAudioOffset; private double startOffset; + private IDisposable beatmapOffsetSubscription; + + [Resolved] + private RealmAccess realm { get; set; } + + [Resolved] + private OsuConfigManager config { get; set; } + public MasterGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStartTime, bool startAtGameplayStart = false) : base(beatmap.Track) { @@ -68,11 +79,33 @@ namespace osu.Game.Screens.Play firstHitObjectTime = beatmap.Beatmap.HitObjects.First().StartTime; } - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + protected override void LoadComplete() { + base.LoadComplete(); + userAudioOffset = config.GetBindable(OsuSetting.AudioOffset); - userAudioOffset.BindValueChanged(offset => userOffsetClock.Offset = offset.NewValue, true); + userAudioOffset.BindValueChanged(offset => userGlobalOffsetClock.Offset = offset.NewValue, true); + + beatmapOffsetSubscription = realm.RegisterCustomSubscription(r => + { + var userSettings = r.Find(beatmap.BeatmapInfo.ID)?.UserSettings; + + if (userSettings == null) // only the case for tests. + return null; + + void onUserSettingsOnPropertyChanged(object sender, PropertyChangedEventArgs args) + { + if (args.PropertyName == nameof(BeatmapUserSettings.Offset)) + updateOffset(); + } + + updateOffset(); + userSettings.PropertyChanged += onUserSettingsOnPropertyChanged; + + return new InvokeOnDisposal(() => userSettings.PropertyChanged -= onUserSettingsOnPropertyChanged); + + void updateOffset() => userBeatmapOffsetClock.Offset = userSettings.Offset; + }); // sane default provided by ruleset. startOffset = gameplayStartTime; @@ -161,9 +194,10 @@ namespace osu.Game.Screens.Play platformOffsetClock = new HardwareCorrectionOffsetClock(source, pauseFreqAdjust) { Offset = RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? 15 : 0 }; // the final usable gameplay clock with user-set offsets applied. - userOffsetClock = new HardwareCorrectionOffsetClock(platformOffsetClock, pauseFreqAdjust); + userGlobalOffsetClock = new HardwareCorrectionOffsetClock(platformOffsetClock, pauseFreqAdjust); + userBeatmapOffsetClock = new HardwareCorrectionOffsetClock(userGlobalOffsetClock, pauseFreqAdjust); - return masterGameplayClock = new MasterGameplayClock(userOffsetClock); + return masterGameplayClock = new MasterGameplayClock(userBeatmapOffsetClock); } /// @@ -209,6 +243,7 @@ namespace osu.Game.Screens.Play protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); + beatmapOffsetSubscription?.Dispose(); removeSourceClockAdjustments(); } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index d4b02622d3..86ea412488 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -136,7 +136,11 @@ namespace osu.Game.Screens.Play public readonly PlayerConfiguration Configuration; - protected Score Score { get; private set; } + /// + /// The score for the current play session. + /// Available only after the player is loaded. + /// + public Score Score { get; private set; } /// /// Create a new player instance. diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 6009c85583..f6d63a8ec5 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -61,6 +61,8 @@ namespace osu.Game.Screens.Play protected VisualSettings VisualSettings { get; private set; } + protected AudioSettings AudioSettings { get; private set; } + protected Task LoadTask { get; private set; } protected Task DisposalTask { get; private set; } @@ -167,6 +169,7 @@ namespace osu.Game.Screens.Play Children = new PlayerSettingsGroup[] { VisualSettings = new VisualSettings(), + AudioSettings = new AudioSettings(), new InputSettings() } }, @@ -225,6 +228,10 @@ namespace osu.Game.Screens.Play { base.OnResuming(last); + var lastScore = player.Score; + + AudioSettings.ReferenceScore.Value = lastScore?.ScoreInfo; + // prepare for a retry. player = null; playerConsumed = false; diff --git a/osu.Game/Screens/Play/PlayerSettings/AudioSettings.cs b/osu.Game/Screens/Play/PlayerSettings/AudioSettings.cs new file mode 100644 index 0000000000..32de5333e1 --- /dev/null +++ b/osu.Game/Screens/Play/PlayerSettings/AudioSettings.cs @@ -0,0 +1,37 @@ +// 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.Configuration; +using osu.Game.Scoring; + +namespace osu.Game.Screens.Play.PlayerSettings +{ + public class AudioSettings : PlayerSettingsGroup + { + public Bindable ReferenceScore { get; } = new Bindable(); + + private readonly PlayerCheckbox beatmapHitsoundsToggle; + + public AudioSettings() + : base("Audio Settings") + { + Children = new Drawable[] + { + beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hitsounds" }, + new BeatmapOffsetControl + { + ReferenceScore = { BindTarget = ReferenceScore }, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + beatmapHitsoundsToggle.Current = config.GetBindable(OsuSetting.BeatmapHitsounds); + } + } +} diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs new file mode 100644 index 0000000000..dc3e80d695 --- /dev/null +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -0,0 +1,213 @@ +// 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.ComponentModel; +using System.Linq; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Localisation; +using osu.Game.Overlays.Settings; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; +using osu.Game.Screens.Ranking.Statistics; +using osuTK; + +#nullable enable + +namespace osu.Game.Screens.Play.PlayerSettings +{ + public class BeatmapOffsetControl : CompositeDrawable + { + public Bindable ReferenceScore { get; } = new Bindable(); + + public BindableDouble Current { get; } = new BindableDouble + { + Default = 0, + Value = 0, + MinValue = -50, + MaxValue = 50, + Precision = 0.1, + }; + + private readonly FillFlowContainer referenceScoreContainer; + + [Resolved] + private RealmAccess realm { get; set; } = null!; + + [Resolved] + private IBindable beatmap { get; set; } = null!; + + [Resolved] + private OsuColour colours { get; set; } = null!; + + private double lastPlayAverage; + + private SettingsButton? useAverageButton; + + private IDisposable? beatmapOffsetSubscription; + + private Task? realmWriteTask; + + public BeatmapOffsetControl() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + InternalChild = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(10), + Children = new Drawable[] + { + new PlayerSliderBar + { + KeyboardStep = 5, + LabelText = BeatmapOffsetControlStrings.BeatmapOffset, + Current = Current, + }, + referenceScoreContainer = new FillFlowContainer + { + Spacing = new Vector2(10), + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + ReferenceScore.BindValueChanged(scoreChanged, true); + + beatmapOffsetSubscription = realm.RegisterCustomSubscription(r => + { + var userSettings = r.Find(beatmap.Value.BeatmapInfo.ID)?.UserSettings; + + if (userSettings == null) // only the case for tests. + return null; + + Current.Value = userSettings.Offset; + userSettings.PropertyChanged += onUserSettingsOnPropertyChanged; + + return new InvokeOnDisposal(() => userSettings.PropertyChanged -= onUserSettingsOnPropertyChanged); + + void onUserSettingsOnPropertyChanged(object sender, PropertyChangedEventArgs args) + { + if (args.PropertyName == nameof(BeatmapUserSettings.Offset)) + Current.Value = userSettings.Offset; + } + }); + + Current.BindValueChanged(currentChanged); + } + + private void currentChanged(ValueChangedEvent offset) + { + Scheduler.AddOnce(updateOffset); + + void updateOffset() + { + // ensure the previous write has completed. ignoring performance concerns, if we don't do this, the async writes could be out of sequence. + if (realmWriteTask?.IsCompleted == false) + { + Scheduler.AddOnce(updateOffset); + return; + } + + if (useAverageButton != null) + useAverageButton.Enabled.Value = !Precision.AlmostEquals(lastPlayAverage, -Current.Value, Current.Precision / 2); + + realmWriteTask = realm.WriteAsync(r => + { + var settings = r.Find(beatmap.Value.BeatmapInfo.ID)?.UserSettings; + + if (settings == null) // only the case for tests. + return; + + if (settings.Offset == Current.Value) + return; + + settings.Offset = Current.Value; + }); + } + } + + private void scoreChanged(ValueChangedEvent score) + { + referenceScoreContainer.Clear(); + + if (score.NewValue == null) + return; + + if (score.NewValue.Mods.Any(m => !m.UserPlayable)) + return; + + var hitEvents = score.NewValue.HitEvents; + + if (!(hitEvents.CalculateAverageHitError() is double average)) + return; + + referenceScoreContainer.Children = new Drawable[] + { + new OsuSpriteText + { + Text = BeatmapOffsetControlStrings.PreviousPlay + }, + }; + + if (hitEvents.Count < 10) + { + referenceScoreContainer.AddRange(new Drawable[] + { + new OsuTextFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Colour = colours.Red1, + Text = BeatmapOffsetControlStrings.PreviousPlayTooShortToUseForCalibration + }, + }); + + return; + } + + lastPlayAverage = average; + + referenceScoreContainer.AddRange(new Drawable[] + { + new HitEventTimingDistributionGraph(hitEvents) + { + RelativeSizeAxes = Axes.X, + Height = 50, + }, + new AverageHitError(hitEvents), + useAverageButton = new SettingsButton + { + Text = BeatmapOffsetControlStrings.CalibrateUsingLastPlay, + Action = () => Current.Value = -lastPlayAverage + }, + }); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + beatmapOffsetSubscription?.Dispose(); + } + } +} diff --git a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs index a97078c461..81950efa9e 100644 --- a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs @@ -15,7 +15,6 @@ namespace osu.Game.Screens.Play.PlayerSettings private readonly PlayerCheckbox showStoryboardToggle; private readonly PlayerCheckbox beatmapSkinsToggle; private readonly PlayerCheckbox beatmapColorsToggle; - private readonly PlayerCheckbox beatmapHitsoundsToggle; public VisualSettings() : base("Visual Settings") @@ -45,7 +44,6 @@ namespace osu.Game.Screens.Play.PlayerSettings showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboard / Video" }, beatmapSkinsToggle = new PlayerCheckbox { LabelText = "Beatmap skins" }, beatmapColorsToggle = new PlayerCheckbox { LabelText = "Beatmap colours" }, - beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hitsounds" } }; } @@ -57,7 +55,6 @@ namespace osu.Game.Screens.Play.PlayerSettings showStoryboardToggle.Current = config.GetBindable(OsuSetting.ShowStoryboard); beatmapSkinsToggle.Current = config.GetBindable(OsuSetting.BeatmapSkins); beatmapColorsToggle.Current = config.GetBindable(OsuSetting.BeatmapColours); - beatmapHitsoundsToggle.Current = config.GetBindable(OsuSetting.BeatmapHitsounds); } } }