From a08d6b07155cbdd193c15a5557f7109aee74fbf9 Mon Sep 17 00:00:00 2001 From: Daniel Cronqvist Date: Mon, 28 Apr 2025 20:12:18 +0200 Subject: [PATCH] Fix issue by cloning template objects and overriding GID's in tmj parsing --- src/DotTiled/Layers/Objects/EllipseObject.cs | 20 ++++++++++++- src/DotTiled/Layers/Objects/Object.cs | 6 ++++ src/DotTiled/Layers/Objects/PointObject.cs | 20 ++++++++++++- src/DotTiled/Layers/Objects/PolygonObject.cs | 17 +++++++++++ src/DotTiled/Layers/Objects/PolylineObject.cs | 17 +++++++++++ .../Layers/Objects/RectangleObject.cs | 20 ++++++++++++- src/DotTiled/Layers/Objects/TextObject.cs | 29 +++++++++++++++++++ src/DotTiled/Layers/Objects/TileObject.cs | 19 ++++++++++++ .../Serialization/Tmj/TmjReaderBase.Data.cs | 3 +- .../Tmj/TmjReaderBase.ObjectLayer.cs | 6 ++-- .../Tmx/TmxReaderBase.ObjectLayer.cs | 2 +- 11 files changed, 151 insertions(+), 8 deletions(-) diff --git a/src/DotTiled/Layers/Objects/EllipseObject.cs b/src/DotTiled/Layers/Objects/EllipseObject.cs index 75db631..3ebd55c 100644 --- a/src/DotTiled/Layers/Objects/EllipseObject.cs +++ b/src/DotTiled/Layers/Objects/EllipseObject.cs @@ -1,7 +1,25 @@ +using System.Linq; + namespace DotTiled; /// /// An ellipse object in a map. The existing , , , /// and properties are used to determine the size of the ellipse. /// -public class EllipseObject : Object { } +public class EllipseObject : Object +{ + internal override Object Clone() => new EllipseObject + { + ID = ID, + Name = Name, + Type = Type, + X = X, + Y = Y, + Width = Width, + Height = Height, + Rotation = Rotation, + Visible = Visible, + Template = Template, + Properties = Properties.Select(p => p.Clone()).ToList(), + }; +} diff --git a/src/DotTiled/Layers/Objects/Object.cs b/src/DotTiled/Layers/Objects/Object.cs index 2484ae8..4015508 100644 --- a/src/DotTiled/Layers/Objects/Object.cs +++ b/src/DotTiled/Layers/Objects/Object.cs @@ -64,4 +64,10 @@ public abstract class Object : HasPropertiesBase /// public override IList GetProperties() => Properties; + + /// + /// Creates a deep copy of the object. + /// + /// + internal abstract Object Clone(); } diff --git a/src/DotTiled/Layers/Objects/PointObject.cs b/src/DotTiled/Layers/Objects/PointObject.cs index 0c53e1b..43540de 100644 --- a/src/DotTiled/Layers/Objects/PointObject.cs +++ b/src/DotTiled/Layers/Objects/PointObject.cs @@ -1,7 +1,25 @@ +using System.Linq; + namespace DotTiled; /// /// A point object in a map. The existing and properties are used to /// determine the position of the point. /// -public class PointObject : Object { } +public class PointObject : Object +{ + internal override Object Clone() => new PointObject + { + ID = ID, + Name = Name, + Type = Type, + X = X, + Y = Y, + Width = Width, + Height = Height, + Rotation = Rotation, + Visible = Visible, + Template = Template, + Properties = Properties.Select(p => p.Clone()).ToList(), + }; +} diff --git a/src/DotTiled/Layers/Objects/PolygonObject.cs b/src/DotTiled/Layers/Objects/PolygonObject.cs index 2cf3895..afbab41 100644 --- a/src/DotTiled/Layers/Objects/PolygonObject.cs +++ b/src/DotTiled/Layers/Objects/PolygonObject.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Numerics; namespace DotTiled; @@ -14,4 +15,20 @@ public class PolygonObject : Object /// and are used as the origin of the polygon. /// public required List Points { get; set; } + + internal override Object Clone() => new PolygonObject + { + ID = ID, + Name = Name, + Type = Type, + X = X, + Y = Y, + Width = Width, + Height = Height, + Rotation = Rotation, + Visible = Visible, + Template = Template, + Properties = Properties.Select(p => p.Clone()).ToList(), + Points = Points.ToList(), + }; } diff --git a/src/DotTiled/Layers/Objects/PolylineObject.cs b/src/DotTiled/Layers/Objects/PolylineObject.cs index d755521..4ae8270 100644 --- a/src/DotTiled/Layers/Objects/PolylineObject.cs +++ b/src/DotTiled/Layers/Objects/PolylineObject.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Numerics; namespace DotTiled; @@ -13,4 +14,20 @@ public class PolylineObject : Object /// The points that make up the polyline. and are used as the origin of the polyline. /// public required List Points { get; set; } + + internal override Object Clone() => new PolylineObject + { + ID = ID, + Name = Name, + Type = Type, + X = X, + Y = Y, + Width = Width, + Height = Height, + Rotation = Rotation, + Visible = Visible, + Template = Template, + Properties = Properties.Select(p => p.Clone()).ToList(), + Points = Points.ToList(), + }; } diff --git a/src/DotTiled/Layers/Objects/RectangleObject.cs b/src/DotTiled/Layers/Objects/RectangleObject.cs index 8e71ee8..2dfc92a 100644 --- a/src/DotTiled/Layers/Objects/RectangleObject.cs +++ b/src/DotTiled/Layers/Objects/RectangleObject.cs @@ -1,7 +1,25 @@ +using System.Linq; + namespace DotTiled; /// /// A rectangle object in a map. The existing , , , /// and properties are used to determine the size of the rectangle. /// -public class RectangleObject : Object { } +public class RectangleObject : Object +{ + internal override Object Clone() => new RectangleObject + { + ID = ID, + Name = Name, + Type = Type, + X = X, + Y = Y, + Width = Width, + Height = Height, + Rotation = Rotation, + Visible = Visible, + Template = Template, + Properties = Properties.Select(p => p.Clone()).ToList(), + }; +} diff --git a/src/DotTiled/Layers/Objects/TextObject.cs b/src/DotTiled/Layers/Objects/TextObject.cs index 42b07d0..955242f 100644 --- a/src/DotTiled/Layers/Objects/TextObject.cs +++ b/src/DotTiled/Layers/Objects/TextObject.cs @@ -1,4 +1,5 @@ using System.Globalization; +using System.Linq; namespace DotTiled; @@ -113,4 +114,32 @@ public class TextObject : Object /// The text to be displayed. /// public string Text { get; set; } = ""; + + internal override Object Clone() => new TextObject + { + ID = ID, + Name = Name, + Type = Type, + X = X, + Y = Y, + Width = Width, + Height = Height, + Rotation = Rotation, + Visible = Visible, + Template = Template, + Properties = Properties.Select(p => p.Clone()).ToList(), + + FontFamily = FontFamily, + PixelSize = PixelSize, + Wrap = Wrap, + Color = Color, + Bold = Bold, + Italic = Italic, + Underline = Underline, + Strikeout = Strikeout, + Kerning = Kerning, + HorizontalAlignment = HorizontalAlignment, + VerticalAlignment = VerticalAlignment, + Text = Text, + }; } diff --git a/src/DotTiled/Layers/Objects/TileObject.cs b/src/DotTiled/Layers/Objects/TileObject.cs index ea23d70..f54a9b9 100644 --- a/src/DotTiled/Layers/Objects/TileObject.cs +++ b/src/DotTiled/Layers/Objects/TileObject.cs @@ -1,3 +1,5 @@ +using System.Linq; + namespace DotTiled; /// @@ -14,4 +16,21 @@ public class TileObject : Object /// The flipping flags for the tile. /// public FlippingFlags FlippingFlags { get; set; } + + internal override Object Clone() => new TileObject + { + ID = ID, + Name = Name, + Type = Type, + X = X, + Y = Y, + Width = Width, + Height = Height, + Rotation = Rotation, + Visible = Visible, + Template = Template, + Properties = Properties.Select(p => p.Clone()).ToList(), + GID = GID, + FlippingFlags = FlippingFlags, + }; } diff --git a/src/DotTiled/Serialization/Tmj/TmjReaderBase.Data.cs b/src/DotTiled/Serialization/Tmj/TmjReaderBase.Data.cs index 07b7a62..904321b 100644 --- a/src/DotTiled/Serialization/Tmj/TmjReaderBase.Data.cs +++ b/src/DotTiled/Serialization/Tmj/TmjReaderBase.Data.cs @@ -19,8 +19,7 @@ public abstract partial class TmjReaderBase internal static Chunk ReadChunk(JsonElement element, Optional compression, DataEncoding encoding) { - var data = ReadDataWithoutChunks(element, compression, encoding); - + var data = element.GetRequiredPropertyCustom("data", e => ReadDataWithoutChunks(e, compression, encoding)); var x = element.GetRequiredProperty("x"); var y = element.GetRequiredProperty("y"); var width = element.GetRequiredProperty("width"); diff --git a/src/DotTiled/Serialization/Tmj/TmjReaderBase.ObjectLayer.cs b/src/DotTiled/Serialization/Tmj/TmjReaderBase.ObjectLayer.cs index 21e58a0..6eb5f8a 100644 --- a/src/DotTiled/Serialization/Tmj/TmjReaderBase.ObjectLayer.cs +++ b/src/DotTiled/Serialization/Tmj/TmjReaderBase.ObjectLayer.cs @@ -76,12 +76,13 @@ public abstract partial class TmjReaderBase List polygonDefault = null; List polylineDefault = null; List propertiesDefault = []; + Optional gidDefault = Optional.Empty; var template = element.GetOptionalProperty("template"); if (template.HasValue) { var resolvedTemplate = _externalTemplateResolver(template.Value); - var templObj = resolvedTemplate.Object; + var templObj = resolvedTemplate.Object.Clone(); idDefault = templObj.ID; nameDefault = templObj.Name; @@ -97,10 +98,11 @@ public abstract partial class TmjReaderBase pointDefault = templObj is PointObject; polygonDefault = (templObj is PolygonObject polygonObj) ? polygonObj.Points : null; polylineDefault = (templObj is PolylineObject polylineObj) ? polylineObj.Points : null; + gidDefault = (templObj is TileObject tileObj) ? tileObj.GID : Optional.Empty; } var ellipse = element.GetOptionalProperty("ellipse").GetValueOr(ellipseDefault); - var gid = element.GetOptionalProperty("gid"); + var gid = element.GetOptionalProperty("gid").GetValueOrOptional(gidDefault); var height = element.GetOptionalProperty("height").GetValueOr(heightDefault); var id = element.GetOptionalProperty("id").GetValueOrOptional(idDefault); var name = element.GetOptionalProperty("name").GetValueOr(nameDefault); diff --git a/src/DotTiled/Serialization/Tmx/TmxReaderBase.ObjectLayer.cs b/src/DotTiled/Serialization/Tmx/TmxReaderBase.ObjectLayer.cs index a53cee6..b6fa3dc 100644 --- a/src/DotTiled/Serialization/Tmx/TmxReaderBase.ObjectLayer.cs +++ b/src/DotTiled/Serialization/Tmx/TmxReaderBase.ObjectLayer.cs @@ -74,7 +74,7 @@ public abstract partial class TmxReaderBase var template = _reader.GetOptionalAttribute("template"); DotTiled.Object obj = null; if (template.HasValue) - obj = _externalTemplateResolver(template.Value).Object; + obj = _externalTemplateResolver(template.Value).Object.Clone(); uint idDefault = obj?.ID.GetValueOr(0) ?? 0; string nameDefault = obj?.Name ?? "";