Fix rest of model to use new structure and prepare docs

This commit is contained in:
Daniel Cronqvist 2024-08-24 23:08:24 +02:00
parent b46eed774a
commit 4580772ced
25 changed files with 94 additions and 200 deletions

View file

@ -0,0 +1 @@
# Accessing properties

View file

@ -3,4 +3,5 @@
- href: quickstart.md
- name: Essentials
- href: loading-a-map.md
- href: loading-a-map.md
- href: accessing-properties.md

View file

@ -91,7 +91,7 @@ public static partial class DotTiledAssert
AssertEqual(expected.NextObjectID, actual.NextObjectID, nameof(Map.NextObjectID));
AssertEqual(expected.Infinite, actual.Infinite, nameof(Map.Infinite));
AssertPropertiesList(actual.Properties, expected.Properties);
AssertProperties(actual.Properties, expected.Properties);
Assert.NotNull(actual.Tilesets);
AssertEqual(expected.Tilesets.Count, actual.Tilesets.Count, "Tilesets.Count");

View file

@ -4,24 +4,7 @@ namespace DotTiled.Tests;
public static partial class DotTiledAssert
{
internal static void AssertProperties(Dictionary<string, IProperty>? expected, Dictionary<string, IProperty>? actual)
{
if (expected is null)
{
Assert.Null(actual);
return;
}
Assert.NotNull(actual);
AssertEqual(expected.Count, actual.Count, "Properties.Count");
foreach (var kvp in expected)
{
Assert.Contains(kvp.Key, actual.Keys);
AssertProperty((dynamic)kvp.Value, (dynamic)actual[kvp.Key]);
}
}
internal static void AssertPropertiesList(IList<IProperty>? expected, IList<IProperty>? actual)
internal static void AssertProperties(IList<IProperty>? expected, IList<IProperty>? actual)
{
if (expected is null)
{
@ -36,17 +19,13 @@ public static partial class DotTiledAssert
Assert.Contains(actual, p => p.Name == prop.Name);
var actualProp = actual.First(p => p.Name == prop.Name);
AssertEqual(prop.Type, actualProp.Type, "Property.Type");
AssertEqual(prop.Name, actualProp.Name, "Property.Name");
AssertProperty((dynamic)prop, (dynamic)actualProp);
}
}
private static void AssertProperty(IProperty expected, IProperty actual)
{
AssertEqual(expected.Type, actual.Type, "Property.Type");
AssertEqual(expected.Name, actual.Name, "Property.Name");
AssertProperties((dynamic)actual, (dynamic)expected);
}
private static void AssertProperty(StringProperty expected, StringProperty actual) => AssertEqual(expected.Value, actual.Value, "StringProperty.Value");
private static void AssertProperty(IntProperty expected, IntProperty actual) => AssertEqual(expected.Value, actual.Value, "IntProperty.Value");
@ -64,6 +43,6 @@ public static partial class DotTiledAssert
private static void AssertProperty(ClassProperty expected, ClassProperty actual)
{
AssertEqual(expected.PropertyType, actual.PropertyType, "ClassProperty.PropertyType");
AssertPropertiesList(expected.Value, actual.Value);
AssertProperties(expected.Value, actual.Value);
}
}

View file

