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.NextObjectID, actual.NextObjectID, nameof(Map.NextObjectID));
|
||||||
AssertEqual(expected.Infinite, actual.Infinite, nameof(Map.Infinite));
|
AssertEqual(expected.Infinite, actual.Infinite, nameof(Map.Infinite));
|
||||||
|
|
||||||
AssertProperties(actual.Properties, expected.Properties);
|
AssertPropertiesList(actual.Properties, expected.Properties);
|
||||||
|
|
||||||
Assert.NotNull(actual.Tilesets);
|
Assert.NotNull(actual.Tilesets);
|
||||||
AssertEqual(expected.Tilesets.Count, actual.Tilesets.Count, "Tilesets.Count");
|
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)
|
private static void AssertProperty(IProperty expected, IProperty actual)
|
||||||
{
|
{
|
||||||
AssertEqual(expected.Type, actual.Type, "Property.Type");
|
AssertEqual(expected.Type, actual.Type, "Property.Type");
|
||||||
|
@ -45,6 +64,6 @@ public static partial class DotTiledAssert
|
||||||
private static void AssertProperty(ClassProperty expected, ClassProperty actual)
|
private static void AssertProperty(ClassProperty expected, ClassProperty actual)
|
||||||
{
|
{
|
||||||
AssertEqual(expected.PropertyType, actual.PropertyType, "ClassProperty.PropertyType");
|
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>
|
Properties =
|
||||||
{
|
[
|
||||||
["boolprop"] = new BoolProperty { Name = "boolprop", Value = true },
|
new BoolProperty { Name = "boolprop", Value = true },
|
||||||
["colorprop"] = new ColorProperty { Name = "colorprop", Value = Color.Parse("#ff55ffff", CultureInfo.InvariantCulture) },
|
new ColorProperty { Name = "colorprop", Value = Color.Parse("#ff55ffff", CultureInfo.InvariantCulture) },
|
||||||
["fileprop"] = new FileProperty { Name = "fileprop", Value = "file.txt" },
|
new FileProperty { Name = "fileprop", Value = "file.txt" },
|
||||||
["floatprop"] = new FloatProperty { Name = "floatprop", Value = 4.2f },
|
new FloatProperty { Name = "floatprop", Value = 4.2f },
|
||||||
["intprop"] = new IntProperty { Name = "intprop", Value = 8 },
|
new IntProperty { Name = "intprop", Value = 8 },
|
||||||
["objectprop"] = new ObjectProperty { Name = "objectprop", Value = 5 },
|
new ObjectProperty { Name = "objectprop", Value = 5 },
|
||||||
["stringprop"] = new StringProperty { Name = "stringprop", Value = "This is a string, hello world!" }
|
new StringProperty { Name = "stringprop", Value = "This is a string, hello world!" }
|
||||||
}
|
]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,24 +55,22 @@ public partial class TestData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
Properties = new Dictionary<string, IProperty>
|
Properties = [
|
||||||
{
|
new ClassProperty
|
||||||
["customclassprop"] = new ClassProperty
|
|
||||||
{
|
{
|
||||||
Name = "customclassprop",
|
Name = "customclassprop",
|
||||||
PropertyType = "CustomClass",
|
PropertyType = "CustomClass",
|
||||||
Properties = new Dictionary<string, IProperty>
|
Value = [
|
||||||
{
|
new BoolProperty { Name = "boolinclass", Value = true },
|
||||||
["boolinclass"] = new BoolProperty { Name = "boolinclass", Value = true },
|
new ColorProperty { Name = "colorinclass", Value = Color.Parse("#000000ff", CultureInfo.InvariantCulture) },
|
||||||
["colorinclass"] = new ColorProperty { Name = "colorinclass", Value = Color.Parse("#000000ff", CultureInfo.InvariantCulture) },
|
new FileProperty { Name = "fileinclass", Value = "" },
|
||||||
["fileinclass"] = new FileProperty { Name = "fileinclass", Value = "" },
|
new FloatProperty { Name = "floatinclass", Value = 13.37f },
|
||||||
["floatinclass"] = new FloatProperty { Name = "floatinclass", Value = 13.37f },
|
new IntProperty { Name = "intinclass", Value = 0 },
|
||||||
["intinclass"] = new IntProperty { Name = "intinclass", Value = 0 },
|
new ObjectProperty { Name = "objectinclass", Value = 0 },
|
||||||
["objectinclass"] = new ObjectProperty { Name = "objectinclass", Value = 0 },
|
new StringProperty { Name = "stringinclass", Value = "This is a set string" }
|
||||||
["stringinclass"] = new StringProperty { Name = "stringinclass", Value = "This is a set string" }
|
]
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
// This comes from map-with-custom-type-props/propertytypes.json
|
// This comes from map-with-custom-type-props/propertytypes.json
|
||||||
|
|
|
@ -90,7 +90,7 @@ public enum StaggerIndex
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a Tiled map.
|
/// Represents a Tiled map.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Map
|
public class Map : HasPropertiesBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The TMX format version. Is incremented to match minor Tiled releases.
|
/// The TMX format version. Is incremented to match minor Tiled releases.
|
||||||
|
@ -191,7 +191,10 @@ public class Map
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Map properties.
|
/// Map properties.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Dictionary<string, IProperty>? Properties { get; set; }
|
public List<IProperty> Properties { get; set; } = [];
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override IList<IProperty> GetProperties() => Properties;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// List of tilesets used by the map.
|
/// List of tilesets used by the map.
|
||||||
|
|
|
@ -3,7 +3,7 @@ namespace DotTiled.Model;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a boolean property.
|
/// Represents a boolean property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class BoolProperty : IProperty
|
public class BoolProperty : IProperty<bool>
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace DotTiled.Model;
|
namespace DotTiled.Model;
|
||||||
|
@ -6,7 +7,7 @@ namespace DotTiled.Model;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a class property.
|
/// Represents a class property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ClassProperty : IProperty
|
public class ClassProperty : IHasProperties, IProperty<IList<IProperty>>
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
|
@ -23,13 +24,32 @@ public class ClassProperty : IProperty
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The properties of the class property.
|
/// The properties of the class property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public required Dictionary<string, IProperty> Properties { get; set; }
|
public required IList<IProperty> Value { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IProperty Clone() => new ClassProperty
|
public IProperty Clone() => new ClassProperty
|
||||||
{
|
{
|
||||||
Name = Name,
|
Name = Name,
|
||||||
PropertyType = PropertyType,
|
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>
|
/// <summary>
|
||||||
/// Represents a color property.
|
/// Represents a color property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ColorProperty : IProperty
|
public class ColorProperty : IProperty<Color>
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
|
|
|
@ -3,7 +3,7 @@ namespace DotTiled.Model;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a file property.
|
/// Represents a file property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FileProperty : IProperty
|
public class FileProperty : IProperty<string>
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
|
|
|
@ -3,7 +3,7 @@ namespace DotTiled.Model;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a float property.
|
/// Represents a float property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FloatProperty : IProperty
|
public class FloatProperty : IProperty<float>
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public required string Name { get; set; }
|
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>
|
/// <returns>An identical, but non-reference-equal, instance of the same property.</returns>
|
||||||
IProperty Clone();
|
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>
|
/// <summary>
|
||||||
/// Represents an integer property.
|
/// Represents an integer property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class IntProperty : IProperty
|
public class IntProperty : IProperty<int>
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
|
|
|
@ -3,7 +3,7 @@ namespace DotTiled.Model;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an object property.
|
/// Represents an object property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ObjectProperty : IProperty
|
public class ObjectProperty : IProperty<uint>
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
|
|
|
@ -3,7 +3,7 @@ namespace DotTiled.Model;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a string property.
|
/// Represents a string property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class StringProperty : IProperty
|
public class StringProperty : IProperty<string>
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public required string Name { get; set; }
|
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)
|
internal static Dictionary<string, IProperty> MergeProperties(Dictionary<string, IProperty>? baseProperties, Dictionary<string, IProperty>? overrideProperties)
|
||||||
{
|
{
|
||||||
if (baseProperties is null)
|
if (baseProperties is null)
|
||||||
|
@ -93,7 +96,7 @@ internal static partial class Helpers
|
||||||
{
|
{
|
||||||
if (value is ClassProperty classProp)
|
if (value is ClassProperty classProp)
|
||||||
{
|
{
|
||||||
((ClassProperty)baseProp).Properties = MergeProperties(((ClassProperty)baseProp).Properties, classProp.Properties);
|
((ClassProperty)baseProp).Value = MergePropertiesList(((ClassProperty)baseProp).Value, classProp.Value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -105,6 +108,48 @@ internal static partial class Helpers
|
||||||
return result;
|
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)
|
internal static void SetAtMostOnce<T>(ref T? field, T value, string fieldName)
|
||||||
{
|
{
|
||||||
if (field is not null)
|
if (field is not null)
|
||||||
|
|
|
@ -58,7 +58,7 @@ internal partial class Tmj
|
||||||
var nextObjectID = element.GetRequiredProperty<uint>("nextobjectid");
|
var nextObjectID = element.GetRequiredProperty<uint>("nextobjectid");
|
||||||
var infinite = element.GetOptionalProperty<bool>("infinite", false);
|
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<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)), []);
|
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,
|
NextLayerID = nextLayerID,
|
||||||
NextObjectID = nextObjectID,
|
NextObjectID = nextObjectID,
|
||||||
Infinite = infinite,
|
Infinite = infinite,
|
||||||
Properties = properties,
|
Properties = properties ?? [],
|
||||||
Tilesets = tilesets,
|
Tilesets = tilesets,
|
||||||
Layers = layers
|
Layers = layers
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,6 +43,41 @@ internal partial class Tmj
|
||||||
return property!;
|
return property!;
|
||||||
}).ToDictionary(p => p.Name);
|
}).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(
|
internal static ClassProperty ReadClassProperty(
|
||||||
JsonElement element,
|
JsonElement element,
|
||||||
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
|
@ -54,28 +89,28 @@ internal partial class Tmj
|
||||||
|
|
||||||
if (customTypeDef is CustomClassDefinition ccd)
|
if (customTypeDef is CustomClassDefinition ccd)
|
||||||
{
|
{
|
||||||
var propsInType = CreateInstanceOfCustomClass(ccd);
|
var propsInType = Helpers.CreateInstanceOfCustomClass(ccd);
|
||||||
var props = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>>("value", el => ReadCustomClassProperties(el, ccd, customTypeDefinitions), []);
|
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
|
return new ClassProperty
|
||||||
{
|
{
|
||||||
Name = name,
|
Name = name,
|
||||||
PropertyType = propertyType,
|
PropertyType = propertyType,
|
||||||
Properties = mergedProps
|
Value = props
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new JsonException($"Unknown custom class '{propertyType}'.");
|
throw new JsonException($"Unknown custom class '{propertyType}'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Dictionary<string, IProperty> ReadCustomClassProperties(
|
internal static List<IProperty> ReadCustomClassProperties(
|
||||||
JsonElement element,
|
JsonElement element,
|
||||||
CustomClassDefinition customClassDefinition,
|
CustomClassDefinition customClassDefinition,
|
||||||
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
{
|
{
|
||||||
Dictionary<string, IProperty> resultingProps = [];
|
List<IProperty> resultingProps = Helpers.CreateInstanceOfCustomClass(customClassDefinition);
|
||||||
|
|
||||||
foreach (var prop in customClassDefinition.Members)
|
foreach (var prop in customClassDefinition.Members)
|
||||||
{
|
{
|
||||||
|
@ -95,12 +130,9 @@ internal partial class Tmj
|
||||||
_ => throw new JsonException("Invalid property type")
|
_ => throw new JsonException("Invalid property type")
|
||||||
};
|
};
|
||||||
|
|
||||||
resultingProps[prop.Name] = property;
|
Helpers.ReplacePropertyInList(resultingProps, property);
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultingProps;
|
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;
|
var infinite = (reader.GetOptionalAttributeParseable<uint>("infinite") ?? 0) == 1;
|
||||||
|
|
||||||
// At most one of
|
// At most one of
|
||||||
Dictionary<string, IProperty>? properties = null;
|
List<IProperty>? properties = null;
|
||||||
|
|
||||||
// Any number of
|
// Any number of
|
||||||
List<BaseLayer> layers = [];
|
List<BaseLayer> layers = [];
|
||||||
|
@ -69,7 +69,7 @@ internal partial class Tmx
|
||||||
|
|
||||||
reader.ProcessChildren("map", (r, elementName) => elementName switch
|
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)),
|
"tileset" => () => tilesets.Add(ReadTileset(r, externalTilesetResolver, externalTemplateResolver, customTypeDefinitions)),
|
||||||
"layer" => () => layers.Add(ReadTileLayer(r, infinite, customTypeDefinitions)),
|
"layer" => () => layers.Add(ReadTileLayer(r, infinite, customTypeDefinitions)),
|
||||||
"objectgroup" => () => layers.Add(ReadObjectLayer(r, externalTemplateResolver, customTypeDefinitions)),
|
"objectgroup" => () => layers.Add(ReadObjectLayer(r, externalTemplateResolver, customTypeDefinitions)),
|
||||||
|
@ -99,7 +99,7 @@ internal partial class Tmx
|
||||||
NextLayerID = nextLayerID,
|
NextLayerID = nextLayerID,
|
||||||
NextObjectID = nextObjectID,
|
NextObjectID = nextObjectID,
|
||||||
Infinite = infinite,
|
Infinite = infinite,
|
||||||
Properties = properties,
|
Properties = properties ?? [],
|
||||||
Tilesets = tilesets,
|
Tilesets = tilesets,
|
||||||
Layers = layers
|
Layers = layers
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,6 +43,42 @@ internal partial class Tmx
|
||||||
}).ToDictionary(x => x.name, x => x.property);
|
}).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(
|
internal static ClassProperty ReadClassProperty(
|
||||||
XmlReader reader,
|
XmlReader reader,
|
||||||
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
|
@ -54,18 +90,14 @@ internal partial class Tmx
|
||||||
if (customTypeDef is CustomClassDefinition ccd)
|
if (customTypeDef is CustomClassDefinition ccd)
|
||||||
{
|
{
|
||||||
reader.ReadStartElement("property");
|
reader.ReadStartElement("property");
|
||||||
var propsInType = CreateInstanceOfCustomClass(ccd);
|
var propsInType = Helpers.CreateInstanceOfCustomClass(ccd);
|
||||||
var props = ReadProperties(reader, customTypeDefinitions);
|
var props = ReadPropertiesList(reader, customTypeDefinitions);
|
||||||
|
var mergedProps = Helpers.MergePropertiesList(propsInType, props);
|
||||||
var mergedProps = Helpers.MergeProperties(propsInType, props);
|
|
||||||
|
|
||||||
reader.ReadEndElement();
|
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}");
|
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