From 653e5b5326d5429252cac4b81120e619ec4f148c Mon Sep 17 00:00:00 2001 From: Daniel Cronqvist Date: Tue, 13 Aug 2024 23:18:42 +0200 Subject: [PATCH] Add final tests --- DotTiled.Tests/Assert/AssertMap.cs | 40 ++++ DotTiled.Tests/Assert/AssertObject.cs | 7 +- DotTiled.Tests/Serialization/TestData.cs | 1 + .../map-with-many-layers.cs | 214 ++++++++++++++++++ .../map-with-many-layers.tmj | 181 +++++++++++++++ .../map-with-many-layers.tmx | 51 +++++ .../Map/map-with-many-layers/tileset.png | Bin 0 -> 11908 bytes .../Map/map-with-many-layers/tileset.tsj | 14 ++ .../Map/map-with-many-layers/tileset.tsx | 4 + DotTiled/Model/Layers/ImageLayer.cs | 4 +- DotTiled/Model/Layers/Objects/Object.cs | 1 - DotTiled/Model/Layers/Objects/TileObject.cs | 6 + DotTiled/Serialization/Tmj/Tmj.ImageLayer.cs | 4 +- DotTiled/Serialization/Tmj/Tmj.ObjectLayer.cs | 26 ++- DotTiled/Serialization/Tmx/Tmx.ObjectLayer.cs | 9 +- DotTiled/Serialization/Tmx/Tmx.TileLayer.cs | 4 +- 16 files changed, 549 insertions(+), 17 deletions(-) create mode 100644 DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/map-with-many-layers.cs create mode 100644 DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/map-with-many-layers.tmj create mode 100644 DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/map-with-many-layers.tmx create mode 100644 DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/tileset.png create mode 100644 DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/tileset.tsj create mode 100644 DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/tileset.tsx create mode 100644 DotTiled/Model/Layers/Objects/TileObject.cs diff --git a/DotTiled.Tests/Assert/AssertMap.cs b/DotTiled.Tests/Assert/AssertMap.cs index 6596ee8..e9ad8be 100644 --- a/DotTiled.Tests/Assert/AssertMap.cs +++ b/DotTiled.Tests/Assert/AssertMap.cs @@ -1,3 +1,6 @@ +using System.Collections; +using System.Numerics; + namespace DotTiled.Tests; public static partial class DotTiledAssert @@ -10,6 +13,29 @@ public static partial class DotTiledAssert return; } + if (typeof(T) == typeof(float)) + { + var expectedFloat = (float)(object)expected; + var actualFloat = (float)(object)actual!; + + var expecRounded = MathF.Round(expectedFloat, 3); + var actRounded = MathF.Round(actualFloat, 3); + + Assert.True(expecRounded == actRounded, $"Expected {nameof} '{expecRounded}' but got '{actRounded}'"); + return; + } + + if (expected is Vector2) + { + var expectedVector = (Vector2)(object)expected; + var actualVector = (Vector2)(object)actual!; + + AssertEqual(expectedVector.X, actualVector.X, $"{nameof}.X"); + AssertEqual(expectedVector.Y, actualVector.Y, $"{nameof}.Y"); + + return; + } + if (typeof(T).IsArray) { var expectedArray = (Array)(object)expected; @@ -24,6 +50,20 @@ public static partial class DotTiledAssert return; } + if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(List<>)) + { + var expectedList = (IList)(object)expected; + var actualList = (IList)(object)actual!; + + Assert.NotNull(actualList); + AssertEqual(expectedList.Count, actualList.Count, $"{nameof}.Count"); + + for (var i = 0; i < expectedList.Count; i++) + AssertEqual(expectedList[i], actualList[i], $"{nameof}[{i}]"); + + return; + } + Assert.True(expected.Equals(actual), $"Expected {nameof} '{expected}' but got '{actual}'"); } diff --git a/DotTiled.Tests/Assert/AssertObject.cs b/DotTiled.Tests/Assert/AssertObject.cs index c49b6e7..bd303f9 100644 --- a/DotTiled.Tests/Assert/AssertObject.cs +++ b/DotTiled.Tests/Assert/AssertObject.cs @@ -13,7 +13,6 @@ public static partial class DotTiledAssert AssertEqual(expected.Width, actual.Width, nameof(Object.Width)); AssertEqual(expected.Height, actual.Height, nameof(Object.Height)); AssertEqual(expected.Rotation, actual.Rotation, nameof(Object.Rotation)); - AssertEqual(expected.GID, actual.GID, nameof(Object.GID)); AssertEqual(expected.Visible, actual.Visible, nameof(Object.Visible)); AssertEqual(expected.Template, actual.Template, nameof(Object.Template)); @@ -63,4 +62,10 @@ public static partial class DotTiledAssert AssertEqual(expected.Text, actual.Text, nameof(TextObject.Text)); } + + private static void AssertObject(TileObject expected, TileObject actual) + { + // Attributes + AssertEqual(expected.GID, actual.GID, nameof(TileObject.GID)); + } } diff --git a/DotTiled.Tests/Serialization/TestData.cs b/DotTiled.Tests/Serialization/TestData.cs index d2d3316..c3d52f8 100644 --- a/DotTiled.Tests/Serialization/TestData.cs +++ b/DotTiled.Tests/Serialization/TestData.cs @@ -39,6 +39,7 @@ public static partial class TestData ["Serialization/TestData/Map/map_with_flippingflags/map-with-flippingflags", (string f) => TestData.MapWithFlippingFlags(f), Array.Empty()], ["Serialization/TestData/Map/map_external_tileset_multi/map-external-tileset-multi", (string f) => TestData.MapExternalTilesetMulti(f), Array.Empty()], ["Serialization/TestData/Map/map_external_tileset_wangset/map-external-tileset-wangset", (string f) => TestData.MapExternalTilesetWangset(f), Array.Empty()], + ["Serialization/TestData/Map/map_with_many_layers/map-with-many-layers", (string f) => TestData.MapWithManyLayers(f), Array.Empty()], ]; private static CustomTypeDefinition[] typedefs = [ diff --git a/DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/map-with-many-layers.cs b/DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/map-with-many-layers.cs new file mode 100644 index 0000000..8ef6ce5 --- /dev/null +++ b/DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/map-with-many-layers.cs @@ -0,0 +1,214 @@ +using System.Numerics; + +namespace DotTiled.Tests; + +public partial class TestData +{ + public static Map MapWithManyLayers(string fileExt) => new Map + { + Class = "", + Orientation = MapOrientation.Orthogonal, + Width = 5, + Height = 5, + TileWidth = 32, + TileHeight = 32, + Infinite = false, + HexSideLength = null, + StaggerAxis = null, + StaggerIndex = null, + ParallaxOriginX = 0, + ParallaxOriginY = 0, + RenderOrder = RenderOrder.RightDown, + CompressionLevel = -1, + BackgroundColor = new Color { R = 0, G = 0, B = 0, A = 0 }, + Version = "1.10", + TiledVersion = "1.11.0", + NextLayerID = 8, + NextObjectID = 7, + Tilesets = [ + new Tileset + { + Version = "1.10", + TiledVersion = "1.11.0", + FirstGID = 1, + Name = "tileset", + TileWidth = 32, + TileHeight = 32, + TileCount = 24, + Columns = 8, + Source = $"tileset.{(fileExt == "tmx" ? "tsx" : "tsj")}", + Image = new Image + { + Format = ImageFormat.Png, + Source = "tileset.png", + Width = 256, + Height = 96, + } + } + ], + Layers = [ + new Group + { + ID = 2, + Name = "Root", + Layers = [ + new ObjectLayer + { + ID = 3, + Name = "Objects", + Objects = [ + new RectangleObject + { + ID = 1, + Name = "Object 1", + X = 25.6667f, + Y = 28.6667f, + Width = 31.3333f, + Height = 31.3333f + }, + new PointObject + { + ID = 3, + Name = "P1", + X = 117.667f, + Y = 48.6667f + }, + new EllipseObject + { + ID = 4, + Name = "Circle1", + X = 77f, + Y = 72.3333f, + Width = 34.6667f, + Height = 34.6667f + }, + new PolygonObject + { + ID = 5, + Name = "Poly", + X = 20.6667f, + Y = 114.667f, + Points = [ + new Vector2(0, 0), + new Vector2(104,20), + new Vector2(35.6667f, 32.3333f) + ] + }, + new TileObject + { + ID = 6, + Name = "TileObj", + GID = 7, + X = -35, + Y = 110.333f, + Width = 64, + Height = 146 + } + ] + }, + new Group + { + ID = 5, + Name = "Sub", + Layers = [ + new TileLayer + { + ID = 7, + Name = "Tile 3", + Width = 5, + Height = 5, + Data = new Data + { + Encoding = DataEncoding.Csv, + Chunks = null, + Compression = null, + GlobalTileIDs = [ + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 + ], + FlippingFlags = [ + FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, + FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, + FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, + FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, + FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None + ] + } + }, + new TileLayer + { + ID = 6, + Name = "Tile 2", + Width = 5, + Height = 5, + Data = new Data + { + Encoding = DataEncoding.Csv, + Chunks = null, + Compression = null, + GlobalTileIDs = [ + 0, 15, 15, 0, 0, + 0, 15, 15, 0, 0, + 0, 15, 15, 15, 0, + 15, 15, 15, 0, 0, + 0, 0, 0, 0, 0 + ], + FlippingFlags = [ + FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, + FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, + FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, + FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, + FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None + ] + } + } + ] + }, + new ImageLayer + { + ID = 4, + Name = "ImageLayer", + Image = new Image + { + Format = ImageFormat.Png, + Source = "tileset.png", + Width = fileExt == "tmx" ? 256u : 0, // Currently, json format does not + Height = fileExt == "tmx" ? 96u : 0 // include image dimensions in image layer https://github.com/mapeditor/tiled/issues/4028 + }, + RepeatX = true + }, + new TileLayer + { + ID = 1, + Name = "Tile Layer 1", + Width = 5, + Height = 5, + Data = new Data + { + Encoding = DataEncoding.Csv, + Chunks = null, + Compression = null, + GlobalTileIDs = [ + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 + ], + FlippingFlags = [ + FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, + FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, + FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, + FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, + FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None + ] + } + } + ] + } + ] + }; +} diff --git a/DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/map-with-many-layers.tmj b/DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/map-with-many-layers.tmj new file mode 100644 index 0000000..16561f4 --- /dev/null +++ b/DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/map-with-many-layers.tmj @@ -0,0 +1,181 @@ +{ "compressionlevel":-1, + "height":5, + "infinite":false, + "layers":[ + { + "id":2, + "layers":[ + { + "draworder":"topdown", + "id":3, + "name":"Objects", + "objects":[ + { + "height":31.3333333333333, + "id":1, + "name":"Object 1", + "rotation":0, + "type":"", + "visible":true, + "width":31.3333333333333, + "x":25.6666666666667, + "y":28.6666666666667 + }, + { + "height":0, + "id":3, + "name":"P1", + "point":true, + "rotation":0, + "type":"", + "visible":true, + "width":0, + "x":117.666666666667, + "y":48.6666666666667 + }, + { + "ellipse":true, + "height":34.6666666666667, + "id":4, + "name":"Circle1", + "rotation":0, + "type":"", + "visible":true, + "width":34.6666666666667, + "x":77, + "y":72.3333333333333 + }, + { + "height":0, + "id":5, + "name":"Poly", + "polygon":[ + { + "x":0, + "y":0 + }, + { + "x":104, + "y":20 + }, + { + "x":35.6666666666667, + "y":32.3333333333333 + }], + "rotation":0, + "type":"", + "visible":true, + "width":0, + "x":20.6666666666667, + "y":114.666666666667 + }, + { + "gid":7, + "height":146, + "id":6, + "name":"TileObj", + "rotation":0, + "type":"", + "visible":true, + "width":64, + "x":-35, + "y":110.333333333333 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }, + { + "id":5, + "layers":[ + { + "data":[0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0], + "height":5, + "id":7, + "name":"Tile 3", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":5, + "x":0, + "y":0 + }, + { + "data":[0, 15, 15, 0, 0, + 0, 15, 15, 0, 0, + 0, 15, 15, 15, 0, + 15, 15, 15, 0, 0, + 0, 0, 0, 0, 0], + "height":5, + "id":6, + "name":"Tile 2", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":5, + "x":0, + "y":0 + }], + "name":"Sub", + "opacity":1, + "type":"group", + "visible":true, + "x":0, + "y":0 + }, + { + "id":4, + "image":"tileset.png", + "name":"ImageLayer", + "opacity":1, + "repeatx":true, + "type":"imagelayer", + "visible":true, + "x":0, + "y":0 + }, + { + "data":[1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0], + "height":5, + "id":1, + "name":"Tile Layer 1", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":5, + "x":0, + "y":0 + }], + "name":"Root", + "opacity":1, + "type":"group", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":8, + "nextobjectid":7, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"1.11.0", + "tileheight":32, + "tilesets":[ + { + "firstgid":1, + "source":"tileset.tsj" + }], + "tilewidth":32, + "type":"map", + "version":"1.10", + "width":5 +} \ No newline at end of file diff --git a/DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/map-with-many-layers.tmx b/DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/map-with-many-layers.tmx new file mode 100644 index 0000000..34cd91c --- /dev/null +++ b/DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/map-with-many-layers.tmx @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + +0,0,0,0,0, +0,0,0,0,0, +0,0,0,0,0, +0,0,0,0,0, +0,0,0,0,0 + + + + +0,15,15,0,0, +0,15,15,0,0, +0,15,15,15,0, +15,15,15,0,0, +0,0,0,0,0 + + + + + + + + +1,1,1,1,1, +0,0,0,0,0, +0,0,0,0,0, +0,0,0,0,0, +0,0,0,0,0 + + + + diff --git a/DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/tileset.png b/DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/tileset.png new file mode 100644 index 0000000000000000000000000000000000000000..97c1fb31b1fa4dcf1214b8fe9b2e2b8e287c72d0 GIT binary patch literal 11908 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 Pn-8F-q^(#jZyEZ3BaoJ| literal 0 HcmV?d00001 diff --git a/DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/tileset.tsj b/DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/tileset.tsj new file mode 100644 index 0000000..820e88f --- /dev/null +++ b/DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/tileset.tsj @@ -0,0 +1,14 @@ +{ "columns":8, + "image":"tileset.png", + "imageheight":96, + "imagewidth":256, + "margin":0, + "name":"tileset", + "spacing":0, + "tilecount":24, + "tiledversion":"1.11.0", + "tileheight":32, + "tilewidth":32, + "type":"tileset", + "version":"1.10" +} \ No newline at end of file diff --git a/DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/tileset.tsx b/DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/tileset.tsx new file mode 100644 index 0000000..d730182 --- /dev/null +++ b/DotTiled.Tests/Serialization/TestData/Map/map-with-many-layers/tileset.tsx @@ -0,0 +1,4 @@ + + + + diff --git a/DotTiled/Model/Layers/ImageLayer.cs b/DotTiled/Model/Layers/ImageLayer.cs index 6489c22..a140b0d 100644 --- a/DotTiled/Model/Layers/ImageLayer.cs +++ b/DotTiled/Model/Layers/ImageLayer.cs @@ -5,8 +5,8 @@ public class ImageLayer : BaseLayer // Attributes public uint X { get; set; } = 0; public uint Y { get; set; } = 0; - public required bool RepeatX { get; set; } - public required bool RepeatY { get; set; } + public bool RepeatX { get; set; } = false; + public bool RepeatY { get; set; } = false; // At most one of public Image? Image { get; set; } diff --git a/DotTiled/Model/Layers/Objects/Object.cs b/DotTiled/Model/Layers/Objects/Object.cs index b3313d7..765de69 100644 --- a/DotTiled/Model/Layers/Objects/Object.cs +++ b/DotTiled/Model/Layers/Objects/Object.cs @@ -13,7 +13,6 @@ public abstract class Object public float Width { get; set; } = 0f; public float Height { get; set; } = 0f; public float Rotation { get; set; } = 0f; - public uint? GID { get; set; } public bool Visible { get; set; } = true; public string? Template { get; set; } diff --git a/DotTiled/Model/Layers/Objects/TileObject.cs b/DotTiled/Model/Layers/Objects/TileObject.cs new file mode 100644 index 0000000..c066780 --- /dev/null +++ b/DotTiled/Model/Layers/Objects/TileObject.cs @@ -0,0 +1,6 @@ +namespace DotTiled; + +public class TileObject : Object +{ + public uint GID { get; set; } +} diff --git a/DotTiled/Serialization/Tmj/Tmj.ImageLayer.cs b/DotTiled/Serialization/Tmj/Tmj.ImageLayer.cs index d315891..dbd75a1 100644 --- a/DotTiled/Serialization/Tmj/Tmj.ImageLayer.cs +++ b/DotTiled/Serialization/Tmj/Tmj.ImageLayer.cs @@ -25,8 +25,8 @@ internal partial class Tmj var properties = element.GetOptionalPropertyCustom?>("properties", e => ReadProperties(e, customTypeDefinitions), null); var image = element.GetRequiredProperty("image"); - var repeatX = element.GetRequiredProperty("repeatx"); - var repeatY = element.GetRequiredProperty("repeaty"); + var repeatX = element.GetOptionalProperty("repeatx", false); + var repeatY = element.GetOptionalProperty("repeaty", false); var transparentColor = element.GetOptionalPropertyParseable("transparentcolor", s => Color.Parse(s, CultureInfo.InvariantCulture), null); var x = element.GetOptionalProperty("x", 0); var y = element.GetOptionalProperty("y", 0); diff --git a/DotTiled/Serialization/Tmj/Tmj.ObjectLayer.cs b/DotTiled/Serialization/Tmj/Tmj.ObjectLayer.cs index 2fdf3c9..564f2db 100644 --- a/DotTiled/Serialization/Tmj/Tmj.ObjectLayer.cs +++ b/DotTiled/Serialization/Tmj/Tmj.ObjectLayer.cs @@ -97,7 +97,6 @@ internal partial class Tmj widthDefault = templObj.Width; heightDefault = templObj.Height; rotationDefault = templObj.Rotation; - gidDefault = templObj.GID; visibleDefault = templObj.Visible; propertiesDefault = templObj.Properties; ellipseDefault = templObj is EllipseObject; @@ -123,6 +122,25 @@ internal partial class Tmj var x = element.GetOptionalProperty("x", xDefault); var y = element.GetOptionalProperty("y", yDefault); + if (gid is not null) + { + return new TileObject + { + ID = id, + Name = name, + Type = type, + X = x, + Y = y, + Width = width, + Height = height, + Rotation = rotation, + Visible = visible, + Template = template, + Properties = properties, + GID = gid.Value + }; + } + if (ellipse) { return new EllipseObject @@ -135,7 +153,6 @@ internal partial class Tmj Width = width, Height = height, Rotation = rotation, - GID = gid, Visible = visible, Template = template, Properties = properties @@ -154,7 +171,6 @@ internal partial class Tmj Width = width, Height = height, Rotation = rotation, - GID = gid, Visible = visible, Template = template, Properties = properties @@ -173,7 +189,6 @@ internal partial class Tmj Width = width, Height = height, Rotation = rotation, - GID = gid, Visible = visible, Template = template, Properties = properties, @@ -193,7 +208,6 @@ internal partial class Tmj Width = width, Height = height, Rotation = rotation, - GID = gid, Visible = visible, Template = template, Properties = properties, @@ -211,7 +225,6 @@ internal partial class Tmj text.Width = width; text.Height = height; text.Rotation = rotation; - text.GID = gid; text.Visible = visible; text.Template = template; text.Properties = properties; @@ -228,7 +241,6 @@ internal partial class Tmj Width = width, Height = height, Rotation = rotation, - GID = gid, Visible = visible, Template = template, Properties = properties diff --git a/DotTiled/Serialization/Tmx/Tmx.ObjectLayer.cs b/DotTiled/Serialization/Tmx/Tmx.ObjectLayer.cs index 2367974..fa80805 100644 --- a/DotTiled/Serialization/Tmx/Tmx.ObjectLayer.cs +++ b/DotTiled/Serialization/Tmx/Tmx.ObjectLayer.cs @@ -105,7 +105,6 @@ internal partial class Tmx widthDefault = templObj.Width; heightDefault = templObj.Height; rotationDefault = templObj.Rotation; - gidDefault = templObj.GID; visibleDefault = templObj.Visible; propertiesDefault = templObj.Properties; } @@ -137,12 +136,19 @@ internal partial class Tmx _ => throw new Exception($"Unknown object marker '{elementName}'") }); + if (gid is not null) + { + obj = new TileObject { ID = id, GID = gid.Value }; + reader.Skip(); + } + if (obj is null) { obj = new RectangleObject { ID = id }; reader.Skip(); } + obj.ID = id; obj.Name = name; obj.Type = type; obj.X = x; @@ -150,7 +156,6 @@ internal partial class Tmx obj.Width = width; obj.Height = height; obj.Rotation = rotation; - obj.GID = gid; obj.Visible = visible; obj.Template = template; obj.Properties = properties; diff --git a/DotTiled/Serialization/Tmx/Tmx.TileLayer.cs b/DotTiled/Serialization/Tmx/Tmx.TileLayer.cs index 78096e3..6fc64fb 100644 --- a/DotTiled/Serialization/Tmx/Tmx.TileLayer.cs +++ b/DotTiled/Serialization/Tmx/Tmx.TileLayer.cs @@ -74,8 +74,8 @@ internal partial class Tmx var offsetY = reader.GetOptionalAttributeParseable("offsety") ?? 0.0f; var parallaxX = reader.GetOptionalAttributeParseable("parallaxx") ?? 1.0f; var parallaxY = reader.GetOptionalAttributeParseable("parallaxy") ?? 1.0f; - var repeatX = reader.GetRequiredAttributeParseable("repeatx"); - var repeatY = reader.GetRequiredAttributeParseable("repeaty"); + var repeatX = (reader.GetOptionalAttributeParseable("repeatx") ?? 0) == 1; + var repeatY = (reader.GetOptionalAttributeParseable("repeaty") ?? 0) == 1; Dictionary? properties = null; Image? image = null;