diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index 19378821b3..54ebebeb7b 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -468,6 +468,40 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
}
+ [Test]
+ public void TestDecodeBeatmapHitObjectCoordinatesLegacy()
+ {
+ var decoder = new LegacyBeatmapDecoder();
+
+ using (var resStream = TestResources.OpenResource("hitobject-coordinates-legacy.osu"))
+ using (var stream = new LineBufferedReader(resStream))
+ {
+ var hitObjects = decoder.Decode(stream).HitObjects;
+
+ var positionData = hitObjects[0] as IHasPosition;
+
+ Assert.IsNotNull(positionData);
+ Assert.AreEqual(new Vector2(256, 256), positionData!.Position);
+ }
+ }
+
+ [Test]
+ public void TestDecodeBeatmapHitObjectCoordinatesLazer()
+ {
+ var decoder = new LegacyBeatmapDecoder(LegacyBeatmapEncoder.FIRST_LAZER_VERSION);
+
+ using (var resStream = TestResources.OpenResource("hitobject-coordinates-lazer.osu"))
+ using (var stream = new LineBufferedReader(resStream))
+ {
+ var hitObjects = decoder.Decode(stream).HitObjects;
+
+ var positionData = hitObjects[0] as IHasPosition;
+
+ Assert.IsNotNull(positionData);
+ Assert.AreEqual(new Vector2(256.99853f, 256.001f), positionData!.Position);
+ }
+ }
+
[Test]
public void TestDecodeBeatmapHitObjects()
{
diff --git a/osu.Game.Tests/Resources/hitobject-coordinates-lazer.osu b/osu.Game.Tests/Resources/hitobject-coordinates-lazer.osu
new file mode 100644
index 0000000000..bb898a1521
--- /dev/null
+++ b/osu.Game.Tests/Resources/hitobject-coordinates-lazer.osu
@@ -0,0 +1,6 @@
+osu file format v128
+
+[HitObjects]
+// Coordinates should be preserves in lazer beatmaps.
+
+256.99853,256.001,1000,49,0,0:0:0:0:
diff --git a/osu.Game.Tests/Resources/hitobject-coordinates-legacy.osu b/osu.Game.Tests/Resources/hitobject-coordinates-legacy.osu
new file mode 100644
index 0000000000..e914c2fb36
--- /dev/null
+++ b/osu.Game.Tests/Resources/hitobject-coordinates-legacy.osu
@@ -0,0 +1,5 @@
+osu file format v14
+
+[HitObjects]
+// Coordinates should be truncated to int values in legacy beatmaps.
+256.99853,256.001,1000,49,0,0:0:0:0:
diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
index 30a78a16ed..ca4fadf458 100644
--- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
@@ -18,6 +18,12 @@ namespace osu.Game.Beatmaps.Formats
{
public const int LATEST_VERSION = 14;
+ ///
+ /// The .osu format (beatmap) version.
+ ///
+ /// osu!stable's versions end at .
+ /// osu!lazer's versions starts at .
+ ///
protected readonly int FormatVersion;
protected LegacyDecoder(int version)
diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
index 37a87462ca..8e6ffa20cc 100644
--- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
@@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
protected readonly double Offset;
///
- /// The beatmap version.
+ /// The .osu format (beatmap) version.
///
protected readonly int FormatVersion;
@@ -48,7 +48,10 @@ namespace osu.Game.Rulesets.Objects.Legacy
{
string[] split = text.Split(',');
- Vector2 pos = new Vector2((int)Parsing.ParseFloat(split[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE));
+ Vector2 pos =
+ FormatVersion >= LegacyBeatmapEncoder.FIRST_LAZER_VERSION
+ ? new Vector2(Parsing.ParseFloat(split[0], Parsing.MAX_COORDINATE_VALUE), Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE))
+ : new Vector2((int)Parsing.ParseFloat(split[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE));
double startTime = Parsing.ParseDouble(split[2]) + Offset;