@ -49,16 +49,15 @@ public partial class TestData
Width = 1,
Height = 1
},
Properties = new Dictionary<string, IProperty>
{
["tilesetbool"] = new BoolProperty { Name = "tilesetbool", Value = true },
["tilesetcolor"] = new ColorProperty { Name = "tilesetcolor", Value = Color.Parse("#ffff0000", CultureInfo.InvariantCulture) },
["tilesetfile"] = new FileProperty { Name = "tilesetfile", Value = "" },
["tilesetfloat"] = new FloatProperty { Name = "tilesetfloat", Value = 5.2f },
["tilesetint"] = new IntProperty { Name = "tilesetint", Value = 9 },
["tilesetobject"] = new ObjectProperty { Name = "tilesetobject", Value = 0 },
["tilesetstring"] = new StringProperty { Name = "tilesetstring", Value = "hello world!" }
},
Properties = [
new BoolProperty { Name = "tilesetbool", Value = true },
new ColorProperty { Name = "tilesetcolor", Value = Color.Parse("#ffff0000", CultureInfo.InvariantCulture) },
new FileProperty { Name = "tilesetfile", Value = "" },
new FloatProperty { Name = "tilesetfloat", Value = 5.2f },
new IntProperty { Name = "tilesetint", Value = 9 },
new ObjectProperty { Name = "tilesetobject", Value = 0 },
new StringProperty { Name = "tilesetstring", Value = "hello world!" }
],
Tiles = [
new Tile
{

View file

@ -95,10 +95,9 @@ public partial class TestData
new Vector2(35.6667f, 32.3333f)
],
Template = fileExt == "tmx" ? "poly.tx" : "poly.tj",
Properties = new Dictionary<string, IProperty>
{
["templateprop"] = new StringProperty { Name = "templateprop", Value = "helo there" }
}
Properties = [
new StringProperty { Name = "templateprop", Value = "helo there" }
]
},
new TileObject
{

View file

@ -7,7 +7,7 @@ namespace DotTiled.Model;
/// To check the type of a layer, <see href="https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching">use C# pattern matching</see>,
/// or some other mechanism to determine the type of the layer at runtime.
/// </summary>
public abstract class BaseLayer
public abstract class BaseLayer : HasPropertiesBase
{
/// <summary>
/// Unique ID of the layer. Each layer that is added to a map gets a unique ID. Even if a layer is deleted, no layer ever gets the same ID.
@ -62,5 +62,8 @@ public abstract class BaseLayer
/// <summary>
/// Layer properties.
/// </summary>
public Dictionary<string, IProperty>? Properties { get; set; }
public List<IProperty> Properties { get; set; } = [];
/// <inheritdoc/>
public override IList<IProperty> GetProperties() => Properties;
}

View file

@ -5,7 +5,7 @@ namespace DotTiled.Model;
/// <summary>
/// Base class for objects in object layers.
/// </summary>
public abstract class Object
public abstract class Object : HasPropertiesBase
{
/// <summary>
/// Unique ID of the objects. Each object that is placed on a map gets a unique ID. Even if an object was deleted, no object gets the same ID.
@ -60,5 +60,8 @@ public abstract class Object
/// <summary>
/// Object properties.
/// </summary>
public Dictionary<string, IProperty>? Properties { get; set; }
public List<IProperty> Properties { get; set; } = [];
/// <inheritdoc/>
public override IList<IProperty> GetProperties() => Properties;
}

View file

@ -6,7 +6,7 @@ namespace DotTiled.Model;
/// Represents a single tile in a tileset, when using a collection of images to represent the tileset.
/// <see href="https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#tile">Tiled documentation for Tileset tiles</see>
/// </summary>
public class Tile
public class Tile : HasPropertiesBase
{
/// <summary>
/// The local tile ID within its tileset.
@ -46,7 +46,10 @@ public class Tile
/// <summary>
/// Tile properties.
/// </summary>
public Dictionary<string, IProperty>? Properties { get; set; }
public List<IProperty> Properties { get; set; } = [];
/// <inheritdoc/>
public override IList<IProperty> GetProperties() => Properties;
/// <summary>
/// The image representing this tile. Only used for tilesets that composed of a collection of images.

View file

@ -8,7 +8,7 @@ namespace DotTiled.Model;
public enum ObjectAlignment
{
/// <summary>
/// The alignment is unspecified. Tile objects will use <see cref="BottomLeft"/> in orthogonal maps, and <see cref="Bottom"/> in isometric maps.
/// The alignment is unspecified. Tile objects will use <see cref="BottomLeft"/> in orthogonal maps, and <see cref="Bottom"/> in isometric maps.
/// </summary>
Unspecified,
@ -93,7 +93,7 @@ public enum FillMode
/// <summary>
/// A tileset is a collection of tiles that can be used in a tile layer, or by tile objects.
/// </summary>
public class Tileset
public class Tileset : HasPropertiesBase
{
/// <summary>
/// The TMX format version. Is incremented to match minor Tiled releases.
@ -161,7 +161,7 @@ public class Tileset
public ObjectAlignment ObjectAlignment { get; set; } = ObjectAlignment.Unspecified;
/// <summary>
/// The size to use when rendering tiles from thie tileset on a tile layer. When set to <see cref="TileRenderSize.Grid"/>, the tile is drawn at the tile grid size of the map.
/// The size to use when rendering tiles from thie tileset on a tile layer. When set to <see cref="TileRenderSize.Grid"/>, the tile is drawn at the tile grid size of the map.
/// </summary>
public TileRenderSize RenderSize { get; set; } = TileRenderSize.Tile;
@ -188,7 +188,10 @@ public class Tileset
/// <summary>
/// Tileset properties.
/// </summary>
public Dictionary<string, IProperty>? Properties { get; set; }
public List<IProperty> Properties { get; set; } = [];
/// <inheritdoc/>
public override IList<IProperty> GetProperties() => Properties;
// public List<Terrain>? TerrainTypes { get; set; } TODO: Implement Terrain -> Wangset conversion during deserialization

View file

@ -5,7 +5,7 @@ namespace DotTiled.Model;
/// <summary>
/// Represents a Wang color in a Wang set.
/// </summary>
public class WangColor
public class WangColor : HasPropertiesBase
{
/// <summary>
/// The name of this color.
@ -35,5 +35,8 @@ public class WangColor
/// <summary>
/// The Wang color properties.
/// </summary>
public Dictionary<string, IProperty>? Properties { get; set; }
public List<IProperty> Properties { get; set; } = [];
/// <inheritdoc/>
public override IList<IProperty> GetProperties() => Properties;
}

View file

@ -5,7 +5,7 @@ namespace DotTiled.Model;
/// <summary>
/// Defines a list of colors and any number of Wang tiles using these colors.
/// </summary>
public class Wangset
public class Wangset : HasPropertiesBase
{
/// <summary>
/// The name of the Wang set.
@ -25,7 +25,10 @@ public class Wangset
/// <summary>
/// The Wang set properties.
/// </summary>
public Dictionary<string, IProperty>? Properties { get; set; }
public List<IProperty> Properties { get; set; } = [];
/// <inheritdoc/>
public override IList<IProperty> GetProperties() => Properties;
// Up to 254 Wang colors
/// <summary>

View file

@ -76,39 +76,7 @@ internal static partial class Helpers
internal static List<IProperty> CreateInstanceOfCustomClass(CustomClassDefinition customClassDefinition) =>
customClassDefinition.Members.Select(x => x.Clone()).ToList();
internal static Dictionary<string, IProperty> MergeProperties(Dictionary<string, IProperty>? baseProperties, Dictionary<string, IProperty>? overrideProperties)
{
if (baseProperties is null)
return overrideProperties ?? new Dictionary<string, IProperty>();
if (overrideProperties is null)
return baseProperties;
var result = baseProperties.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Clone());
foreach (var (key, value) in overrideProperties)
{
if (!result.TryGetValue(key, out var baseProp))
{
result[key] = value;
continue;
}
else
{
if (value is ClassProperty classProp)
{
((ClassProperty)baseProp).Value = MergePropertiesList(((ClassProperty)baseProp).Value, classProp.Value);
}
else
{
result[key] = value;
}
}
}
return result;
}
internal static IList<IProperty> MergePropertiesList(IList<IProperty>? baseProperties, IList<IProperty>? overrideProperties)
internal static IList<IProperty> MergeProperties(IList<IProperty>? baseProperties, IList<IProperty>? overrideProperties)
{
if (baseProperties is null)
return overrideProperties ?? [];
@ -129,7 +97,7 @@ internal static partial class Helpers
var existingProp = result.First(x => x.Name == overrideProp.Name);
if (existingProp is ClassProperty classProp)
{
classProp.Value = MergePropertiesList(classProp.Value, ((ClassProperty)overrideProp).Value);
classProp.Value = MergeProperties(classProp.Value, ((ClassProperty)overrideProp).Value);
}
else
{

View file

@ -23,7 +23,7 @@ internal partial class Tmj
var offsetY = element.GetOptionalProperty<float>("offsety", 0.0f);
var parallaxX = element.GetOptionalProperty<float>("parallaxx", 1.0f);
var parallaxY = element.GetOptionalProperty<float>("parallaxy", 1.0f);
var properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", e => ReadProperties(e, customTypeDefinitions), null);
var properties = element.GetOptionalPropertyCustom("properties", e => ReadProperties(e, customTypeDefinitions), []);
var layers = element.GetOptionalPropertyCustom<List<BaseLayer>>("layers", e => e.GetValueAsList<BaseLayer>(el => ReadLayer(el, externalTemplateResolver, customTypeDefinitions)), []);
return new Group

View file

@ -21,7 +21,7 @@ internal partial class Tmj
var offsetY = element.GetOptionalProperty<float>("offsety", 0.0f);
var parallaxX = element.GetOptionalProperty<float>("parallaxx", 1.0f);
var parallaxY = element.GetOptionalProperty<float>("parallaxy", 1.0f);
var properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", e => ReadProperties(e, customTypeDefinitions), null);
var properties = element.GetOptionalPropertyCustom("properties", e => ReadProperties(e, customTypeDefinitions), []);
var image = element.GetRequiredProperty<string>("image");
var repeatX = element.GetOptionalProperty<bool>("repeatx", false);

View file

@ -58,7 +58,7 @@ internal partial class Tmj
var nextObjectID = element.GetRequiredProperty<uint>("nextobjectid");
var infinite = element.GetOptionalProperty<bool>("infinite", false);
var properties = element.GetOptionalPropertyCustom<List<IProperty>?>("properties", el => ReadPropertiesList(el, customTypeDefinitions), null);
var properties = element.GetOptionalPropertyCustom("properties", el => ReadProperties(el, customTypeDefinitions), []);
List<BaseLayer> layers = element.GetOptionalPropertyCustom<List<BaseLayer>>("layers", e => e.GetValueAsList<BaseLayer>(el => ReadLayer(el, externalTemplateResolver, customTypeDefinitions)), []);
List<Tileset> tilesets = element.GetOptionalPropertyCustom<List<Tileset>>("tilesets", e => e.GetValueAsList<Tileset>(el => ReadTileset(el, externalTilesetResolver, externalTemplateResolver, customTypeDefinitions)), []);
@ -84,7 +84,7 @@ internal partial class Tmj
NextLayerID = nextLayerID,
NextObjectID = nextObjectID,
Infinite = infinite,
Properties = properties ?? [],
Properties = properties,
Tilesets = tilesets,
Layers = layers
};

View file

@ -24,7 +24,7 @@ internal partial class Tmj
var offsetY = element.GetOptionalProperty<float>("offsety", 0.0f);
var parallaxX = element.GetOptionalProperty<float>("parallaxx", 1.0f);
var parallaxY = element.GetOptionalProperty<float>("parallaxy", 1.0f);
var properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", e => ReadProperties(e, customTypeDefinitions), null);
var properties = element.GetOptionalPropertyCustom("properties", e => ReadProperties(e, customTypeDefinitions), []);
var x = element.GetOptionalProperty<uint>("x", 0);
var y = element.GetOptionalProperty<uint>("y", 0);
@ -82,7 +82,7 @@ internal partial class Tmj
bool pointDefault = false;
List<Vector2>? polygonDefault = null;
List<Vector2>? polylineDefault = null;
Dictionary<string, IProperty>? propertiesDefault = null;
List<IProperty> propertiesDefault = [];
var template = element.GetOptionalProperty<string?>("template", null);
if (template is not null)
@ -114,7 +114,7 @@ internal partial class Tmj
var point = element.GetOptionalProperty<bool>("point", pointDefault);
var polygon = element.GetOptionalPropertyCustom<List<Vector2>?>("polygon", ReadPoints, polygonDefault);
var polyline = element.GetOptionalPropertyCustom<List<Vector2>?>("polyline", ReadPoints, polylineDefault);
var properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", e => ReadProperties(e, customTypeDefinitions), propertiesDefault);
var properties = element.GetOptionalPropertyCustom("properties", e => ReadProperties(e, customTypeDefinitions), propertiesDefault);
var rotation = element.GetOptionalProperty<float>("rotation", rotationDefault);
var text = element.GetOptionalPropertyCustom<TextObject?>("text", ReadText, null);
var type = element.GetOptionalProperty<string>("type", typeDefault);

View file

@ -8,42 +8,7 @@ namespace DotTiled.Serialization.Tmj;
internal partial class Tmj
{
internal static Dictionary<string, IProperty> ReadProperties(
JsonElement element,
IReadOnlyCollection<ICustomTypeDefinition> customTypeDefinitions) =>
element.GetValueAsList<IProperty>(e =>
{
var name = e.GetRequiredProperty<string>("name");
var type = e.GetOptionalPropertyParseable<PropertyType>("type", s => s switch
{
"string" => PropertyType.String,
"int" => PropertyType.Int,
"float" => PropertyType.Float,
"bool" => PropertyType.Bool,
"color" => PropertyType.Color,
"file" => PropertyType.File,
"object" => PropertyType.Object,
"class" => PropertyType.Class,
_ => throw new JsonException("Invalid property type")
}, PropertyType.String);
IProperty property = type switch
{
PropertyType.String => new StringProperty { Name = name, Value = e.GetRequiredProperty<string>("value") },
PropertyType.Int => new IntProperty { Name = name, Value = e.GetRequiredProperty<int>("value") },
PropertyType.Float => new FloatProperty { Name = name, Value = e.GetRequiredProperty<float>("value") },
PropertyType.Bool => new BoolProperty { Name = name, Value = e.GetRequiredProperty<bool>("value") },
PropertyType.Color => new ColorProperty { Name = name, Value = e.GetRequiredPropertyParseable<Color>("value") },
PropertyType.File => new FileProperty { Name = name, Value = e.GetRequiredProperty<string>("value") },
PropertyType.Object => new ObjectProperty { Name = name, Value = e.GetRequiredProperty<uint>("value") },
PropertyType.Class => ReadClassProperty(e, customTypeDefinitions),
_ => throw new JsonException("Invalid property type")
};
return property!;
}).ToDictionary(p => p.Name);
internal static List<IProperty> ReadPropertiesList(
internal static List<IProperty> ReadProperties(
JsonElement element,
IReadOnlyCollection<ICustomTypeDefinition> customTypeDefinitions) =>
element.GetValueAsList<IProperty>(e =>
@ -92,7 +57,7 @@ internal partial class Tmj
var propsInType = Helpers.CreateInstanceOfCustomClass(ccd);
var props = element.GetOptionalPropertyCustom<List<IProperty>>("value", el => ReadCustomClassProperties(el, ccd, customTypeDefinitions), []);
var mergedProps = Helpers.MergePropertiesList(propsInType, props);
var mergedProps = Helpers.MergeProperties(propsInType, props);
return new ClassProperty
{

View file

@ -35,7 +35,7 @@ internal partial class Tmj
var opacity = element.GetOptionalProperty<float>("opacity", 1.0f);
var parallaxx = element.GetOptionalProperty<float>("parallaxx", 1.0f);
var parallaxy = element.GetOptionalProperty<float>("parallaxy", 1.0f);
var properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", e => ReadProperties(e, customTypeDefinitions), null);
var properties = element.GetOptionalPropertyCustom("properties", e => ReadProperties(e, customTypeDefinitions), []);
var repeatX = element.GetOptionalProperty<bool>("repeatx", false);
var repeatY = element.GetOptionalProperty<bool>("repeaty", false);
var startX = element.GetOptionalProperty<int>("startx", 0);

View file

@ -44,7 +44,7 @@ internal partial class Tmj
"bottomright" => ObjectAlignment.BottomRight,
_ => throw new JsonException($"Unknown object alignment '{s}'")
}, ObjectAlignment.Unspecified);
var properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", el => ReadProperties(el, customTypeDefinitions), null);
var properties = element.GetOptionalPropertyCustom("properties", el => ReadProperties(el, customTypeDefinitions), []);
var source = element.GetOptionalProperty<string?>("source", null);
var spacing = element.GetOptionalProperty<uint?>("spacing", null);
var tileCount = element.GetOptionalProperty<uint?>("tilecount", null);
@ -176,7 +176,7 @@ internal partial class Tmj
var height = e.GetOptionalProperty<uint>("height", imageHeight ?? 0);
var objectGroup = e.GetOptionalPropertyCustom<ObjectLayer?>("objectgroup", e => ReadObjectLayer(e, externalTemplateResolver, customTypeDefinitions), null);
var probability = e.GetOptionalProperty<float>("probability", 0.0f);
var properties = e.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", el => ReadProperties(el, customTypeDefinitions), null);
var properties = e.GetOptionalPropertyCustom("properties", el => ReadProperties(el, customTypeDefinitions), []);
// var terrain, replaced by wangsets
var type = e.GetOptionalProperty<string>("type", "");
@ -223,7 +223,7 @@ internal partial class Tmj
var @clalss = element.GetOptionalProperty<string>("class", "");
var colors = element.GetOptionalPropertyCustom<List<WangColor>>("colors", e => e.GetValueAsList<WangColor>(el => ReadWangColor(el, customTypeDefinitions)), []);
var name = element.GetRequiredProperty<string>("name");
var properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", e => ReadProperties(e, customTypeDefinitions), null);
var properties = element.GetOptionalPropertyCustom("properties", e => ReadProperties(e, customTypeDefinitions), []);
var tile = element.GetOptionalProperty<int>("tile", 0);
var type = element.GetOptionalProperty<string>("type", "");
var wangTiles = element.GetOptionalPropertyCustom<List<WangTile>>("wangtiles", e => e.GetValueAsList<WangTile>(ReadWangTile), []);
@ -247,7 +247,7 @@ internal partial class Tmj
var color = element.GetRequiredPropertyParseable<Color>("color", s => Color.Parse(s, CultureInfo.InvariantCulture));
var name = element.GetRequiredProperty<string>("name");
var probability = element.GetOptionalProperty<float>("probability", 1.0f);
var properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", e => ReadProperties(e, customTypeDefinitions), null);
var properties = element.GetOptionalPropertyCustom("properties", e => ReadProperties(e, customTypeDefinitions), []);
var tile = element.GetOptionalProperty<int>("tile", 0);
return new WangColor

View file

@ -69,7 +69,7 @@ internal partial class Tmx
reader.ProcessChildren("map", (r, elementName) => elementName switch
{
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadPropertiesList(r, customTypeDefinitions), "Properties"),
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r, customTypeDefinitions), "Properties"),
"tileset" => () => tilesets.Add(ReadTileset(r, externalTilesetResolver, externalTemplateResolver, customTypeDefinitions)),
"layer" => () => layers.Add(ReadTileLayer(r, infinite, customTypeDefinitions)),
"objectgroup" => () => layers.Add(ReadObjectLayer(r, externalTemplateResolver, customTypeDefinitions)),

View file

@ -39,7 +39,7 @@ internal partial class Tmx
}) ?? DrawOrder.TopDown;
// Elements
Dictionary<string, IProperty>? properties = null;
List<IProperty>? properties = null;
List<Model.Object> objects = [];
reader.ProcessChildren("objectgroup", (r, elementName) => elementName switch
@ -66,7 +66,7 @@ internal partial class Tmx
ParallaxX = parallaxX,
ParallaxY = parallaxY,
Color = color,
Properties = properties,
Properties = properties ?? [],
DrawOrder = drawOrder,
Objects = objects
};
@ -93,7 +93,7 @@ internal partial class Tmx
float rotationDefault = obj?.Rotation ?? 0f;
uint? gidDefault = obj is TileObject tileObj ? tileObj.GID : null;
bool visibleDefault = obj?.Visible ?? true;
Dictionary<string, IProperty>? propertiesDefault = obj?.Properties ?? null;
List<IProperty>? propertiesDefault = obj?.Properties ?? null;
var id = reader.GetOptionalAttributeParseable<uint>("id") ?? idDefault;
var name = reader.GetOptionalAttribute("name") ?? nameDefault;
@ -109,11 +109,11 @@ internal partial class Tmx
// Elements
Model.Object? foundObject = null;
int propertiesCounter = 0;
Dictionary<string, IProperty>? properties = propertiesDefault;
List<IProperty>? properties = propertiesDefault;
reader.ProcessChildren("object", (r, elementName) => elementName switch
{
"properties" => () => Helpers.SetAtMostOnceUsingCounter(ref properties, Helpers.MergeProperties(properties, ReadProperties(r, customTypeDefinitions)), "Properties", ref propertiesCounter),
"properties" => () => Helpers.SetAtMostOnceUsingCounter(ref properties, Helpers.MergeProperties(properties, ReadProperties(r, customTypeDefinitions)).ToList(), "Properties", ref propertiesCounter),
"ellipse" => () => Helpers.SetAtMostOnce(ref foundObject, ReadEllipseObject(r), "Object marker"),
"point" => () => Helpers.SetAtMostOnce(ref foundObject, ReadPointObject(r), "Object marker"),
"polygon" => () => Helpers.SetAtMostOnce(ref foundObject, ReadPolygonObject(r), "Object marker"),
@ -139,7 +139,7 @@ internal partial class Tmx
foundObject.Height = height;
foundObject.Rotation = rotation;
foundObject.Visible = visible;
foundObject.Properties = properties;
foundObject.Properties = properties ?? [];
foundObject.Template = template;
return OverrideObject(obj, foundObject);
@ -161,7 +161,7 @@ internal partial class Tmx
obj.Height = foundObject.Height;
obj.Rotation = foundObject.Rotation;
obj.Visible = foundObject.Visible;
obj.Properties = Helpers.MergeProperties(obj.Properties, foundObject.Properties);
obj.Properties = Helpers.MergeProperties(obj.Properties, foundObject.Properties).ToList();
obj.Template = foundObject.Template;
return obj;
}

View file

@ -7,43 +7,7 @@ namespace DotTiled.Serialization.Tmx;
internal partial class Tmx
{
internal static Dictionary<string, IProperty> ReadProperties(
XmlReader reader,
IReadOnlyCollection<ICustomTypeDefinition> customTypeDefinitions)
{
return reader.ReadList("properties", "property", (r) =>
{
var name = r.GetRequiredAttribute("name");
var type = r.GetOptionalAttributeEnum<PropertyType>("type", (s) => s switch
{
"string" => PropertyType.String,
"int" => PropertyType.Int,
"float" => PropertyType.Float,
"bool" => PropertyType.Bool,
"color" => PropertyType.Color,
"file" => PropertyType.File,
"object" => PropertyType.Object,
"class" => PropertyType.Class,
_ => throw new XmlException("Invalid property type")
}) ?? PropertyType.String;
IProperty property = type switch
{
PropertyType.String => new StringProperty { Name = name, Value = r.GetRequiredAttribute("value") },
PropertyType.Int => new IntProperty { Name = name, Value = r.GetRequiredAttributeParseable<int>("value") },
PropertyType.Float => new FloatProperty { Name = name, Value = r.GetRequiredAttributeParseable<float>("value") },
PropertyType.Bool => new BoolProperty { Name = name, Value = r.GetRequiredAttributeParseable<bool>("value") },
PropertyType.Color => new ColorProperty { Name = name, Value = r.GetRequiredAttributeParseable<Color>("value") },
PropertyType.File => new FileProperty { Name = name, Value = r.GetRequiredAttribute("value") },
PropertyType.Object => new ObjectProperty { Name = name, Value = r.GetRequiredAttributeParseable<uint>("value") },
PropertyType.Class => ReadClassProperty(r, customTypeDefinitions),
_ => throw new XmlException("Invalid property type")
};
return (name, property);
}).ToDictionary(x => x.name, x => x.property);
}
internal static List<IProperty> ReadPropertiesList(
internal static List<IProperty> ReadProperties(
XmlReader reader,
IReadOnlyCollection<ICustomTypeDefinition> customTypeDefinitions)
{
@ -91,8 +55,8 @@ internal partial class Tmx
{
reader.ReadStartElement("property");
var propsInType = Helpers.CreateInstanceOfCustomClass(ccd);
var props = ReadPropertiesList(reader, customTypeDefinitions);
var mergedProps = Helpers.MergePropertiesList(propsInType, props);
var props = ReadProperties(reader, customTypeDefinitions);
var mergedProps = Helpers.MergeProperties(propsInType, props);
reader.ReadEndElement();
return new ClassProperty { Name = name, PropertyType = propertyType, Value = mergedProps };

View file

@ -28,7 +28,7 @@ internal partial class Tmx
var parallaxX = reader.GetOptionalAttributeParseable<float>("parallaxx") ?? 1.0f;
var parallaxY = reader.GetOptionalAttributeParseable<float>("parallaxy") ?? 1.0f;
Dictionary<string, IProperty>? properties = null;
List<IProperty>? properties = null;
Data? data = null;
reader.ProcessChildren("layer", (r, elementName) => elementName switch
@ -55,7 +55,7 @@ internal partial class Tmx
ParallaxX = parallaxX,
ParallaxY = parallaxY,
Data = data,
Properties = properties
Properties = properties ?? []
};
}
@ -78,7 +78,7 @@ internal partial class Tmx
var repeatX = (reader.GetOptionalAttributeParseable<uint>("repeatx") ?? 0) == 1;
var repeatY = (reader.GetOptionalAttributeParseable<uint>("repeaty") ?? 0) == 1;
Dictionary<string, IProperty>? properties = null;
List<IProperty>? properties = null;
Image? image = null;
reader.ProcessChildren("imagelayer", (r, elementName) => elementName switch
@ -102,7 +102,7 @@ internal partial class Tmx
OffsetY = offsetY,
ParallaxX = parallaxX,
ParallaxY = parallaxY,
Properties = properties,
Properties = properties ?? [],
Image = image,
RepeatX = repeatX,
RepeatY = repeatY
@ -125,7 +125,7 @@ internal partial class Tmx
var parallaxX = reader.GetOptionalAttributeParseable<float>("parallaxx") ?? 1.0f;
var parallaxY = reader.GetOptionalAttributeParseable<float>("parallaxy") ?? 1.0f;
Dictionary<string, IProperty>? properties = null;
List<IProperty>? properties = null;
List<BaseLayer> layers = [];
reader.ProcessChildren("group", (r, elementName) => elementName switch
@ -150,7 +150,7 @@ internal partial class Tmx
OffsetY = offsetY,
ParallaxX = parallaxX,
ParallaxY = parallaxY,
Properties = properties,
Properties = properties ?? [],
Layers = layers
};
}

View file

@ -60,7 +60,7 @@ internal partial class Tmx
Image? image = null;
TileOffset? tileOffset = null;
Grid? grid = null;
Dictionary<string, IProperty>? properties = null;
List<IProperty>? properties = null;
List<Wangset>? wangsets = null;
Transformations? transformations = null;
List<Tile> tiles = [];
@ -109,7 +109,7 @@ internal partial class Tmx
Image = image,
TileOffset = tileOffset,
Grid = grid,
Properties = properties,
Properties = properties ?? [],
Wangsets = wangsets,
Transformations = transformations,
Tiles = tiles
@ -219,7 +219,7 @@ internal partial class Tmx
var height = reader.GetOptionalAttributeParseable<uint>("height");
// Elements
Dictionary<string, IProperty>? properties = null;
List<IProperty>? properties = null;
Image? image = null;
ObjectLayer? objectLayer = null;
List<Frame>? animation = null;
@ -247,7 +247,7 @@ internal partial class Tmx
Y = y,
Width = width ?? image?.Width ?? 0,
Height = height ?? image?.Height ?? 0,
Properties = properties,
Properties = properties ?? [],
Image = image,
ObjectLayer = objectLayer,
Animation = animation
@ -269,7 +269,7 @@ internal partial class Tmx
var tile = reader.GetRequiredAttributeParseable<int>("tile");
// Elements
Dictionary<string, IProperty>? properties = null;
List<IProperty>? properties = null;
List<WangColor> wangColors = [];
List<WangTile> wangTiles = [];
@ -289,7 +289,7 @@ internal partial class Tmx
Name = name,
Class = @class,
Tile = tile,
Properties = properties,
Properties = properties ?? [],
WangColors = wangColors,
WangTiles = wangTiles
};
@ -307,7 +307,7 @@ internal partial class Tmx
var probability = reader.GetOptionalAttributeParseable<float>("probability") ?? 0f;
// Elements
Dictionary<string, IProperty>? properties = null;
List<IProperty>? properties = null;
reader.ProcessChildren("wangcolor", (r, elementName) => elementName switch
{
@ -322,7 +322,7 @@ internal partial class Tmx
Color = color,
Tile = tile,
Probability = probability,
Properties = properties
Properties = properties ?? []
};
}