diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs index 24e752da31..9bb90ab461 100644 --- a/osu.Game/Database/LegacyBeatmapExporter.cs +++ b/osu.Game/Database/LegacyBeatmapExporter.cs @@ -120,18 +120,30 @@ namespace osu.Game.Database if (BezierConverter.CountSegments(hasPath.Path.ControlPoints) <= 1 && hasPath.Path.ControlPoints[0].Type!.Value.Degree == null) continue; - var newControlPoints = BezierConverter.ConvertToModernBezier(hasPath.Path.ControlPoints); - - // Truncate control points to integer positions - foreach (var pathControlPoint in newControlPoints) - { - pathControlPoint.Position = new Vector2( - (float)Math.Floor(pathControlPoint.Position.X), - (float)Math.Floor(pathControlPoint.Position.Y)); - } + var convertedToBezier = BezierConverter.ConvertToModernBezier(hasPath.Path.ControlPoints); hasPath.Path.ControlPoints.Clear(); - hasPath.Path.ControlPoints.AddRange(newControlPoints); + + for (int i = 0; i < convertedToBezier.Count; i++) + { + var convertedPoint = convertedToBezier[i]; + + // Truncate control points to integer positions + var position = new Vector2( + (float)Math.Floor(convertedPoint.Position.X), + (float)Math.Floor(convertedPoint.Position.Y)); + + // stable only supports a single curve type specification per slider. + // we exploit the fact that the converted-to-Bézier path only has Bézier segments, + // and thus we specify the Bézier curve type once ever at the start of the slider. + hasPath.Path.ControlPoints.Add(new PathControlPoint(position, i == 0 ? PathType.BEZIER : null)); + + // however, the Bézier path as output by the converter has multiple segments. + // `LegacyBeatmapEncoder` will attempt to encode this by emitting per-control-point curve type specs which don't do anything for stable. + // instead, stable expects control points that start a segment to be present in the path twice in succession. + if (convertedPoint.Type == PathType.BEZIER) + hasPath.Path.ControlPoints.Add(new PathControlPoint(position)); + } } // Encode to legacy format diff --git a/osu.Game/Rulesets/Objects/BezierConverter.cs b/osu.Game/Rulesets/Objects/BezierConverter.cs index 384c686167..638975630e 100644 --- a/osu.Game/Rulesets/Objects/BezierConverter.cs +++ b/osu.Game/Rulesets/Objects/BezierConverter.cs @@ -136,7 +136,6 @@ namespace osu.Game.Rulesets.Objects { for (int j = 0; j < segment.Length - 1; j++) { - if (result.Count > 0 && j == 0) result.Add(new PathControlPoint(segment[j])); result.Add(new PathControlPoint(segment[j], j == 0 ? PathType.BEZIER : null)); } } @@ -148,7 +147,6 @@ namespace osu.Game.Rulesets.Objects { for (int j = 0; j < segment.Length - 1; j++) { - if (result.Count > 0 && j == 0) result.Add(new PathControlPoint(segment[j])); result.Add(new PathControlPoint(segment[j], j == 0 ? PathType.BEZIER : null)); } } @@ -160,7 +158,6 @@ namespace osu.Game.Rulesets.Objects for (int j = 0; j < circleResult.Length - 1; j++) { - if (result.Count > 0 && j == 0) result.Add(new PathControlPoint(circleResult[j])); result.Add(new PathControlPoint(circleResult[j], j == 0 ? PathType.BEZIER : null)); } @@ -173,7 +170,6 @@ namespace osu.Game.Rulesets.Objects for (int j = 0; j < bSplineResult.Length - 1; j++) { - if (result.Count > 0 && j == 0) result.Add(new PathControlPoint(bSplineResult[j])); result.Add(new PathControlPoint(bSplineResult[j], j == 0 ? PathType.BEZIER : null)); }