From e85869e0b48e7d683933ed821f6ec4baa5ea57a3 Mon Sep 17 00:00:00 2001 From: Aurelian Date: Sun, 2 Jun 2024 16:38:58 +0200 Subject: [PATCH 01/20] 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 8cdbd97ecb7c..cc0d1956267b 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.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 @@ public partial class TapTimingControl : CompositeDrawable private MetronomeDisplay metronome = null!; + private LabelledSwitchButton adjustPlacedNotes = null!; + [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider, OsuColour colours) { @@ -59,6 +65,7 @@ private void load(OverlayColourProvider colourProvider, OsuColour colours) { 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 @@ private void load(OverlayColourProvider colourProvider, OsuColour colours) }, }, 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 @@ private void reset() 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 @@ private void adjustOffset(double adjust) 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 @@ private void adjustOffset(double adjust) // 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 @@ private void adjustBpm(double adjust) 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 02/20] 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 cc0d1956267b..c4d67eaf1a97 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 @@ private List hitObjectsInTimingRange(EditorBeatmap beatmap, ControlPo 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 03/20] 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 c4d67eaf1a97..952bdf0ccce0 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -270,7 +270,7 @@ private void adjustBpm(double adjust) { 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 04/20] 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 952bdf0ccce0..6ef7bd1a4041 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 @@ private void reset() 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 05/20] 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 6ef7bd1a4041..cffa31d117b3 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.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 @@ private void adjustBpm(double adjust) 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 06/20] 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 cffa31d117b3..739b9894ce07 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -252,6 +252,9 @@ private void adjustOffset(double adjust) 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 @@ private void adjustBpm(double adjust) } 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 07/20] 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 739b9894ce07..1c7f89c80d16 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -254,7 +254,7 @@ private void adjustOffset(double adjust) 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 @@ private void adjustBpm(double adjust) 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 08/20] 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 4e69486e2d25..a3224a6ab9a0 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 @@ public bool Equals(TimingControlPoint? other) && 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 1c7f89c80d16..49d3df4aefa7 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -211,15 +211,6 @@ private void reset() 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 @@ private void adjustOffset(double adjust) 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 @@ private void adjustOffset(double adjust) 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 @@ private void adjustBpm(double 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; - - 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 09/20] 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 a3224a6ab9a0..234a34dc878c 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -128,16 +128,16 @@ public void AdjustHitObjectOffset(IBeatmap beatmap, double adjust) } } - 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 6ad12f54df2e..b604fc388974 100644 --- a/osu.Game/Localisation/EditorStrings.cs +++ b/osu.Game/Localisation/EditorStrings.cs @@ -39,6 +39,11 @@ public static class EditorStrings /// 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 07c32983f5f3..af099413fd17 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -367,7 +367,11 @@ private void load(OsuConfigManager config) { 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 7a3ea474fb91..42a3a6d3b225 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -88,6 +88,8 @@ public partial class EditorBeatmap : TransactionalCommitComponent, IBeatmap, IBe 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 49d3df4aefa7..30e591941182 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -37,8 +37,6 @@ public partial class TapTimingControl : CompositeDrawable private MetronomeDisplay metronome = null!; - private LabelledSwitchButton adjustPlacedNotes = null!; - [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider, OsuColour colours) { @@ -65,7 +63,6 @@ private void load(OverlayColourProvider colourProvider, OsuColour colours) { 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 @@ private void load(OverlayColourProvider colourProvider, OsuColour colours) }, }, 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 @@ private void adjustOffset(double adjust) 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 @@ private void adjustBpm(double adjust) if (timing == null) return; - double newBeatLength = 60000 / (timing.BPM + adjust); - - if (adjustPlacedNotes.Current.Value) - timing.SetHitObjectBPM(beatmap, newBeatLength); + timing.BeatLength = 60000 / (timing.BPM + adjust); - 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 10/20] 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 487a871881e7..7d0eab1f7fc9 100644 --- a/osu.Game/Screens/Edit/Timing/GroupSection.cs +++ b/osu.Game/Screens/Edit/Timing/GroupSection.cs @@ -109,11 +109,18 @@ private void changeSelectedGroupTime(in double time) 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 30e591941182..937235c7bcc5 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.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 @@ private void adjustOffset(double adjust) 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 2757753b0729..24cf865ce39e 100644 --- a/osu.Game/Screens/Edit/Timing/TimingSection.cs +++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs @@ -42,6 +42,17 @@ void saveChanges() { 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 11/20] 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 24cf865ce39e..1ae85e6c9ea1 100644 --- a/osu.Game/Screens/Edit/Timing/TimingSection.cs +++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs @@ -45,7 +45,7 @@ void saveChanges() bpmTextEntry.Bindable.BindValueChanged(val => { - if (ControlPoint.Value == null) + if (!Beatmap.AdjustNotesOnOffsetBPMChange.Value || ControlPoint.Value == null) return; ChangeHandler?.BeginChange(); From 9906ab3449719c0d01b3f847edbabe73359a3d95 Mon Sep 17 00:00:00 2001 From: Aurelian Date: Wed, 12 Jun 2024 19:25:48 +0200 Subject: [PATCH 12/20] 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 937235c7bcc5..55404702acf3 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -231,9 +231,6 @@ private void adjustBpm(double adjust) 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 1ae85e6c9ea1..5be1de467d49 100644 --- a/osu.Game/Screens/Edit/Timing/TimingSection.cs +++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs @@ -48,10 +48,8 @@ void saveChanges() 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 13/20] 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 234a34dc878c..4e69486e2d25 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 @@ public bool Equals(TimingControlPoint? other) && 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 7d0eab1f7fc9..c5a4422192a5 100644 --- a/osu.Game/Screens/Edit/Timing/GroupSection.cs +++ b/osu.Game/Screens/Edit/Timing/GroupSection.cs @@ -112,7 +112,7 @@ private void changeSelectedGroupTime(in double time) { // 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 55404702acf3..f2e37369d1f8 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -209,7 +209,7 @@ private void adjustOffset(double adjust) 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 5be1de467d49..ba335d55eb24 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 @@ void saveChanges() 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 @@ public Bindable Bindable 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 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 14/20] 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 4b47ecaab623..139f53a961a9 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 @@ public Bindable Bindable 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 000000000000..11c4be4790cb --- /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 15/20] 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 000000000000..2cb9e72c1ffd --- /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 11c4be4790cb..65edc47ff526 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 16/20] 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 2cb9e72c1ffd..5f5a1760ea8e 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 17/20] 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 18a222f4147c..abcdf7e4ff19 100644 --- a/osu.Game/Screens/Edit/Timing/GroupSection.cs +++ b/osu.Game/Screens/Edit/Timing/GroupSection.cs @@ -113,15 +113,17 @@ private void changeSelectedGroupTime(in double time) { // 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 f2e37369d1f8..91a0a43d62d0 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -202,6 +202,7 @@ private void adjustOffset(double adjust) // 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 @@ private void adjustOffset(double adjust) 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 @@ private void adjustBpm(double adjust) 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 139f53a961a9..e1567d65aaa3 100644 --- a/osu.Game/Screens/Edit/Timing/TimingSection.cs +++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs @@ -48,8 +48,10 @@ void saveChanges() 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 18/20] 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 e1567d65aaa3..e668120d0d4d 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 @@ void saveChanges() 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 @@ protected override TimingControlPoint CreatePoint() private partial class BPMTextBox : LabelledTextBox { + public new Action? OnCommit { get; set; } + private readonly BindableNumber beatLengthBindable = new TimingControlPoint().BeatLengthBindable; public BPMTextBox() @@ -92,10 +95,12 @@ public BPMTextBox() 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 @@ public BPMTextBox() // 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 0cddb93dda4275c592436c71f07c2e514e1635da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Nov 2024 09:57:17 +0100 Subject: [PATCH 19/20] Move setting to user config --- osu.Game/Configuration/OsuConfigManager.cs | 2 ++ osu.Game/Localisation/EditorStrings.cs | 4 ++-- osu.Game/Screens/Edit/Editor.cs | 4 ++-- osu.Game/Screens/Edit/EditorBeatmap.cs | 2 -- osu.Game/Screens/Edit/Timing/GroupSection.cs | 6 +++++- osu.Game/Screens/Edit/Timing/TapTimingControl.cs | 8 ++++++-- osu.Game/Screens/Edit/Timing/TimingSection.cs | 6 +++++- 7 files changed, 22 insertions(+), 10 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index f642d23bb03c..af6fd61a3d1b 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -196,6 +196,7 @@ protected override void InitialiseDefaults() SetDefault(OsuSetting.EditorShowSpeedChanges, false); SetDefault(OsuSetting.EditorScaleOrigin, EditorOrigin.GridCentre); SetDefault(OsuSetting.EditorRotationOrigin, EditorOrigin.GridCentre); + SetDefault(OsuSetting.EditorAdjustExistingObjectsOnTimingChanges, true); SetDefault(OsuSetting.HideCountryFlags, false); @@ -442,5 +443,6 @@ public enum OsuSetting EditorScaleOrigin, EditorRotationOrigin, EditorTimelineShowBreaks, + EditorAdjustExistingObjectsOnTimingChanges, } } diff --git a/osu.Game/Localisation/EditorStrings.cs b/osu.Game/Localisation/EditorStrings.cs index 19b783de9222..127bdd835519 100644 --- a/osu.Game/Localisation/EditorStrings.cs +++ b/osu.Game/Localisation/EditorStrings.cs @@ -40,9 +40,9 @@ public static class EditorStrings 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" + /// "Move already placed objects when changing timing" /// - public static LocalisableString AdjustNotesOnOffsetBPMChange => new TranslatableString(getKey(@"adjust_notes_on_offset_bpm_change"), @"Move already placed notes when changing the offset / BPM"); + public static LocalisableString AdjustExistingObjectsOnTimingChanges => new TranslatableString(getKey(@"adjust_existing_objects_on_timing_changes"), @"Move already placed objects when changing timing"); /// /// "For editing (.olz)" diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 178b3f57b04c..8de0c7c33d4e 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -422,9 +422,9 @@ private void load(OsuConfigManager config) Items = new MenuItem[] { new EditorMenuItem(EditorStrings.SetPreviewPointToCurrent, MenuItemType.Standard, SetPreviewPointToCurrentTime), - new ToggleMenuItem(EditorStrings.AdjustNotesOnOffsetBPMChange) + new ToggleMenuItem(EditorStrings.AdjustExistingObjectsOnTimingChanges) { - State = { BindTarget = editorBeatmap.AdjustNotesOnOffsetBPMChange }, + State = { BindTarget = config.GetBindable(OsuSetting.EditorAdjustExistingObjectsOnTimingChanges) }, } } } diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 154ebf3c8898..ad31c2ccc324 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -89,8 +89,6 @@ public partial class EditorBeatmap : TransactionalCommitComponent, IBeatmap, IBe 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/GroupSection.cs b/osu.Game/Screens/Edit/Timing/GroupSection.cs index abcdf7e4ff19..13e802a8e46d 100644 --- a/osu.Game/Screens/Edit/Timing/GroupSection.cs +++ b/osu.Game/Screens/Edit/Timing/GroupSection.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osuTK; @@ -25,6 +26,9 @@ internal partial class GroupSection : CompositeDrawable [Resolved] protected EditorBeatmap Beatmap { get; private set; } = null!; + [Resolved] + private OsuConfigManager configManager { get; set; } = null!; + [Resolved] private EditorClock clock { get; set; } = null!; @@ -112,7 +116,7 @@ private void changeSelectedGroupTime(in double time) 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) + if (cp is TimingControlPoint tp && configManager.Get(OsuSetting.EditorAdjustExistingObjectsOnTimingChanges)) { TimingSectionAdjustments.AdjustHitObjectOffset(Beatmap, tp, time - SelectedGroup.Value.Time); Beatmap.UpdateAllHitObjects(); diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs index 91a0a43d62d0..f105c0072631 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; @@ -26,6 +27,9 @@ public partial class TapTimingControl : CompositeDrawable [Resolved] private EditorBeatmap beatmap { get; set; } = null!; + [Resolved] + private OsuConfigManager configManager { get; set; } = null!; + [Resolved] private Bindable selectedGroup { get; set; } = null!; @@ -209,7 +213,7 @@ private void adjustOffset(double adjust) foreach (var cp in currentGroupItems) { - if (beatmap.AdjustNotesOnOffsetBPMChange.Value && cp is TimingControlPoint tp) + if (cp is TimingControlPoint tp) { TimingSectionAdjustments.AdjustHitObjectOffset(beatmap, tp, adjust); beatmap.UpdateAllHitObjects(); @@ -236,7 +240,7 @@ private void adjustBpm(double adjust) double oldBeatLength = timing.BeatLength; timing.BeatLength = 60000 / (timing.BPM + adjust); - if (beatmap.AdjustNotesOnOffsetBPMChange.Value) + if (configManager.Get(OsuSetting.EditorAdjustExistingObjectsOnTimingChanges)) { beatmap.BeginChange(); TimingSectionAdjustments.SetHitObjectBPM(beatmap, timing, oldBeatLength); diff --git a/osu.Game/Screens/Edit/Timing/TimingSection.cs b/osu.Game/Screens/Edit/Timing/TimingSection.cs index e668120d0d4d..6a89dc134117 100644 --- a/osu.Game/Screens/Edit/Timing/TimingSection.cs +++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Configuration; using osu.Game.Graphics.UserInterfaceV2; namespace osu.Game.Screens.Edit.Timing @@ -16,6 +17,9 @@ internal partial class TimingSection : Section private LabelledSwitchButton omitBarLine = null!; private BPMTextBox bpmTextEntry = null!; + [Resolved] + private OsuConfigManager configManager { get; set; } = null!; + [BackgroundDependencyLoader] private void load() { @@ -46,7 +50,7 @@ void saveChanges() bpmTextEntry.OnCommit = (oldBeatLength, _) => { - if (!Beatmap.AdjustNotesOnOffsetBPMChange.Value || ControlPoint.Value == null) + if (!configManager.Get(OsuSetting.EditorAdjustExistingObjectsOnTimingChanges) || ControlPoint.Value == null) return; Beatmap.BeginChange(); From c37e4877e24ea295b788e54f3048643629975791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Nov 2024 10:08:26 +0100 Subject: [PATCH 20/20] Move setting back to timing panel --- osu.Game/Screens/Edit/Editor.cs | 4 ---- osu.Game/Screens/Edit/Timing/TimingSection.cs | 7 +++++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 8de0c7c33d4e..644e1afb3bf5 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -422,10 +422,6 @@ private void load(OsuConfigManager config) Items = new MenuItem[] { new EditorMenuItem(EditorStrings.SetPreviewPointToCurrent, MenuItemType.Standard, SetPreviewPointToCurrentTime), - new ToggleMenuItem(EditorStrings.AdjustExistingObjectsOnTimingChanges) - { - State = { BindTarget = config.GetBindable(OsuSetting.EditorAdjustExistingObjectsOnTimingChanges) }, - } } } } diff --git a/osu.Game/Screens/Edit/Timing/TimingSection.cs b/osu.Game/Screens/Edit/Timing/TimingSection.cs index 6a89dc134117..ae1ac02dd647 100644 --- a/osu.Game/Screens/Edit/Timing/TimingSection.cs +++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs @@ -8,6 +8,7 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Localisation; namespace osu.Game.Screens.Edit.Timing { @@ -25,6 +26,12 @@ private void load() { Flow.AddRange(new Drawable[] { + new LabelledSwitchButton + { + Label = EditorStrings.AdjustExistingObjectsOnTimingChanges, + FixedLabelWidth = 220, + Current = configManager.GetBindable(OsuSetting.EditorAdjustExistingObjectsOnTimingChanges), + }, new TapTimingControl(), bpmTextEntry = new BPMTextBox(), timeSignature = new LabelledTimeSignature