mirror of
https://github.com/dcronqvist/DotTiled.git
synced 2025-02-05 08:52:50 +02:00
Restructure properties API
This commit is contained in:
parent
3a6684a52d
commit
1c1ba326b2
20 changed files with 282 additions and 62 deletions
|
@ -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));
|
||||
|
||||
AssertProperties(actual.Properties, expected.Properties);
|
||||
AssertPropertiesList(actual.Properties, expected.Properties);
|
||||
|
||||
Assert.NotNull(actual.Tilesets);
|
||||
AssertEqual(expected.Tilesets.Count, actual.Tilesets.Count, "Tilesets.Count");
|
||||
|
|
|
@ -21,6 +21,25 @@ public static partial class DotTiledAssert
|
|||
}
|
||||
}
|
||||
|
||||
internal static void AssertPropertiesList(IList<IProperty>? expected, IList<IProperty>? actual)
|
||||
{
|
||||
if (expected is null)
|
||||
{
|
||||
Assert.Null(actual);
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.NotNull(actual);
|
||||
AssertEqual(expected.Count, actual.Count, "Properties.Count");
|
||||
foreach (var prop in expected)
|
||||
{
|
||||
Assert.Contains(actual, p => p.Name == prop.Name);
|
||||
|
||||
var actualProp = actual.First(p => p.Name == prop.Name);
|
||||
AssertProperty((dynamic)prop, (dynamic)actualProp);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AssertProperty(IProperty expected, IProperty actual)
|
||||
{
|
||||
AssertEqual(expected.Type, actual.Type, "Property.Type");
|
||||
|
@ -45,6 +64,6 @@ public static partial class DotTiledAssert
|
|||
private static void AssertProperty(ClassProperty expected, ClassProperty actual)
|
||||
{
|
||||
AssertEqual(expected.PropertyType, actual.PropertyType, "ClassProperty.PropertyType");
|
||||
AssertProperties(expected.Properties, actual.Properties);
|
||||
AssertPropertiesList(expected.Value, actual.Value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,15 +55,15 @@ public partial class TestData
|
|||
}
|
||||
}
|
||||
],
|
||||
Properties = new Dictionary<string, IProperty>
|
||||
{
|
||||
["boolprop"] = new BoolProperty { Name = "boolprop", Value = true },
|
||||
["colorprop"] = new ColorProperty { Name = "colorprop", Value = Color.Parse("#ff55ffff", CultureInfo.InvariantCulture) },
|
||||
["fileprop"] = new FileProperty { Name = "fileprop", Value = "file.txt" },
|
||||
["floatprop"] = new FloatProperty { Name = "floatprop", Value = 4.2f },
|
||||
["intprop"] = new IntProperty { Name = "intprop", Value = 8 },
|
||||
["objectprop"] = new ObjectProperty { Name = "objectprop", Value = 5 },
|
||||
["stringprop"] = new StringProperty { Name = "stringprop", Value = "This is a string, hello world!" }
|
||||
}
|
||||
Properties =
|
||||
[
|
||||
new BoolProperty { Name = "boolprop", Value = true },
|
||||
new ColorProperty { Name = "colorprop", Value = Color.Parse("#ff55ffff", CultureInfo.InvariantCulture) },
|
||||
new FileProperty { Name = "fileprop", Value = "file.txt" },
|
||||
new FloatProperty { Name = "floatprop", Value = 4.2f },
|
||||
new IntProperty { Name = "intprop", Value = 8 },
|
||||
new ObjectProperty { Name = "objectprop", Value = 5 },
|
||||
new StringProperty { Name = "stringprop", Value = "This is a string, hello world!" }
|
||||
]
|
||||
};
|
||||
}
|
||||
|
|
|
@ -55,24 +55,22 @@ public partial class TestData
|
|||
}
|
||||
}
|
||||
],
|
||||
Properties = new Dictionary<string, IProperty>
|
||||
{
|
||||
["customclassprop"] = new ClassProperty
|
||||
Properties = [
|
||||
new ClassProperty
|
||||
{
|
||||
Name = "customclassprop",
|
||||
PropertyType = "CustomClass",
|
||||
Properties = new Dictionary<string, IProperty>
|
||||
{
|
||||
["boolinclass"] = new BoolProperty { Name = "boolinclass", Value = true },
|
||||
["colorinclass"] = new ColorProperty { Name = "colorinclass", Value = Color.Parse("#000000ff", CultureInfo.InvariantCulture) },
|
||||
["fileinclass"] = new FileProperty { Name = "fileinclass", Value = "" },
|
||||
["floatinclass"] = new FloatProperty { Name = "floatinclass", Value = 13.37f },
|
||||
["intinclass"] = new IntProperty { Name = "intinclass", Value = 0 },
|
||||
["objectinclass"] = new ObjectProperty { Name = "objectinclass", Value = 0 },
|
||||
["stringinclass"] = new StringProperty { Name = "stringinclass", Value = "This is a set string" }
|
||||
}
|
||||
Value = [
|
||||
new BoolProperty { Name = "boolinclass", Value = true },
|
||||
new ColorProperty { Name = "colorinclass", Value = Color.Parse("#000000ff", CultureInfo.InvariantCulture) },
|
||||
new FileProperty { Name = "fileinclass", Value = "" },
|
||||
new FloatProperty { Name = "floatinclass", Value = 13.37f },
|
||||
new IntProperty { Name = "intinclass", Value = 0 },
|
||||
new ObjectProperty { Name = "objectinclass", Value = 0 },
|
||||
new StringProperty { Name = "stringinclass", Value = "This is a set string" }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// This comes from map-with-custom-type-props/propertytypes.json
|
||||
|
|
|
@ -90,7 +90,7 @@ public enum StaggerIndex
|
|||
/// <summary>
|
||||
/// Represents a Tiled map.
|
||||
/// </summary>
|
||||
public class Map
|
||||
public class Map : HasPropertiesBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The TMX format version. Is incremented to match minor Tiled releases.
|
||||
|
@ -191,7 +191,10 @@ public class Map
|
|||
/// <summary>
|
||||
/// Map properties.
|
||||
/// </summary>
|
||||
public Dictionary<string, IProperty>? Properties { get; set; }
|
||||
public List<IProperty> Properties { get; set; } = [];
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override IList<IProperty> GetProperties() => Properties;
|
||||
|
||||
/// <summary>
|
||||
/// List of tilesets used by the map.
|
||||
|
|
|
@ -3,7 +3,7 @@ namespace DotTiled.Model;
|
|||
/// <summary>
|
||||
/// Represents a boolean property.
|
||||
/// </summary>
|
||||
public class BoolProperty : IProperty
|
||||
public class BoolProperty : IProperty<bool>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public required string Name { get; set; }
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace DotTiled.Model;
|
||||
|
@ -6,7 +7,7 @@ namespace DotTiled.Model;
|
|||
/// <summary>
|
||||
/// Represents a class property.
|
||||
/// </summary>
|
||||
public class ClassProperty : IProperty
|
||||
public class ClassProperty : IHasProperties, IProperty<IList<IProperty>>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public required string Name { get; set; }
|
||||
|
@ -23,13 +24,32 @@ public class ClassProperty : IProperty
|
|||
/// <summary>
|
||||
/// The properties of the class property.
|
||||
/// </summary>
|
||||
public required Dictionary<string, IProperty> Properties { get; set; }
|
||||
public required IList<IProperty> Value { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IProperty Clone() => new ClassProperty
|
||||
{
|
||||
Name = Name,
|
||||
PropertyType = PropertyType,
|
||||
Properties = Properties.ToDictionary(p => p.Key, p => p.Value.Clone())
|
||||
Value = Value.Select(property => property.Clone()).ToList()
|
||||
};
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IList<IProperty> GetProperties() => Value;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public T GetProperty<T>(string name) where T : IProperty => throw new System.NotImplementedException();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TryGetProperty<T>(string name, [NotNullWhen(true)] out T? property) where T : IProperty
|
||||
{
|
||||
if (Value.FirstOrDefault(_properties => _properties.Name == name) is T prop)
|
||||
{
|
||||
property = prop;
|
||||
return true;
|
||||
}
|
||||
|
||||
property = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ namespace DotTiled.Model;
|
|||
/// <summary>
|
||||
/// Represents a color property.
|
||||
/// </summary>
|
||||
public class ColorProperty : IProperty
|
||||
public class ColorProperty : IProperty<Color>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public required string Name { get; set; }
|
||||
|
|
|
@ -3,7 +3,7 @@ namespace DotTiled.Model;
|
|||
/// <summary>
|
||||
/// Represents a file property.
|
||||
/// </summary>
|
||||
public class FileProperty : IProperty
|
||||
public class FileProperty : IProperty<string>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public required string Name { get; set; }
|
||||
|
|
|
@ -3,7 +3,7 @@ namespace DotTiled.Model;
|
|||
/// <summary>
|
||||
/// Represents a float property.
|
||||
/// </summary>
|
||||
public class FloatProperty : IProperty
|
||||
public class FloatProperty : IProperty<float>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public required string Name { get; set; }
|
||||
|
|
59
src/DotTiled/Model/Properties/IHasProperties.cs
Normal file
59
src/DotTiled/Model/Properties/IHasProperties.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace DotTiled.Model;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for objects that have properties attached to them.
|
||||
/// </summary>
|
||||
public interface IHasProperties
|
||||
{
|
||||
/// <summary>
|
||||
/// The properties attached to the object.
|
||||
/// </summary>
|
||||
IList<IProperty> GetProperties();
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a property of the specified type with the specified name.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the property to get.</typeparam>
|
||||
/// <param name="name">The name of the property to get.</param>
|
||||
/// <param name="property">The property with the specified name, if found.</param>
|
||||
/// <returns>True if a property with the specified name was found; otherwise, false.</returns>
|
||||
bool TryGetProperty<T>(string name, out T? property) where T : IProperty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a property of the specified type with the specified name.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the property to get.</typeparam>
|
||||
/// <param name="name">The name of the property to get.</param>
|
||||
/// <returns>The property with the specified name.</returns>
|
||||
T GetProperty<T>(string name) where T : IProperty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for objects that have properties attached to them.
|
||||
/// </summary>
|
||||
public abstract class HasPropertiesBase : IHasProperties
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public abstract IList<IProperty> GetProperties();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public T GetProperty<T>(string name) where T : IProperty => throw new System.NotImplementedException();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TryGetProperty<T>(string name, [NotNullWhen(true)] out T? property) where T : IProperty
|
||||
{
|
||||
var properties = GetProperties();
|
||||
if (properties.FirstOrDefault(_properties => _properties.Name == name) is T prop)
|
||||
{
|
||||
property = prop;
|
||||
return true;
|
||||
}
|
||||
|
||||
property = default;
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -22,3 +22,15 @@ public interface IProperty
|
|||
/// <returns>An identical, but non-reference-equal, instance of the same property.</returns>
|
||||
IProperty Clone();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for properties that can be attached to objects, tiles, tilesets, maps etc.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the property value.</typeparam>
|
||||
public interface IProperty<T> : IProperty
|
||||
{
|
||||
/// <summary>
|
||||
/// The value of the property.
|
||||
/// </summary>
|
||||
public T Value { get; set; }
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ namespace DotTiled.Model;
|
|||
/// <summary>
|
||||
/// Represents an integer property.
|
||||
/// </summary>
|
||||
public class IntProperty : IProperty
|
||||
public class IntProperty : IProperty<int>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public required string Name { get; set; }
|
||||
|
|
|
@ -3,7 +3,7 @@ namespace DotTiled.Model;
|
|||
/// <summary>
|
||||
/// Represents an object property.
|
||||
/// </summary>
|
||||
public class ObjectProperty : IProperty
|
||||
public class ObjectProperty : IProperty<uint>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public required string Name { get; set; }
|
||||
|
|
|
@ -3,7 +3,7 @@ namespace DotTiled.Model;
|
|||
/// <summary>
|
||||
/// Represents a string property.
|
||||
/// </summary>
|
||||
public class StringProperty : IProperty
|
||||
public class StringProperty : IProperty<string>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public required string Name { get; set; }
|
||||
|
|
|
@ -73,6 +73,9 @@ 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)
|
||||
|
@ -93,7 +96,7 @@ internal static partial class Helpers
|
|||
{
|
||||
if (value is ClassProperty classProp)
|
||||
{
|
||||
((ClassProperty)baseProp).Properties = MergeProperties(((ClassProperty)baseProp).Properties, classProp.Properties);
|
||||
((ClassProperty)baseProp).Value = MergePropertiesList(((ClassProperty)baseProp).Value, classProp.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -105,6 +108,48 @@ internal static partial class Helpers
|
|||
return result;
|
||||
}
|
||||
|
||||
internal static IList<IProperty> MergePropertiesList(IList<IProperty>? baseProperties, IList<IProperty>? overrideProperties)
|
||||
{
|
||||
if (baseProperties is null)
|
||||
return overrideProperties ?? [];
|
||||
|
||||
if (overrideProperties is null)
|
||||
return baseProperties;
|
||||
|
||||
var result = baseProperties.Select(x => x.Clone()).ToList();
|
||||
foreach (var overrideProp in overrideProperties)
|
||||
{
|
||||
if (!result.Any(x => x.Name == overrideProp.Name))
|
||||
{
|
||||
result.Add(overrideProp);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
var existingProp = result.First(x => x.Name == overrideProp.Name);
|
||||
if (existingProp is ClassProperty classProp)
|
||||
{
|
||||
classProp.Value = MergePropertiesList(classProp.Value, ((ClassProperty)overrideProp).Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
ReplacePropertyInList(result, overrideProp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static void ReplacePropertyInList(List<IProperty> properties, IProperty property)
|
||||
{
|
||||
var index = properties.FindIndex(p => p.Name == property.Name);
|
||||
if (index == -1)
|
||||
properties.Add(property);
|
||||
else
|
||||
properties[index] = property;
|
||||
}
|
||||
|
||||
internal static void SetAtMostOnce<T>(ref T? field, T value, string fieldName)
|
||||
{
|
||||
if (field is not null)
|
||||
|
|
|
@ -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<Dictionary<string, IProperty>?>("properties", el => ReadProperties(el, customTypeDefinitions), null);
|
||||
var properties = element.GetOptionalPropertyCustom<List<IProperty>?>("properties", el => ReadPropertiesList(el, customTypeDefinitions), null);
|
||||
|
||||
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
|
||||
};
|
||||
|
|
|
@ -43,6 +43,41 @@ internal partial class Tmj
|
|||
return property!;
|
||||
}).ToDictionary(p => p.Name);
|
||||
|
||||
internal static List<IProperty> ReadPropertiesList(
|
||||
JsonElement element,
|
||||
IReadOnlyCollection<CustomTypeDefinition> 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!;
|
||||
});
|
||||
|
||||
internal static ClassProperty ReadClassProperty(
|
||||
JsonElement element,
|
||||
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||
|
@ -54,28 +89,28 @@ internal partial class Tmj
|
|||
|
||||
if (customTypeDef is CustomClassDefinition ccd)
|
||||
{
|
||||
var propsInType = CreateInstanceOfCustomClass(ccd);
|
||||
var props = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>>("value", el => ReadCustomClassProperties(el, ccd, customTypeDefinitions), []);
|
||||
var propsInType = Helpers.CreateInstanceOfCustomClass(ccd);
|
||||
var props = element.GetOptionalPropertyCustom<List<IProperty>>("value", el => ReadCustomClassProperties(el, ccd, customTypeDefinitions), []);
|
||||
|
||||
var mergedProps = Helpers.MergeProperties(propsInType, props);
|
||||
var mergedProps = Helpers.MergePropertiesList(propsInType, props);
|
||||
|
||||
return new ClassProperty
|
||||
{
|
||||
Name = name,
|
||||
PropertyType = propertyType,
|
||||
Properties = mergedProps
|
||||
Value = props
|
||||
};
|
||||
}
|
||||
|
||||
throw new JsonException($"Unknown custom class '{propertyType}'.");
|
||||
}
|
||||
|
||||
internal static Dictionary<string, IProperty> ReadCustomClassProperties(
|
||||
internal static List<IProperty> ReadCustomClassProperties(
|
||||
JsonElement element,
|
||||
CustomClassDefinition customClassDefinition,
|
||||
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||
{
|
||||
Dictionary<string, IProperty> resultingProps = [];
|
||||
List<IProperty> resultingProps = Helpers.CreateInstanceOfCustomClass(customClassDefinition);
|
||||
|
||||
foreach (var prop in customClassDefinition.Members)
|
||||
{
|
||||
|
@ -95,12 +130,9 @@ internal partial class Tmj
|
|||
_ => throw new JsonException("Invalid property type")
|
||||
};
|
||||
|
||||
resultingProps[prop.Name] = property;
|
||||
Helpers.ReplacePropertyInList(resultingProps, property);
|
||||
}
|
||||
|
||||
return resultingProps;
|
||||
}
|
||||
|
||||
internal static Dictionary<string, IProperty> CreateInstanceOfCustomClass(CustomClassDefinition customClassDefinition) =>
|
||||
customClassDefinition.Members.ToDictionary(m => m.Name, m => m.Clone());
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ internal partial class Tmx
|
|||
var infinite = (reader.GetOptionalAttributeParseable<uint>("infinite") ?? 0) == 1;
|
||||
|
||||
// At most one of
|
||||
Dictionary<string, IProperty>? properties = null;
|
||||
List<IProperty>? properties = null;
|
||||
|
||||
// Any number of
|
||||
List<BaseLayer> layers = [];
|
||||
|
@ -69,7 +69,7 @@ internal partial class Tmx
|
|||
|
||||
reader.ProcessChildren("map", (r, elementName) => elementName switch
|
||||
{
|
||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r, customTypeDefinitions), "Properties"),
|
||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadPropertiesList(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)),
|
||||
|
@ -99,7 +99,7 @@ internal partial class Tmx
|
|||
NextLayerID = nextLayerID,
|
||||
NextObjectID = nextObjectID,
|
||||
Infinite = infinite,
|
||||
Properties = properties,
|
||||
Properties = properties ?? [],
|
||||
Tilesets = tilesets,
|
||||
Layers = layers
|
||||
};
|
||||
|
|
|
@ -43,6 +43,42 @@ internal partial class Tmx
|
|||
}).ToDictionary(x => x.name, x => x.property);
|
||||
}
|
||||
|
||||
internal static List<IProperty> ReadPropertiesList(
|
||||
XmlReader reader,
|
||||
IReadOnlyCollection<CustomTypeDefinition> 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 property;
|
||||
});
|
||||
}
|
||||
|
||||
internal static ClassProperty ReadClassProperty(
|
||||
XmlReader reader,
|
||||
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||
|
@ -54,18 +90,14 @@ internal partial class Tmx
|
|||
if (customTypeDef is CustomClassDefinition ccd)
|
||||
{
|
||||
reader.ReadStartElement("property");
|
||||
var propsInType = CreateInstanceOfCustomClass(ccd);
|
||||
var props = ReadProperties(reader, customTypeDefinitions);
|
||||
|
||||
var mergedProps = Helpers.MergeProperties(propsInType, props);
|
||||
var propsInType = Helpers.CreateInstanceOfCustomClass(ccd);
|
||||
var props = ReadPropertiesList(reader, customTypeDefinitions);
|
||||
var mergedProps = Helpers.MergePropertiesList(propsInType, props);
|
||||
|
||||
reader.ReadEndElement();
|
||||
return new ClassProperty { Name = name, PropertyType = propertyType, Properties = mergedProps };
|
||||
return new ClassProperty { Name = name, PropertyType = propertyType, Value = mergedProps };
|
||||
}
|
||||
|
||||
throw new XmlException($"Unkonwn custom class definition: {propertyType}");
|
||||
}
|
||||
|
||||
internal static Dictionary<string, IProperty> CreateInstanceOfCustomClass(CustomClassDefinition customClassDefinition) =>
|
||||
customClassDefinition.Members.ToDictionary(m => m.Name, m => m.Clone());
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue