diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointGroup.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointGroup.cs
index c4b990675e..d57baf25be 100644
--- a/osu.Game/Beatmaps/ControlPoints/ControlPointGroup.cs
+++ b/osu.Game/Beatmaps/ControlPoints/ControlPointGroup.cs
@@ -30,11 +30,13 @@ namespace osu.Game.Beatmaps.ControlPoints
public void Add(ControlPoint point)
{
- point.AttachGroup(this);
+ var existing = controlPoints.FirstOrDefault(p => p.GetType() == point.GetType());
- foreach (var existing in controlPoints.Where(p => p.GetType() == point.GetType()).ToArray())
+ if (existing != null)
Remove(existing);
+ point.AttachGroup(this);
+
controlPoints.Add(point);
ItemAdded?.Invoke(point);
}
diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
index fc26fb9946..24e3c3d8ca 100644
--- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
+++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
@@ -62,28 +62,28 @@ namespace osu.Game.Beatmaps.ControlPoints
///
/// The time to find the difficulty control point at.
/// The difficulty control point.
- public DifficultyControlPoint DifficultyPointAt(double time) => binarySearch(DifficultyPoints, time);
+ public DifficultyControlPoint DifficultyPointAt(double time) => binarySearchWithFallback(DifficultyPoints, time);
///
/// Finds the effect control point that is active at .
///
/// The time to find the effect control point at.
/// The effect control point.
- public EffectControlPoint EffectPointAt(double time) => binarySearch(EffectPoints, time);
+ public EffectControlPoint EffectPointAt(double time) => binarySearchWithFallback(EffectPoints, time);
///
/// Finds the sound control point that is active at .
///
/// The time to find the sound control point at.
/// The sound control point.
- public SampleControlPoint SamplePointAt(double time) => binarySearch(SamplePoints, time, SamplePoints.Count > 0 ? SamplePoints[0] : null);
+ public SampleControlPoint SamplePointAt(double time) => binarySearchWithFallback(SamplePoints, time, SamplePoints.Count > 0 ? SamplePoints[0] : null);
///
/// Finds the timing control point that is active at .
///
/// The time to find the timing control point at.
/// The timing control point.
- public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, TimingPoints.Count > 0 ? TimingPoints[0] : null);
+ public TimingControlPoint TimingPointAt(double time) => binarySearchWithFallback(TimingPoints, time, TimingPoints.Count > 0 ? TimingPoints[0] : null);
///
/// Finds the closest of the same type as that is active at .
@@ -130,22 +130,35 @@ namespace osu.Game.Beatmaps.ControlPoints
///
/// Binary searches one of the control point lists to find the active control point at .
+ /// Includes logic for returning a specific point when no matching point is found.
///
/// The list to search.
/// The time to find the control point at.
/// The control point to use when is before any control points. If null, a new control point will be constructed.
- /// The active control point at .
- private T binarySearch(IReadOnlyList list, double time, T prePoint = null)
+ /// The active control point at , or a fallback if none found.
+ private T binarySearchWithFallback(IReadOnlyList list, double time, T prePoint = null)
where T : ControlPoint, new()
+ {
+ return binarySearch(list, time) ?? prePoint ?? new T();
+ }
+
+ ///
+ /// Binary searches one of the control point lists to find the active control point at .
+ ///
+ /// The list to search.
+ /// The time to find the control point at.
+ /// The active control point at .
+ private T binarySearch(IReadOnlyList list, double time)
+ where T : ControlPoint
{
if (list == null)
throw new ArgumentNullException(nameof(list));
if (list.Count == 0)
- return new T();
+ return null;
if (time < list[0].Time)
- return prePoint ?? new T();
+ return null;
if (time >= list[list.Count - 1].Time)
return list[list.Count - 1];