diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs
index c6649f6af1..090675473d 100644
--- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs
@@ -28,5 +28,22 @@ namespace osu.Game.Beatmaps.ControlPoints
/// An existing control point to compare with.
/// Whether this is redundant when placed alongside .
public abstract bool IsRedundant(ControlPoint existing);
+
+ ///
+ /// Create a copy of this room without online information.
+ /// Should be used to create a local copy of a room for submitting in the future.
+ ///
+ public ControlPoint CreateCopy()
+ {
+ var copy = (ControlPoint)Activator.CreateInstance(GetType());
+
+ copy.CopyFrom(this);
+
+ return copy;
+ }
+
+ public virtual void CopyFrom(ControlPoint other)
+ {
+ }
}
}
diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
index b843aad950..b56f9a106b 100644
--- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
+++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
@@ -11,7 +11,7 @@ using osu.Framework.Lists;
namespace osu.Game.Beatmaps.ControlPoints
{
[Serializable]
- public class ControlPointInfo
+ public class ControlPointInfo : ICloneable
{
///
/// All control points grouped by time.
@@ -297,5 +297,15 @@ namespace osu.Game.Beatmaps.ControlPoints
break;
}
}
+
+ public object Clone()
+ {
+ var controlPointInfo = new ControlPointInfo();
+
+ foreach (var point in AllControlPoints)
+ controlPointInfo.Add(point.Time, point.CreateCopy());
+
+ return controlPointInfo;
+ }
}
}
diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs
index 283bf76572..0bc5605051 100644
--- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs
@@ -39,5 +39,12 @@ namespace osu.Game.Beatmaps.ControlPoints
public override bool IsRedundant(ControlPoint existing)
=> existing is DifficultyControlPoint existingDifficulty
&& SpeedMultiplier == existingDifficulty.SpeedMultiplier;
+
+ public override void CopyFrom(ControlPoint other)
+ {
+ SpeedMultiplier = ((DifficultyControlPoint)other).SpeedMultiplier;
+
+ base.CopyFrom(other);
+ }
}
}
diff --git a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs
index ea28fca170..79bc88e773 100644
--- a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs
@@ -50,5 +50,13 @@ namespace osu.Game.Beatmaps.ControlPoints
&& existing is EffectControlPoint existingEffect
&& KiaiMode == existingEffect.KiaiMode
&& OmitFirstBarLine == existingEffect.OmitFirstBarLine;
+
+ public override void CopyFrom(ControlPoint other)
+ {
+ KiaiMode = ((EffectControlPoint)other).KiaiMode;
+ OmitFirstBarLine = ((EffectControlPoint)other).OmitFirstBarLine;
+
+ base.CopyFrom(other);
+ }
}
}
diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
index fd0b496335..4aa6a3d6e9 100644
--- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
@@ -72,5 +72,13 @@ namespace osu.Game.Beatmaps.ControlPoints
=> existing is SampleControlPoint existingSample
&& SampleBank == existingSample.SampleBank
&& SampleVolume == existingSample.SampleVolume;
+
+ public override void CopyFrom(ControlPoint other)
+ {
+ SampleVolume = ((SampleControlPoint)other).SampleVolume;
+ SampleBank = ((SampleControlPoint)other).SampleBank;
+
+ base.CopyFrom(other);
+ }
}
}
diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
index d9378bca4a..580642f593 100644
--- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
@@ -69,5 +69,13 @@ namespace osu.Game.Beatmaps.ControlPoints
// Timing points are never redundant as they can change the time signature.
public override bool IsRedundant(ControlPoint existing) => false;
+
+ public override void CopyFrom(ControlPoint other)
+ {
+ TimeSignature = ((TimingControlPoint)other).TimeSignature;
+ BeatLength = ((TimingControlPoint)other).BeatLength;
+
+ base.CopyFrom(other);
+ }
}
}
diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
index c9d139bdd0..06ff677aed 100644
--- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
@@ -164,13 +164,24 @@ namespace osu.Game.Beatmaps.Formats
/// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it.
/// DO NOT USE THIS UNLESS 100% SURE.
///
- public readonly float BpmMultiplier;
+ public float BpmMultiplier { get; set; }
public LegacyDifficultyControlPoint(double beatLength)
+ : this()
+ {
+ BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100f : 1;
+ }
+
+ public LegacyDifficultyControlPoint()
{
SpeedMultiplierBindable.Precision = double.Epsilon;
+ }
- BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100f : 1;
+ public override void CopyFrom(ControlPoint other)
+ {
+ base.CopyFrom(other);
+
+ BpmMultiplier = ((LegacyDifficultyControlPoint)other).BpmMultiplier;
}
}
diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs
index 8f27e0b0e9..7dd85e1232 100644
--- a/osu.Game/Beatmaps/IBeatmap.cs
+++ b/osu.Game/Beatmaps/IBeatmap.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Beatmaps
///
/// The control points in this beatmap.
///
- ControlPointInfo ControlPointInfo { get; }
+ ControlPointInfo ControlPointInfo { get; set; }
///
/// The breaks in this beatmap.
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index 30382c444f..06b5913b18 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -14,6 +14,7 @@ using osu.Framework.Graphics.Textures;
using osu.Framework.Logging;
using osu.Framework.Statistics;
using osu.Framework.Testing;
+using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Types;
@@ -111,6 +112,8 @@ namespace osu.Game.Beatmaps
// Convert
IBeatmap converted = converter.Convert(cancellationSource.Token);
+ converted.ControlPointInfo = (ControlPointInfo)converted.ControlPointInfo.Clone();
+
// Apply conversion mods to the result
foreach (var mod in mods.OfType())
{
diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs
index 165d2ba278..a54a95f59d 100644
--- a/osu.Game/Screens/Edit/EditorBeatmap.cs
+++ b/osu.Game/Screens/Edit/EditorBeatmap.cs
@@ -74,7 +74,11 @@ namespace osu.Game.Screens.Edit
public BeatmapMetadata Metadata => PlayableBeatmap.Metadata;
- public ControlPointInfo ControlPointInfo => PlayableBeatmap.ControlPointInfo;
+ public ControlPointInfo ControlPointInfo
+ {
+ get => PlayableBeatmap.ControlPointInfo;
+ set => PlayableBeatmap.ControlPointInfo = value;
+ }
public List Breaks => PlayableBeatmap.Breaks;
diff --git a/osu.Game/Screens/Play/GameplayBeatmap.cs b/osu.Game/Screens/Play/GameplayBeatmap.cs
index 64894544f4..565595656f 100644
--- a/osu.Game/Screens/Play/GameplayBeatmap.cs
+++ b/osu.Game/Screens/Play/GameplayBeatmap.cs
@@ -29,7 +29,11 @@ namespace osu.Game.Screens.Play
public BeatmapMetadata Metadata => PlayableBeatmap.Metadata;
- public ControlPointInfo ControlPointInfo => PlayableBeatmap.ControlPointInfo;
+ public ControlPointInfo ControlPointInfo
+ {
+ get => PlayableBeatmap.ControlPointInfo;
+ set => PlayableBeatmap.ControlPointInfo = value;
+ }
public List Breaks => PlayableBeatmap.Breaks;