diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs index 3b998b4219..c874b39028 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs @@ -92,6 +92,20 @@ namespace osu.Game.Tests.Visual.Editing hitObjectHasVelocity(1, 5); } + [Test] + public void TestUndo() + { + clickDifficultyPiece(1); + velocityPopoverHasSingleValue(2); + + setVelocityViaPopover(5); + hitObjectHasVelocity(1, 5); + dismissPopover(); + + AddStep("undo", () => Editor.Undo()); + hitObjectHasVelocity(1, 2); + } + [Test] public void TestMultipleSelectionWithSameSliderVelocity() { diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs index 530bd5eb20..d812aed0f6 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs @@ -109,6 +109,21 @@ namespace osu.Game.Tests.Visual.Editing hitObjectHasSampleBank(1, "drum"); } + [Test] + public void TestUndo() + { + clickSamplePiece(1); + samplePopoverHasSingleBank("soft"); + samplePopoverHasSingleVolume(60); + + setVolumeViaPopover(90); + hitObjectHasSampleVolume(1, 90); + dismissPopover(); + + AddStep("undo", () => Editor.Undo()); + hitObjectHasSampleVolume(1, 60); + } + [Test] public void TestMultipleSelectionWithSameSampleVolume() { diff --git a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs index b4647c2b64..2cf823ca0c 100644 --- a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs +++ b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; @@ -15,7 +16,9 @@ using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; +using osu.Game.Extensions; using osu.Game.IO; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Skinning; using Decoder = osu.Game.Beatmaps.Formats.Decoder; @@ -42,6 +45,7 @@ namespace osu.Game.Screens.Edit editorBeatmap.BeginChange(); processHitObjects(result, () => newBeatmap ??= readBeatmap(newState)); processTimingPoints(() => newBeatmap ??= readBeatmap(newState)); + processHitObjectLocalData(() => newBeatmap ??= readBeatmap(newState)); editorBeatmap.EndChange(); } @@ -87,6 +91,41 @@ namespace osu.Game.Screens.Edit } } + private void processHitObjectLocalData(Func getNewBeatmap) + { + // This method handles data that are stored in control points in the legacy format, + // but were moved to the hitobjects themselves in lazer. + // Specifically, the data being referred to here consists of: slider velocity and sample information. + + // For simplicity, this implementation relies on the editor beatmap already having the same hitobjects in sequence as the new beatmap. + // To guarantee that, `processHitObjects()` must be ran prior to this method for correct operation. + // This is done to avoid the necessity of reimplementing/reusing parts of LegacyBeatmapDecoder that already treat this data correctly. + + var oldObjects = editorBeatmap.HitObjects; + var newObjects = getNewBeatmap().HitObjects; + + Debug.Assert(oldObjects.Count == newObjects.Count); + + foreach (var (oldObject, newObject) in oldObjects.Zip(newObjects)) + { + // if `oldObject` and `newObject` are the same, it means that `oldObject` was inserted into `editorBeatmap` by `processHitObjects()`. + // in that case, there is nothing to do (and some of the subsequent changes may even prove destructive). + if (ReferenceEquals(oldObject, newObject)) + continue; + + if (oldObject is IHasSliderVelocity oldWithVelocity && newObject is IHasSliderVelocity newWithVelocity) + oldWithVelocity.SliderVelocity = newWithVelocity.SliderVelocity; + + oldObject.Samples = newObject.Samples; + + if (oldObject is IHasRepeats oldWithRepeats && newObject is IHasRepeats newWithRepeats) + { + oldWithRepeats.NodeSamples.Clear(); + oldWithRepeats.NodeSamples.AddRange(newWithRepeats.NodeSamples); + } + } + } + private void findChangedIndices(DiffResult result, LegacyDecoder.Section section, out List removedIndices, out List addedIndices) { removedIndices = new List();