|
@ -235,6 +235,7 @@ dotnet_diagnostic.IDE0004.severity = silent
|
||||||
dotnet_diagnostic.IDE0005.severity = error
|
dotnet_diagnostic.IDE0005.severity = error
|
||||||
dotnet_diagnostic.IDE0008.severity = silent
|
dotnet_diagnostic.IDE0008.severity = silent
|
||||||
dotnet_diagnostic.IDE0055.severity = silent
|
dotnet_diagnostic.IDE0055.severity = silent
|
||||||
|
dotnet_diagnostic.IDE0058.severity = silent
|
||||||
dotnet_diagnostic.IDE0160.severity = none
|
dotnet_diagnostic.IDE0160.severity = none
|
||||||
dotnet_diagnostic.CA1707.severity = silent
|
dotnet_diagnostic.CA1707.severity = silent
|
||||||
dotnet_diagnostic.CA1852.severity = none
|
dotnet_diagnostic.CA1852.severity = none
|
||||||
|
@ -243,7 +244,7 @@ dotnet_diagnostic.CA1720.severity = silent
|
||||||
dotnet_diagnostic.CA1711.severity = silent
|
dotnet_diagnostic.CA1711.severity = silent
|
||||||
dotnet_diagnostic.CA1716.severity = silent
|
dotnet_diagnostic.CA1716.severity = silent
|
||||||
|
|
||||||
[.github/**/*.yml]
|
[*.(yml|json|js)]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
15
.github/pull_request_template/dev_template.md
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
## Description
|
||||||
|
<!-- Provide a clear and concise description of the changes introduced by this PR. Include the purpose and context for these changes. -->
|
||||||
|
|
||||||
|
### Type of Change
|
||||||
|
|
||||||
|
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||||
|
- [ ] New feature (non-breaking change which adds functionality)
|
||||||
|
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||||
|
- [ ] Documentation update
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] Tests have been added/updated to cover new functionality.
|
||||||
|
- [ ] Documentation has been updated for all new changes (e.g., usage examples, API documentation).
|
||||||
|
|
10
.github/pull_request_template/master_template.md
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# Release vX.Y.Z
|
||||||
|
|
||||||
|
<!-- Include a description of what is breaking/new/patched in this release. -->
|
||||||
|
|
||||||
|
## Checklist for a release PR
|
||||||
|
|
||||||
|
- [ ] Version compatibility matrix has been updated.
|
||||||
|
- [ ] Tests have been added/updated to cover new functionality.
|
||||||
|
- [ ] Documentation has been updated for all new changes (e.g., usage examples, API documentation).
|
||||||
|
|
8
Makefile
|
@ -2,13 +2,15 @@ test:
|
||||||
dotnet build src/DotTiled.sln
|
dotnet build src/DotTiled.sln
|
||||||
dotnet test src/DotTiled.sln
|
dotnet test src/DotTiled.sln
|
||||||
|
|
||||||
docs-serve:
|
docs-serve: docs/index.md
|
||||||
docfx docs/docfx.json --serve
|
docfx docs/docfx.json --serve
|
||||||
|
|
||||||
docs-build:
|
docs-build: docs/index.md
|
||||||
cp README.md docs/index.md
|
|
||||||
docfx docs/docfx.json
|
docfx docs/docfx.json
|
||||||
|
|
||||||
|
docs/index.md:
|
||||||
|
cp README.md docs/index.md
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
dotnet format style --verify-no-changes src/DotTiled.sln
|
dotnet format style --verify-no-changes src/DotTiled.sln
|
||||||
dotnet format analyzers --verify-no-changes src/DotTiled.sln
|
dotnet format analyzers --verify-no-changes src/DotTiled.sln
|
||||||
|
|
|
@ -16,10 +16,10 @@ Other similar libraries exist, and you may want to consider them for your projec
|
||||||
|
|
||||||
|**Comparison**|**DotTiled**|[TiledLib](https://github.com/Ragath/TiledLib.Net)|[TiledCSPlus](https://github.com/nolemretaWxd/TiledCSPlus)|[TiledSharp](https://github.com/marshallward/TiledSharp)|[TiledCS](https://github.com/TheBoneJarmer/TiledCS)|[TiledNet](https://github.com/napen123/Tiled.Net)|
|
|**Comparison**|**DotTiled**|[TiledLib](https://github.com/Ragath/TiledLib.Net)|[TiledCSPlus](https://github.com/nolemretaWxd/TiledCSPlus)|[TiledSharp](https://github.com/marshallward/TiledSharp)|[TiledCS](https://github.com/TheBoneJarmer/TiledCS)|[TiledNet](https://github.com/napen123/Tiled.Net)|
|
||||||
|---------------------------------|:-----------------------:|:--------:|:-----------:|:----------:|:-------:|:------:|
|
|---------------------------------|:-----------------------:|:--------:|:-----------:|:----------:|:-------:|:------:|
|
||||||
| Actively maintained | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
|
| Actively maintained | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||||
| Benchmark (time)* | 1.00 | 1.83 | 2.16 | - | - | - |
|
| Benchmark (time)* | 1.00 | 1.83 | 2.16 | - | - | - |
|
||||||
| Benchmark (memory)* | 1.00 | 1.43 | 2.03 | - | - | - |
|
| Benchmark (memory)* | 1.00 | 1.43 | 2.03 | - | - | - |
|
||||||
| .NET Targets | `net8.0` |`net6.0`<br>`net7.0`|`netstandard2.1`|`netstandard2.0`|`netstandard2.0`|`net45`|
|
| .NET Targets | `net8.0` | `net8.0` |`netstandard2.1`|`netstandard2.0`|`netstandard2.0`|`net45`|
|
||||||
| Docs |Usage, API,<br>XML Docs|Usage|Usage, API,<br>XML Docs|Usage, API|Usage, XML Docs|Usage, XML Docs|
|
| Docs |Usage, API,<br>XML Docs|Usage|Usage, API,<br>XML Docs|Usage, API|Usage, XML Docs|Usage, XML Docs|
|
||||||
| License | MIT | MIT | MIT | Apache-2.0 | MIT | BSD 3-Clause |
|
| License | MIT | MIT | MIT | Apache-2.0 | MIT | BSD 3-Clause |
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,10 @@
|
||||||
"_appName": "DotTiled",
|
"_appName": "DotTiled",
|
||||||
"_appTitle": "DotTiled",
|
"_appTitle": "DotTiled",
|
||||||
"_enableSearch": true,
|
"_enableSearch": true,
|
||||||
"pdf": true
|
"pdf": false
|
||||||
}
|
},
|
||||||
|
"xref": [
|
||||||
|
"https://learn.microsoft.com/en-us/dotnet/.xrefmap.json"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -66,11 +66,14 @@ Tiled supports a variety of property types, which are represented in the DotTile
|
||||||
- `object` - <xref:DotTiled.ObjectProperty>
|
- `object` - <xref:DotTiled.ObjectProperty>
|
||||||
- `string` - <xref:DotTiled.StringProperty>
|
- `string` - <xref:DotTiled.StringProperty>
|
||||||
|
|
||||||
In addition to these primitive property types, [Tiled also supports more complex property types](https://doc.mapeditor.org/en/stable/manual/custom-properties/#custom-types). These custom property types are defined in Tiled according to the linked documentation, and to work with them in DotTiled, you *must* define their equivalences as a <xref:DotTiled.ICustomTypeDefinition>. You must then provide a resolving function to a defined type given a custom type name, as it is defined in Tiled.
|
In addition to these primitive property types, [Tiled also supports more complex property types](https://doc.mapeditor.org/en/stable/manual/custom-properties/#custom-types). These custom property types are defined in Tiled according to the linked documentation, and to work with them in DotTiled, you *must* define their equivalences as a <xref:DotTiled.ICustomTypeDefinition>. This is because of how Tiled handles default values for custom property types, and DotTiled needs to know these defaults to be able to populate the properties correctly.
|
||||||
|
|
||||||
## Custom types
|
## Custom types
|
||||||
|
|
||||||
Tiled allows you to define custom property types that can be used in your maps. These custom property types can be of type `class` or `enum`. DotTiled supports custom property types by allowing you to define the equivalent in C# and then providing a custom type resolver function that will return the equivalent definition given a custom type name.
|
Tiled allows you to define custom property types that can be used in your maps. These custom property types can be of type `class` or `enum`. DotTiled supports custom property types by allowing you to define the equivalent in C#. This section will guide you through how to define custom property types in DotTiled and how to map properties in loaded maps to C# classes or enums.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> In the future, DotTiled could provide a way to configure the use of custom property types such that they aren't necessary to be defined, given that you have set the `Resolve object types and properties` setting in Tiled.
|
||||||
|
|
||||||
### Class properties
|
### Class properties
|
||||||
|
|
||||||
|
@ -88,14 +91,39 @@ var monsterSpawnerDefinition = new CustomClassDefinition
|
||||||
Name = "MonsterSpawner",
|
Name = "MonsterSpawner",
|
||||||
UseAs = CustomClassUseAs.All, // Not really validated by DotTiled
|
UseAs = CustomClassUseAs.All, // Not really validated by DotTiled
|
||||||
Members = [ // Make sure that the default values match the Tiled UI
|
Members = [ // Make sure that the default values match the Tiled UI
|
||||||
new BoolProperty { Name = "enabled", Value = true },
|
new BoolProperty { Name = "Enabled", Value = true },
|
||||||
new IntProperty { Name = "maxSpawnAmount", Value = 10 },
|
new IntProperty { Name = "MaxSpawnAmount", Value = 10 },
|
||||||
new IntProperty { Name = "minSpawnAmount", Value = 0 },
|
new IntProperty { Name = "MinSpawnAmount", Value = 0 },
|
||||||
new StringProperty { Name = "monsterNames", Value = "" }
|
new StringProperty { Name = "MonsterNames", Value = "" }
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Luckily, you don't have to manually define these custom class definitions, even though you most definitively can for scenarios that require it. DotTiled provides a way to automatically generate these definitions for you from a C# class. This is done by using the <xref:DotTiled.CustomClassDefinition.FromClass``1> method, or one of its overloads. This method will generate a <xref:DotTiled.CustomClassDefinition> from a given C# class, and you can then use this definition when loading your maps.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
class MonsterSpawner
|
||||||
|
{
|
||||||
|
public bool Enabled { get; set; } = true;
|
||||||
|
public int MaxSpawnAmount { get; set; } = 10;
|
||||||
|
public int MinSpawnAmount { get; set; } = 0;
|
||||||
|
public string MonsterNames { get; set; } = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// These are all valid ways to create your custom class definitions from a C# class
|
||||||
|
// The first two require the class to have a default, parameterless constructor
|
||||||
|
var monsterSpawnerDefinition1 = CustomClassDefinition.FromClass<MonsterSpawner>();
|
||||||
|
var monsterSpawnerDefinition2 = CustomClassDefinition.FromClass(typeof(MonsterSpawner));
|
||||||
|
var monsterSpawnerDefinition3 = CustomClassDefinition.FromClass(() => new MonsterSpawner
|
||||||
|
{
|
||||||
|
Enabled = false // This will use the property values in the instance created by a factory method as the default values
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
The last one is especially useful if you have classes that may not have parameterless constructors, or if you want to provide custom default values for the properties. Finally, the generated custom class definition will be identical to the one defined manually in the first example.
|
||||||
|
|
||||||
### Enum properties
|
### Enum properties
|
||||||
|
|
||||||
Tiled also allows you to define custom property types that work as enums. Similarly to `class` properties, you must define the equivalent in DotTiled as a <xref:DotTiled.CustomEnumDefinition>. You can then return the corresponding definition in the resolving function.
|
Tiled also allows you to define custom property types that work as enums. Similarly to `class` properties, you must define the equivalent in DotTiled as a <xref:DotTiled.CustomEnumDefinition>. You can then return the corresponding definition in the resolving function.
|
||||||
|
@ -110,7 +138,7 @@ The equivalent definition in DotTiled would look like the following:
|
||||||
var entityTypeDefinition = new CustomEnumDefinition
|
var entityTypeDefinition = new CustomEnumDefinition
|
||||||
{
|
{
|
||||||
Name = "EntityType",
|
Name = "EntityType",
|
||||||
StorageType = CustomEnumStorageType.String,
|
StorageType = CustomEnumStorageType.Int,
|
||||||
ValueAsFlags = false,
|
ValueAsFlags = false,
|
||||||
Values = [
|
Values = [
|
||||||
"Bomb",
|
"Bomb",
|
||||||
|
@ -121,23 +149,9 @@ var entityTypeDefinition = new CustomEnumDefinition
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
### [Future] Automatically map custom property `class` types to C# classes
|
Similarly to custom class definitions, you can also automatically generate custom enum definitions from C# enums. This is done by using the <xref:DotTiled.CustomEnumDefinition.FromEnum``1> method, or one of its overloads. This method will generate a <xref:DotTiled.CustomEnumDefinition> from a given C# enum, and you can then use this definition when loading your maps.
|
||||||
|
|
||||||
In the future, DotTiled will support automatically mapping custom property `class` types to C# classes. This will allow you to define a C# class that matches the structure of the `class` property in Tiled, and DotTiled will automatically map the properties of the `class` property to the properties of the C# class. This will make working with `class` properties much easier and more intuitive.
|
|
||||||
|
|
||||||
The idea is to expand on the <xref:DotTiled.IHasProperties> interface with a method like `GetMappedProperty<T>(string propertyName)`, where `T` is a class that matches the structure of the `class` property in Tiled.
|
|
||||||
|
|
||||||
This functionality would be accompanied by a way to automatically create a matching <xref:DotTiled.ICustomTypeDefinition> given a C# class or enum. Something like this would then be possible:
|
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
class MonsterSpawner
|
|
||||||
{
|
|
||||||
public bool Enabled { get; set; } = true;
|
|
||||||
public int MaxSpawnAmount { get; set; } = 10;
|
|
||||||
public int MinSpawnAmount { get; set; } = 0;
|
|
||||||
public string MonsterNames { get; set; } = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
enum EntityType
|
enum EntityType
|
||||||
{
|
{
|
||||||
Bomb,
|
Bomb,
|
||||||
|
@ -146,16 +160,89 @@ enum EntityType
|
||||||
Chair
|
Chair
|
||||||
}
|
}
|
||||||
|
|
||||||
var monsterSpawnerDefinition = CustomClassDefinition.FromClass<MonsterSpawner>();
|
|
||||||
var entityTypeDefinition = CustomEnumDefinition.FromEnum<EntityType>();
|
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
var map = LoadMap();
|
// These are both valid ways to create your custom enum definitions from a C# enum
|
||||||
var monsterSpawner = map.GetMappedProperty<MonsterSpawner>("monsterSpawnerPropertyInMap");
|
var entityTypeDefinition1 = CustomEnumDefinition.FromEnum<EntityType>();
|
||||||
var entityType = map.GetMappedProperty<EntityType>("entityTypePropertyInMap");
|
var entityTypeDefinition2 = CustomEnumDefinition.FromEnum(typeof(EntityType));
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, it might be possible to also make some kind of exporting functionality for <xref:DotTiled.ICustomTypeDefinition>. Given a collection of custom type definitions, DotTiled could generate a corresponding `propertytypes.json` file that you then can import into Tiled. This would make it so that you only have to define your custom property types once (in C#) and then import them into Tiled to use them in your maps.
|
The generated custom enum definition will be identical to the one defined manually in the first example.
|
||||||
|
|
||||||
Depending on implementation this might become something that can inhibit native AOT compilation due to potential reflection usage. Source generators could be used to mitigate this, but it is not yet clear how this will be implemented.
|
For enum definitions, the <xref:System.FlagsAttribute> can be used to indicate that the enum should be treated as a flags enum. This will make it so the enum definition will have `ValueAsFlags = true` and the enum values will be treated as flags when working with them in DotTiled.
|
||||||
|
|
||||||
|
## Mapping properties to C# classes or enums
|
||||||
|
|
||||||
|
So far, we have only discussed how to define custom property types in DotTiled, and why they are needed. However, the most important part is how you can map properties inside your maps to their corresponding C# classes or enums.
|
||||||
|
|
||||||
|
The interface <xref:DotTiled.IHasProperties> has two overloads of the <xref:DotTiled.IHasProperties.MapPropertiesTo``1> method. These methods allow you to map a collection of properties to a given C# class. Let's look at an example:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Define a few Tiled compatible custom types
|
||||||
|
enum EntityType
|
||||||
|
{
|
||||||
|
Player,
|
||||||
|
Enemy,
|
||||||
|
Collectible,
|
||||||
|
Explosive
|
||||||
|
}
|
||||||
|
|
||||||
|
class EntityData
|
||||||
|
{
|
||||||
|
public EntityType Type { get; set; } = EntityType.Player;
|
||||||
|
public int Health { get; set; } = 100;
|
||||||
|
public string Name { get; set; } = "Unnamed Entity";
|
||||||
|
}
|
||||||
|
|
||||||
|
var entityTypeDef = CustomEnumDefinition.FromEnum<EntityType>();
|
||||||
|
var entityDataDef = CustomClassDefinition.FromClass<EntityData>();
|
||||||
|
```
|
||||||
|
|
||||||
|
The above gives us two custom type definitions that we can supply to our map loader. Given a map that looks like this:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="8" nextobjectid="7">
|
||||||
|
<tileset firstgid="1" source="tileset.tsx"/>
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
1,1,1,1,1,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
<objectgroup id="3" name="Objects">
|
||||||
|
<object id="4" name="Circle1" type="EntityData" x="77" y="72.3333" width="34.6667" height="34.6667">
|
||||||
|
<properties>
|
||||||
|
<property name="Health" type="int" value="1"/>
|
||||||
|
<property name="Name" value="Bomb Chest"/>
|
||||||
|
<property name="Type" type="int" propertytype="EntityType" value="3"/>
|
||||||
|
</properties>
|
||||||
|
<ellipse/>
|
||||||
|
</object>
|
||||||
|
</objectgroup>
|
||||||
|
</map>
|
||||||
|
```
|
||||||
|
|
||||||
|
We can see that there is an ellipse object in the map that has the type `EntityData` and it has set the three properties to some value other than their defaults. After having loaded this map, we can map the properties of the object to the `EntityData` class like this:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var map = LoadMap([entityTypeDef, entityDataDef]); // Load the map somehow, using DotTiled.Loader or similar
|
||||||
|
|
||||||
|
// Retrieve the object layer from the map in some way
|
||||||
|
var objectLayer = map.Layers.Skip(1).First() as ObjectLayer;
|
||||||
|
|
||||||
|
// Retrieve the object from the object layer
|
||||||
|
var entityObject = objectLayer.Objects.First();
|
||||||
|
|
||||||
|
// Map the properties of the object to the EntityData class
|
||||||
|
var entityData = entityObject.MapPropertiesTo<EntityData>();
|
||||||
|
```
|
||||||
|
|
||||||
|
The above snippet will map the properties of the object to the `EntityData` class using reflection based on the property names. The `entityData` object will now have the properties set to the values found in the object in the map. If a property is not found in the object, the default value of the property in the `EntityData` class will be used. It will even map the nested enum to its corresponding value in C#. This will work for several levels of depth as it performs this kind of mapping recursively. <xref:DotTiled.IHasProperties.MapPropertiesTo``1> only supports mapping to classes that have a default, parameterless constructor.
|
||||||
|
|
||||||
|
### [Future] Exporting custom types
|
||||||
|
|
||||||
|
It might be possible to also make some kind of exporting functionality for <xref:DotTiled.ICustomTypeDefinition>. Given a collection of custom type definitions, DotTiled could generate a corresponding `propertytypes.json` file that you then can import into Tiled. This would make it so that you only have to define your custom property types once (in C#) and then import them into Tiled to use them in your maps.
|
|
@ -1,15 +1,92 @@
|
||||||
# Loading maps
|
# Loading maps
|
||||||
|
|
||||||
Loading maps with DotTiled is straightforward and easy. The <xref:DotTiled.Map> class is a representation of a Tiled map, mimicking the structure of a Tiled map file. Map files can either be in the [`.tmx`/XML](https://doc.mapeditor.org/en/stable/reference/tmx-map-format/) or [`.tmj`/json](https://doc.mapeditor.org/en/stable/reference/json-map-format/) format. DotTiled supports **both** formats fully.
|
Loading maps with DotTiled is very flexible and allows you as a developer to freely choose how you want to load your maps and tilesets. This guide will show you how to customize the loading process to fit your needs. As the tip below suggests, you can also use the quickstart guide if you just want to load maps from the filesystem without any particular customization.
|
||||||
|
|
||||||
> [!NOTE]
|
> [!TIP]
|
||||||
> Using the `.tmj` file format will result in <xref:DotTiled.ImageLayer.Image> not having the same amount of information as for the `.tmx` format. This is due to the fact that the `.tmj` format does not include the full information that the `.tmx` format does. This is not a problem with DotTiled, but rather a limitation of the `.tmj` format.
|
> For a quick and easy way to load maps from the filesystem, please refer to the [quickstart guide](../quickstart.md).
|
||||||
|
|
||||||
## External resolution
|
## File format caveats
|
||||||
|
|
||||||
Tiled maps may consist of several external files, such as tilesets or object templates. In Tiled map files, they are typically referenced by their path relative to the map file. It would be annoying to have to first load all these external resources before loading a map (which is how some other similar libraries work), so loading a map with DotTiled is designed in a way that you only have to provide a function that resolves these external resources. This way, DotTiled will figure out which external resources are needed and will invoke the corresponding resolver function to load them.
|
The <xref:DotTiled.Map> class is a representation of a Tiled map, mimicking the structure of a Tiled XML map file. Map files can either be in the [`.tmx`/XML](https://doc.mapeditor.org/en/stable/reference/tmx-map-format/) or [`.tmj`/json](https://doc.mapeditor.org/en/stable/reference/json-map-format/) format. DotTiled supports **both** formats fully.
|
||||||
|
|
||||||
Loading a map, tileset, or template will require you to specify **three** resolver functions. We'll go through each of them below.
|
> [!WARNING]
|
||||||
|
> Using the `.tmj` file format will result in <xref:DotTiled.ImageLayer.Image> (the source image for image layers) not having the same amount of information as for the `.tmx` format. This is due to the fact that the `.tmj` format does not include the full information that the `.tmx` format does. This is not a problem with DotTiled, but rather a limitation of the `.tmj` format.
|
||||||
|
|
||||||
|
## The process of loading a map
|
||||||
|
|
||||||
|
Loading a map with DotTiled is not a complex process, but one that at least demands a basic understanding of how Tiled maps are structured. The process can be broken down into the following flow(-ish) chart:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
Z{{Loading a map
|
||||||
|
with DotTiled}} --> A
|
||||||
|
|
||||||
|
subgraph Parsing map
|
||||||
|
A[(Read map)] --> B(Parse map)
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Parsing tileset
|
||||||
|
B -.->|References
|
||||||
|
external tileset| C[(Read tileset)]
|
||||||
|
C --> D(Parse tileset) --o|Store in map| B
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Parsing template
|
||||||
|
B -.->|References external
|
||||||
|
template in object| E[(Read template)]
|
||||||
|
E --> F(Parse template) --o|Use as template
|
||||||
|
for object| B
|
||||||
|
end
|
||||||
|
|
||||||
|
F -.-> |References
|
||||||
|
external tileset| C
|
||||||
|
F -.-> |References
|
||||||
|
external template| E
|
||||||
|
```
|
||||||
|
|
||||||
|
As you can see, the process is quite simple. You read the map, parse it, and then read and parse any external tilesets or templates that are referenced in the map. The tilesets and templates are then stored in the map object that is returned to you.
|
||||||
|
|
||||||
|
However, because DotTiled works in the way that it does, you will need to provide a way to resolve these external resources. We'll go through how to do that in the next section.
|
||||||
|
|
||||||
|
## Loading a map with <xref:DotTiled.Serialization.Loader>
|
||||||
|
|
||||||
|
When using <xref:DotTiled.Serialization.Loader>, external resources like tilesets and templates will be resolved by the loader itself. Since Tiled saves the external resource paths relative to the map file, the loader will automatically resolve these paths and use the provided <xref:DotTiled.Serialization.IResourceReader> to read the external resources. Therefore, as long as the external resources are accessible in a "relative path" way using the provided <xref:DotTiled.Serialization.IResourceReader>, you don't have to worry about resolving them yourself.
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<tileset firstgid="1" source="tileset.tsx"/>
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
1,1,0,0,7,
|
||||||
|
1,1,0,0,7,
|
||||||
|
0,0,1,0,7,
|
||||||
|
0,0,0,1,7,
|
||||||
|
21,21,21,21,1
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
||||||
|
```
|
||||||
|
|
||||||
|
A map like the one above that is loaded by the following code will result in the loader calling `IResourceReader.Read("path/to/tileset.tsx")` to read the external tileset, since it will use the path relative to the map file to resolve the tileset.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var loader = Loader.Default();
|
||||||
|
var map = loader.LoadMap("path/to/map.tmx");
|
||||||
|
```
|
||||||
|
|
||||||
|
Additionally, the loader will use an in-memory cache to avoid loading the same tileset or template multiple times. This is done using the <xref:DotTiled.Serialization.IResourceCache> that is provided to the loader. If you don't provide a cache, the loader will use the <xref:DotTiled.Serialization.DefaultResourceCache> by default.
|
||||||
|
|
||||||
|
## Loading a map manually with <xref:DotTiled.Serialization.MapReader>
|
||||||
|
|
||||||
|
While it is recommended to use the <xref:DotTiled.Serialization.Loader> class to perform the loading of maps and tilesets, you may have certain requirements that necessitate you to load maps in a more manual way. This section will guide you through how to load a map manually without the use of the provided loader.
|
||||||
|
|
||||||
|
### <xref:DotTiled.Serialization.MapReader>, <xref:DotTiled.Serialization.TilesetReader>, and <xref:DotTiled.Serialization.TemplateReader>
|
||||||
|
|
||||||
|
are the three classes that you will use to read the map, tileset, and template, respectively. They are designed to be used in a way that you can provide your own resolver functions to load external resources.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> The resolving functions will get the source path of the external resource as a parameter, *in the exact way it is written in the map file*. You will have to perform your own path resolution to load the external resources.
|
||||||
|
|
||||||
### `Func<string, Tileset>` - Tileset resolver
|
### `Func<string, Tileset>` - Tileset resolver
|
||||||
|
|
||||||
|
@ -38,17 +115,17 @@ Tileset ResolveTileset(string source)
|
||||||
|
|
||||||
### `Func<string, Template>` - Template resolver
|
### `Func<string, Template>` - Template resolver
|
||||||
|
|
||||||
This function is used to resolve external object templates by their source path. The function should return a <xref:DotTiled.Template> instance given the source path of the template. If you just want to load templates from the file system, you can use something very similar to the tileset resolver by replacing <xref:DotTiled.Serialization.TilesetReader> with <xref:DotTiled.Serialization.TemplateReader>.
|
This function is used to resolve external object templates by their source path. The function should return a <xref:DotTiled.Template> instance given the source path of the template. If you just want to load templates from the file system, you can use something very similar to the example tileset resolver by replacing <xref:DotTiled.Serialization.TilesetReader> with <xref:DotTiled.Serialization.TemplateReader>.
|
||||||
|
|
||||||
### `Func<string, CustomType>` - Custom type resolver
|
### `Func<string, ICustomTypeDefinition>` - Custom type resolver
|
||||||
|
|
||||||
This function is used to resolve custom types that are defined in Tiled maps. Please refer to the [custom properties](custom-properties.md) documentation for more information on custom types. The function should return a <xref:DotTiled.ICustomTypeDefinition> instance given the custom type's name.
|
This function is used to resolve custom types that are defined in your Tiled maps. Please refer to the [custom properties](custom-properties.md) documentation for more information on custom types. The function should return a <xref:DotTiled.ICustomTypeDefinition> instance given the custom type's name.
|
||||||
|
|
||||||
## Putting it all together
|
## Putting it all together
|
||||||
|
|
||||||
The following classes are the readers that you will need to use to read the map, tileset, and template: <xref:DotTiled.Serialization.MapReader>, <xref:DotTiled.Serialization.TilesetReader>, and <xref:DotTiled.Serialization.TemplateReader>.
|
The following classes are the readers that you will need to use to read the map, tileset, and template: <xref:DotTiled.Serialization.MapReader>, <xref:DotTiled.Serialization.TilesetReader>, and <xref:DotTiled.Serialization.TemplateReader>.
|
||||||
|
|
||||||
Here is an example of how you can load a map with DotTiled:
|
Here is an example of how you can load a map with DotTiled, and is very similar to how the <xref:DotTiled.Serialization.Loader> class works:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
string mapPath = "path/to/map.tmx";
|
string mapPath = "path/to/map.tmx";
|
||||||
|
|
17
docs/docs/essentials/representation-model.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Representation model
|
||||||
|
|
||||||
|
Tiled map files contain various types of data, such as tilesets, layers, and object groups. The representation model is a way to represent this data in a structured way. By using the `.tmx` file format as inspiration, the representation model is a collection of classes which mimic the structure of a Tiled map file.
|
||||||
|
|
||||||
|
Certain properties throughout the representation model are marked as *optional* by being either wrapped in a <xref:DotTiled.Optional`1> or by having a set default value.
|
||||||
|
|
||||||
|
- Properties that make use of the [required](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/required) keyword must be present in the file, otherwise an error will be raised.
|
||||||
|
- Properties that have default values will use the default value if the property is not present in the file, and are not marked as required or optional since you must not provide a value for them.
|
||||||
|
- Properties that are wrapped in <xref:DotTiled.Optional`1> may or may not be present in the file, and have no default value.
|
||||||
|
|
||||||
|
## Version compatibility
|
||||||
|
|
||||||
|
The representation model is designed to be compatible with the latest version of Tiled. This means that it may not be able to read files from older versions of Tiled, or files that use features that are not yet supported by the representation model. However, here is a table of which versions of Tiled are supported by which versions of DotTiled.
|
||||||
|
|
||||||
|
| Tiled version | Compatible DotTiled version(s) |
|
||||||
|
|---------------|--------------------------------|
|
||||||
|
| 1.11 | 0.1.0, 0.2.0 |
|
|
@ -6,41 +6,64 @@ Install DotTiled from NuGet:
|
||||||
dotnet add package DotTiled
|
dotnet add package DotTiled
|
||||||
```
|
```
|
||||||
|
|
||||||
Load a map from file system:
|
Use the `DotTiled` namespace (if you want).
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
string mapPath = "path/to/map.tmx";
|
using DotTiled;
|
||||||
string mapDirectory = Path.GetDirectoryName(mapPath);
|
|
||||||
|
|
||||||
Tileset ResolveTileset(string source)
|
|
||||||
{
|
|
||||||
string tilesetPath = Path.Combine(mapDirectory, source);
|
|
||||||
using var tilesetFileReader = new StreamReader(tilesetPath);
|
|
||||||
var tilesetString = tilesetReader.ReadToEnd();
|
|
||||||
using var tilesetReader = new TilesetReader(tilesetString, ResolveTileset, ResolveTemplate, ResolveCustomType);
|
|
||||||
return tilesetReader.ReadTileset();
|
|
||||||
}
|
|
||||||
|
|
||||||
Template ResolveTemplate(string source)
|
|
||||||
{
|
|
||||||
string templatePath = Path.Combine(mapDirectory, source);
|
|
||||||
using var templateFileReader = new StreamReader(templatePath);
|
|
||||||
var templateString = templateReader.ReadToEnd();
|
|
||||||
using var templateReader = new TemplateReader(templateString, ResolveTileset, ResolveTemplate, ResolveCustomType);
|
|
||||||
return templateReader.ReadTemplate();
|
|
||||||
}
|
|
||||||
|
|
||||||
ICustomTypeDefinition ResolveCustomType(string name)
|
|
||||||
{
|
|
||||||
var allDefinedTypes = [ ... ];
|
|
||||||
return allDefinedTypes.FirstOrDefault(type => type.Name == name);
|
|
||||||
}
|
|
||||||
|
|
||||||
using var mapFileReader = new StreamReader(mapPath);
|
|
||||||
var mapString = mapFileReader.ReadToEnd();
|
|
||||||
using var mapReader = new MapReader(mapString, ResolveTileset, ResolveTemplate, ResolveCustomType);
|
|
||||||
|
|
||||||
var map = mapReader.ReadMap();
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If the above looks intimidating, don't worry! DotTiled is designed to be flexible and allow you to load maps from any source, such as a database or a custom file format. The above example is just one way to load a map from a file system. Please look at [Loading Maps](essentials/loading-maps.md) for more information on how to load maps from different sources.
|
Or fully qualify all `DotTiled` types e.g. `DotTiled.Loader`.
|
||||||
|
|
||||||
|
## Loading a map from the file system
|
||||||
|
|
||||||
|
This will create a loader that will load files from the underlying file system using <xref:DotTiled.Serialization.FileSystemResourceReader>. It will also be configured to use an in-memory cache to avoid loading the same tileset or template multiple times using <xref:DotTiled.Serialization.DefaultResourceCache>.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var loader = Loader.Default();
|
||||||
|
var map = loader.LoadMap("path/to/map.tmx");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Loading a map from a different source
|
||||||
|
|
||||||
|
If you want to load resources (maps, tilesets, templates) from a different source than the underlying file system, you can override the <xref:DotTiled.Serialization.FileSystemResourceReader> that is being used with your own implementation of <xref:DotTiled.Serialization.IResourceReader>.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var loader = Loader.DefaultWith(
|
||||||
|
resourceReader: new MyCustomResourceReader());
|
||||||
|
var map = loader.LoadMap("path/to/map.tmx");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Caching resources
|
||||||
|
|
||||||
|
Similarly, you can override the <xref:DotTiled.Serialization.DefaultResourceCache> that is being used with your own implementation of <xref:DotTiled.Serialization.IResourceCache>.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var loader = Loader.DefaultWith(
|
||||||
|
resourceReader: new MyCustomResourceReader(),
|
||||||
|
resourceCache: new MyCustomResourceCache());
|
||||||
|
var map = loader.LoadMap("path/to/map.tmx");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Custom types
|
||||||
|
|
||||||
|
If you have custom types in your map, you can provide any `IEnumerable<ICustomTypeDefinition>` to the loader. This will allow the loader to deserialize the custom types in your map.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var monsterSpawnerDef = new CustomClassDefinition { ... };
|
||||||
|
var chestDef = new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = "Chest",
|
||||||
|
UseAs = CustomClassUseAs.All,
|
||||||
|
Members = [
|
||||||
|
new IntProperty { Name = "coins", Value = 0 },
|
||||||
|
new BoolProperty { Name = "locked", Value = true }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
var loader = Loader.DefaultWith(
|
||||||
|
customTypeDefinitions: [monsterSpawnerDef, chestDef]);
|
||||||
|
var map = loader.LoadMap("path/to/map.tmx");
|
||||||
|
|
||||||
|
var chest = map.GetProperty<CustomClassProperty>("chest").Value;
|
||||||
|
var coinsToSpawn = chest.GetProperty<IntProperty>("coins").Value;
|
||||||
|
```
|
|
@ -4,4 +4,5 @@
|
||||||
|
|
||||||
- name: Essentials
|
- name: Essentials
|
||||||
- href: essentials/loading-maps.md
|
- href: essentials/loading-maps.md
|
||||||
|
- href: essentials/representation-model.md
|
||||||
- href: essentials/custom-properties.md
|
- href: essentials/custom-properties.md
|
9
docs/template/public/main.js
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
export default {
|
||||||
|
iconLinks: [
|
||||||
|
{
|
||||||
|
icon: 'github',
|
||||||
|
href: 'https://github.com/dcronqvist/DotTiled',
|
||||||
|
title: 'GitHub'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
23
src/DotTiled.Tests/Assert/AssertCustomTypes.cs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
|
public static partial class DotTiledAssert
|
||||||
|
{
|
||||||
|
internal static void AssertCustomClassDefinitionEqual(CustomClassDefinition expected, CustomClassDefinition actual)
|
||||||
|
{
|
||||||
|
AssertEqual(expected.ID, actual.ID, nameof(CustomClassDefinition.ID));
|
||||||
|
AssertEqual(expected.Name, actual.Name, nameof(CustomClassDefinition.Name));
|
||||||
|
AssertEqual(expected.Color, actual.Color, nameof(CustomClassDefinition.Color));
|
||||||
|
AssertEqual(expected.DrawFill, actual.DrawFill, nameof(CustomClassDefinition.DrawFill));
|
||||||
|
AssertEqual(expected.UseAs, actual.UseAs, nameof(CustomClassDefinition.UseAs));
|
||||||
|
AssertProperties(expected.Members, actual.Members);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void AssertCustomEnumDefinitionEqual(CustomEnumDefinition expected, CustomEnumDefinition actual)
|
||||||
|
{
|
||||||
|
AssertEqual(expected.ID, actual.ID, nameof(CustomEnumDefinition.ID));
|
||||||
|
AssertEqual(expected.Name, actual.Name, nameof(CustomEnumDefinition.Name));
|
||||||
|
AssertEqual(expected.StorageType, actual.StorageType, nameof(CustomEnumDefinition.StorageType));
|
||||||
|
AssertEqual(expected.ValueAsFlags, actual.ValueAsFlags, nameof(CustomEnumDefinition.ValueAsFlags));
|
||||||
|
AssertListOrdered(expected.Values, actual.Values, nameof(CustomEnumDefinition.Values));
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ namespace DotTiled.Tests;
|
||||||
|
|
||||||
public static partial class DotTiledAssert
|
public static partial class DotTiledAssert
|
||||||
{
|
{
|
||||||
internal static void AssertData(Data? expected, Data? actual)
|
internal static void AssertData(Data expected, Data actual)
|
||||||
{
|
{
|
||||||
if (expected is null)
|
if (expected is null)
|
||||||
{
|
{
|
||||||
|
@ -18,14 +18,7 @@ public static partial class DotTiledAssert
|
||||||
// Data
|
// Data
|
||||||
AssertEqual(expected.GlobalTileIDs, actual.GlobalTileIDs, nameof(Data.GlobalTileIDs));
|
AssertEqual(expected.GlobalTileIDs, actual.GlobalTileIDs, nameof(Data.GlobalTileIDs));
|
||||||
AssertEqual(expected.FlippingFlags, actual.FlippingFlags, nameof(Data.FlippingFlags));
|
AssertEqual(expected.FlippingFlags, actual.FlippingFlags, nameof(Data.FlippingFlags));
|
||||||
|
AssertOptionalsEqual(expected.Chunks, actual.Chunks, nameof(Data.Chunks), (a, b) => AssertListOrdered(a, b, nameof(Chunk), AssertChunk));
|
||||||
if (expected.Chunks is not null)
|
|
||||||
{
|
|
||||||
Assert.NotNull(actual.Chunks);
|
|
||||||
AssertEqual(expected.Chunks.Length, actual.Chunks.Length, "Chunks.Length");
|
|
||||||
for (var i = 0; i < expected.Chunks.Length; i++)
|
|
||||||
AssertChunk(expected.Chunks[i], actual.Chunks[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertChunk(Chunk expected, Chunk actual)
|
private static void AssertChunk(Chunk expected, Chunk actual)
|
||||||
|
|
|
@ -2,7 +2,7 @@ namespace DotTiled.Tests;
|
||||||
|
|
||||||
public static partial class DotTiledAssert
|
public static partial class DotTiledAssert
|
||||||
{
|
{
|
||||||
internal static void AssertImage(Image? expected, Image? actual)
|
internal static void AssertImage(Image expected, Image actual)
|
||||||
{
|
{
|
||||||
if (expected is null)
|
if (expected is null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,7 +2,7 @@ namespace DotTiled.Tests;
|
||||||
|
|
||||||
public static partial class DotTiledAssert
|
public static partial class DotTiledAssert
|
||||||
{
|
{
|
||||||
internal static void AssertLayer(BaseLayer? expected, BaseLayer? actual)
|
internal static void AssertLayer(BaseLayer expected, BaseLayer actual)
|
||||||
{
|
{
|
||||||
if (expected is null)
|
if (expected is null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,7 +5,69 @@ namespace DotTiled.Tests;
|
||||||
|
|
||||||
public static partial class DotTiledAssert
|
public static partial class DotTiledAssert
|
||||||
{
|
{
|
||||||
private static void AssertEqual<T>(T expected, T actual, string nameof)
|
internal static void AssertListOrdered<T>(IList<T> expected, IList<T> actual, string nameof, Action<T, T> assertEqual = null)
|
||||||
|
{
|
||||||
|
if (expected is null)
|
||||||
|
{
|
||||||
|
Assert.Null(actual);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.NotNull(actual);
|
||||||
|
AssertEqual(expected.Count, actual.Count, $"{nameof}.Count");
|
||||||
|
|
||||||
|
for (var i = 0; i < expected.Count; i++)
|
||||||
|
{
|
||||||
|
if (assertEqual is not null)
|
||||||
|
{
|
||||||
|
assertEqual(expected[i], actual[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
AssertEqual(expected[i], actual[i], $"{nameof}[{i}]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void AssertOptionalsEqual<T>(
|
||||||
|
Optional<T> expected,
|
||||||
|
Optional<T> actual,
|
||||||
|
string nameof,
|
||||||
|
Action<T, T> assertEqual)
|
||||||
|
{
|
||||||
|
if (expected is null)
|
||||||
|
{
|
||||||
|
Assert.Null(actual);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expected.HasValue)
|
||||||
|
{
|
||||||
|
Assert.True(actual.HasValue, $"Expected {nameof} to have a value");
|
||||||
|
assertEqual(expected.Value, actual.Value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.False(actual.HasValue, $"Expected {nameof} to not have a value");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void AssertEqual<T>(Optional<T> expected, Optional<T> actual, string nameof)
|
||||||
|
{
|
||||||
|
if (expected is null)
|
||||||
|
{
|
||||||
|
Assert.Null(actual);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expected.HasValue)
|
||||||
|
{
|
||||||
|
Assert.True(actual.HasValue, $"Expected {nameof} to have a value");
|
||||||
|
AssertEqual(expected.Value, actual.Value, nameof);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.False(actual.HasValue, $"Expected {nameof} to not have a value");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void AssertEqual<T>(T expected, T actual, string nameof)
|
||||||
{
|
{
|
||||||
if (expected == null)
|
if (expected == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,7 +2,7 @@ namespace DotTiled.Tests;
|
||||||
|
|
||||||
public static partial class DotTiledAssert
|
public static partial class DotTiledAssert
|
||||||
{
|
{
|
||||||
internal static void AssertProperties(IList<IProperty>? expected, IList<IProperty>? actual)
|
internal static void AssertProperties(IList<IProperty> expected, IList<IProperty> actual)
|
||||||
{
|
{
|
||||||
if (expected is null)
|
if (expected is null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,7 +4,6 @@ public static partial class DotTiledAssert
|
||||||
{
|
{
|
||||||
internal static void AssertTileset(Tileset expected, Tileset actual)
|
internal static void AssertTileset(Tileset expected, Tileset actual)
|
||||||
{
|
{
|
||||||
// Attributes
|
|
||||||
AssertEqual(expected.Version, actual.Version, nameof(Tileset.Version));
|
AssertEqual(expected.Version, actual.Version, nameof(Tileset.Version));
|
||||||
AssertEqual(expected.TiledVersion, actual.TiledVersion, nameof(Tileset.TiledVersion));
|
AssertEqual(expected.TiledVersion, actual.TiledVersion, nameof(Tileset.TiledVersion));
|
||||||
AssertEqual(expected.FirstGID, actual.FirstGID, nameof(Tileset.FirstGID));
|
AssertEqual(expected.FirstGID, actual.FirstGID, nameof(Tileset.FirstGID));
|
||||||
|
@ -21,29 +20,16 @@ public static partial class DotTiledAssert
|
||||||
AssertEqual(expected.RenderSize, actual.RenderSize, nameof(Tileset.RenderSize));
|
AssertEqual(expected.RenderSize, actual.RenderSize, nameof(Tileset.RenderSize));
|
||||||
AssertEqual(expected.FillMode, actual.FillMode, nameof(Tileset.FillMode));
|
AssertEqual(expected.FillMode, actual.FillMode, nameof(Tileset.FillMode));
|
||||||
|
|
||||||
// At most one of
|
AssertOptionalsEqual(expected.Image, actual.Image, nameof(Tileset.Image), AssertImage);
|
||||||
AssertImage(expected.Image, actual.Image);
|
AssertOptionalsEqual(expected.TileOffset, actual.TileOffset, nameof(Tileset.TileOffset), AssertTileOffset);
|
||||||
AssertTileOffset(expected.TileOffset, actual.TileOffset);
|
AssertOptionalsEqual(expected.Grid, actual.Grid, nameof(Tileset.Grid), AssertGrid);
|
||||||
AssertGrid(expected.Grid, actual.Grid);
|
|
||||||
AssertProperties(expected.Properties, actual.Properties);
|
AssertProperties(expected.Properties, actual.Properties);
|
||||||
// TODO: AssertTerrainTypes(actual.TerrainTypes, expected.TerrainTypes);
|
AssertListOrdered(expected.Wangsets, actual.Wangsets, nameof(Tileset.Wangsets), AssertWangset);
|
||||||
if (expected.Wangsets is not null)
|
AssertOptionalsEqual(expected.Transformations, actual.Transformations, nameof(Tileset.Transformations), AssertTransformations);
|
||||||
{
|
AssertListOrdered(expected.Tiles, actual.Tiles, nameof(Tileset.Tiles), AssertTile);
|
||||||
Assert.NotNull(actual.Wangsets);
|
|
||||||
AssertEqual(expected.Wangsets.Count, actual.Wangsets.Count, "Wangsets.Count");
|
|
||||||
for (var i = 0; i < expected.Wangsets.Count; i++)
|
|
||||||
AssertWangset(expected.Wangsets[i], actual.Wangsets[i]);
|
|
||||||
}
|
|
||||||
AssertTransformations(expected.Transformations, actual.Transformations);
|
|
||||||
|
|
||||||
// Any number of
|
|
||||||
Assert.NotNull(actual.Tiles);
|
|
||||||
AssertEqual(expected.Tiles.Count, actual.Tiles.Count, "Tiles.Count");
|
|
||||||
for (var i = 0; i < expected.Tiles.Count; i++)
|
|
||||||
AssertTile(expected.Tiles[i], actual.Tiles[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertTileOffset(TileOffset? expected, TileOffset? actual)
|
private static void AssertTileOffset(TileOffset expected, TileOffset actual)
|
||||||
{
|
{
|
||||||
if (expected is null)
|
if (expected is null)
|
||||||
{
|
{
|
||||||
|
@ -57,7 +43,7 @@ public static partial class DotTiledAssert
|
||||||
AssertEqual(expected.Y, actual.Y, nameof(TileOffset.Y));
|
AssertEqual(expected.Y, actual.Y, nameof(TileOffset.Y));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertGrid(Grid? expected, Grid? actual)
|
private static void AssertGrid(Grid expected, Grid actual)
|
||||||
{
|
{
|
||||||
if (expected is null)
|
if (expected is null)
|
||||||
{
|
{
|
||||||
|
@ -74,27 +60,17 @@ public static partial class DotTiledAssert
|
||||||
|
|
||||||
private static void AssertWangset(Wangset expected, Wangset actual)
|
private static void AssertWangset(Wangset expected, Wangset actual)
|
||||||
{
|
{
|
||||||
// Attributes
|
|
||||||
AssertEqual(expected.Name, actual.Name, nameof(Wangset.Name));
|
AssertEqual(expected.Name, actual.Name, nameof(Wangset.Name));
|
||||||
AssertEqual(expected.Class, actual.Class, nameof(Wangset.Class));
|
AssertEqual(expected.Class, actual.Class, nameof(Wangset.Class));
|
||||||
AssertEqual(expected.Tile, actual.Tile, nameof(Wangset.Tile));
|
AssertEqual(expected.Tile, actual.Tile, nameof(Wangset.Tile));
|
||||||
|
|
||||||
// At most one of
|
|
||||||
AssertProperties(expected.Properties, actual.Properties);
|
AssertProperties(expected.Properties, actual.Properties);
|
||||||
if (expected.WangColors is not null)
|
AssertListOrdered(expected.WangColors, actual.WangColors, nameof(Wangset.WangColors), AssertWangColor);
|
||||||
{
|
AssertListOrdered(expected.WangTiles, actual.WangTiles, nameof(Wangset.WangTiles), AssertWangTile);
|
||||||
Assert.NotNull(actual.WangColors);
|
|
||||||
AssertEqual(expected.WangColors.Count, actual.WangColors.Count, "WangColors.Count");
|
|
||||||
for (var i = 0; i < expected.WangColors.Count; i++)
|
|
||||||
AssertWangColor(expected.WangColors[i], actual.WangColors[i]);
|
|
||||||
}
|
|
||||||
for (var i = 0; i < expected.WangTiles.Count; i++)
|
|
||||||
AssertWangTile(expected.WangTiles[i], actual.WangTiles[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertWangColor(WangColor expected, WangColor actual)
|
private static void AssertWangColor(WangColor expected, WangColor actual)
|
||||||
{
|
{
|
||||||
// Attributes
|
|
||||||
AssertEqual(expected.Name, actual.Name, nameof(WangColor.Name));
|
AssertEqual(expected.Name, actual.Name, nameof(WangColor.Name));
|
||||||
AssertEqual(expected.Class, actual.Class, nameof(WangColor.Class));
|
AssertEqual(expected.Class, actual.Class, nameof(WangColor.Class));
|
||||||
AssertEqual(expected.Color, actual.Color, nameof(WangColor.Color));
|
AssertEqual(expected.Color, actual.Color, nameof(WangColor.Color));
|
||||||
|
@ -106,12 +82,11 @@ public static partial class DotTiledAssert
|
||||||
|
|
||||||
private static void AssertWangTile(WangTile expected, WangTile actual)
|
private static void AssertWangTile(WangTile expected, WangTile actual)
|
||||||
{
|
{
|
||||||
// Attributes
|
|
||||||
AssertEqual(expected.TileID, actual.TileID, nameof(WangTile.TileID));
|
AssertEqual(expected.TileID, actual.TileID, nameof(WangTile.TileID));
|
||||||
AssertEqual(expected.WangID, actual.WangID, nameof(WangTile.WangID));
|
AssertEqual(expected.WangID, actual.WangID, nameof(WangTile.WangID));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertTransformations(Transformations? expected, Transformations? actual)
|
private static void AssertTransformations(Transformations expected, Transformations actual)
|
||||||
{
|
{
|
||||||
if (expected is null)
|
if (expected is null)
|
||||||
{
|
{
|
||||||
|
@ -119,7 +94,6 @@ public static partial class DotTiledAssert
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attributes
|
|
||||||
Assert.NotNull(actual);
|
Assert.NotNull(actual);
|
||||||
AssertEqual(expected.HFlip, actual.HFlip, nameof(Transformations.HFlip));
|
AssertEqual(expected.HFlip, actual.HFlip, nameof(Transformations.HFlip));
|
||||||
AssertEqual(expected.VFlip, actual.VFlip, nameof(Transformations.VFlip));
|
AssertEqual(expected.VFlip, actual.VFlip, nameof(Transformations.VFlip));
|
||||||
|
@ -129,7 +103,6 @@ public static partial class DotTiledAssert
|
||||||
|
|
||||||
private static void AssertTile(Tile expected, Tile actual)
|
private static void AssertTile(Tile expected, Tile actual)
|
||||||
{
|
{
|
||||||
// Attributes
|
|
||||||
AssertEqual(expected.ID, actual.ID, nameof(Tile.ID));
|
AssertEqual(expected.ID, actual.ID, nameof(Tile.ID));
|
||||||
AssertEqual(expected.Type, actual.Type, nameof(Tile.Type));
|
AssertEqual(expected.Type, actual.Type, nameof(Tile.Type));
|
||||||
AssertEqual(expected.Probability, actual.Probability, nameof(Tile.Probability));
|
AssertEqual(expected.Probability, actual.Probability, nameof(Tile.Probability));
|
||||||
|
@ -138,22 +111,14 @@ public static partial class DotTiledAssert
|
||||||
AssertEqual(expected.Width, actual.Width, nameof(Tile.Width));
|
AssertEqual(expected.Width, actual.Width, nameof(Tile.Width));
|
||||||
AssertEqual(expected.Height, actual.Height, nameof(Tile.Height));
|
AssertEqual(expected.Height, actual.Height, nameof(Tile.Height));
|
||||||
|
|
||||||
// Elements
|
|
||||||
AssertProperties(expected.Properties, actual.Properties);
|
AssertProperties(expected.Properties, actual.Properties);
|
||||||
AssertImage(expected.Image, actual.Image);
|
AssertOptionalsEqual(expected.Image, actual.Image, nameof(Tile.Image), AssertImage);
|
||||||
AssertLayer((BaseLayer?)expected.ObjectLayer, (BaseLayer?)actual.ObjectLayer);
|
AssertOptionalsEqual(expected.ObjectLayer, actual.ObjectLayer, nameof(Tile.ObjectLayer), (a, b) => AssertLayer((BaseLayer)a, (BaseLayer)b));
|
||||||
if (expected.Animation is not null)
|
AssertListOrdered(expected.Animation, actual.Animation, nameof(Tile.Animation), AssertFrame);
|
||||||
{
|
|
||||||
Assert.NotNull(actual.Animation);
|
|
||||||
AssertEqual(expected.Animation.Count, actual.Animation.Count, "Animation.Count");
|
|
||||||
for (var i = 0; i < expected.Animation.Count; i++)
|
|
||||||
AssertFrame(expected.Animation[i], actual.Animation[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertFrame(Frame expected, Frame actual)
|
private static void AssertFrame(Frame expected, Frame actual)
|
||||||
{
|
{
|
||||||
// Attributes
|
|
||||||
AssertEqual(expected.TileID, actual.TileID, nameof(Frame.TileID));
|
AssertEqual(expected.TileID, actual.TileID, nameof(Frame.TileID));
|
||||||
AssertEqual(expected.Duration, actual.Duration, nameof(Frame.Duration));
|
AssertEqual(expected.Duration, actual.Duration, nameof(Frame.Duration));
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<IsTestProject>true</IsTestProject>
|
<IsTestProject>true</IsTestProject>
|
||||||
|
@ -12,6 +11,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||||
|
<PackageReference Include="NSubstitute" Version="5.1.0" />
|
||||||
<PackageReference Include="xunit" Version="2.5.3" />
|
<PackageReference Include="xunit" Version="2.5.3" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -26,7 +26,12 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- Test data -->
|
<!-- Test data -->
|
||||||
<EmbeddedResource Include="Serialization/TestData/**/*" />
|
<EmbeddedResource Include="TestData/**/*.tmx" />
|
||||||
|
<EmbeddedResource Include="TestData/**/*.tmj" />
|
||||||
|
<EmbeddedResource Include="TestData/**/*.tsx" />
|
||||||
|
<EmbeddedResource Include="TestData/**/*.tsj" />
|
||||||
|
<EmbeddedResource Include="TestData/**/*.tx" />
|
||||||
|
<EmbeddedResource Include="TestData/**/*.tj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -0,0 +1,180 @@
|
||||||
|
using DotTiled.Serialization;
|
||||||
|
using NSubstitute;
|
||||||
|
|
||||||
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
|
public class FromTypeUsedInLoaderTests
|
||||||
|
{
|
||||||
|
private sealed class TestClass
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = "John Doe";
|
||||||
|
public int Age { get; set; } = 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadMap_MapHasClassAndClassIsDefined_ReturnsCorrectMap()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceReader = Substitute.For<IResourceReader>();
|
||||||
|
resourceReader.Read("map.tmx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" class="TestClass" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
||||||
|
""");
|
||||||
|
var classDefinition = CustomClassDefinition.FromClass<TestClass>();
|
||||||
|
var loader = Loader.DefaultWith(
|
||||||
|
resourceReader: resourceReader,
|
||||||
|
customTypeDefinitions: [classDefinition]);
|
||||||
|
var expectedMap = new Map
|
||||||
|
{
|
||||||
|
Class = "TestClass",
|
||||||
|
Orientation = MapOrientation.Orthogonal,
|
||||||
|
Width = 5,
|
||||||
|
Height = 5,
|
||||||
|
TileWidth = 32,
|
||||||
|
TileHeight = 32,
|
||||||
|
Infinite = false,
|
||||||
|
ParallaxOriginX = 0,
|
||||||
|
ParallaxOriginY = 0,
|
||||||
|
RenderOrder = RenderOrder.RightDown,
|
||||||
|
CompressionLevel = -1,
|
||||||
|
BackgroundColor = new Color { R = 0, G = 0, B = 0, A = 0 },
|
||||||
|
Version = "1.10",
|
||||||
|
TiledVersion = "1.11.0",
|
||||||
|
NextLayerID = 2,
|
||||||
|
NextObjectID = 1,
|
||||||
|
Layers = [
|
||||||
|
new TileLayer
|
||||||
|
{
|
||||||
|
ID = 1,
|
||||||
|
Name = "Tile Layer 1",
|
||||||
|
Width = 5,
|
||||||
|
Height = 5,
|
||||||
|
Data = new Data
|
||||||
|
{
|
||||||
|
Encoding = DataEncoding.Csv,
|
||||||
|
GlobalTileIDs = new Optional<uint[]>([
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0
|
||||||
|
]),
|
||||||
|
FlippingFlags = new Optional<FlippingFlags[]>([
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
Properties = [
|
||||||
|
new StringProperty { Name = "Name", Value = "John Doe" },
|
||||||
|
new IntProperty { Name = "Age", Value = 42 }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = loader.LoadMap("map.tmx");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
DotTiledAssert.AssertMap(expectedMap, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadMap_MapHasClassAndClassIsDefinedAndFieldIsOverridenFromDefault_ReturnsCorrectMap()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceReader = Substitute.For<IResourceReader>();
|
||||||
|
resourceReader.Read("map.tmx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" class="TestClass" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<properties>
|
||||||
|
<property name="Name" value="John Doe"/>
|
||||||
|
</properties>
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
||||||
|
""");
|
||||||
|
var classDefinition = CustomClassDefinition.FromClass<TestClass>();
|
||||||
|
var loader = Loader.DefaultWith(
|
||||||
|
resourceReader: resourceReader,
|
||||||
|
customTypeDefinitions: [classDefinition]);
|
||||||
|
var expectedMap = new Map
|
||||||
|
{
|
||||||
|
Class = "TestClass",
|
||||||
|
Orientation = MapOrientation.Orthogonal,
|
||||||
|
Width = 5,
|
||||||
|
Height = 5,
|
||||||
|
TileWidth = 32,
|
||||||
|
TileHeight = 32,
|
||||||
|
Infinite = false,
|
||||||
|
ParallaxOriginX = 0,
|
||||||
|
ParallaxOriginY = 0,
|
||||||
|
RenderOrder = RenderOrder.RightDown,
|
||||||
|
CompressionLevel = -1,
|
||||||
|
BackgroundColor = new Color { R = 0, G = 0, B = 0, A = 0 },
|
||||||
|
Version = "1.10",
|
||||||
|
TiledVersion = "1.11.0",
|
||||||
|
NextLayerID = 2,
|
||||||
|
NextObjectID = 1,
|
||||||
|
Layers = [
|
||||||
|
new TileLayer
|
||||||
|
{
|
||||||
|
ID = 1,
|
||||||
|
Name = "Tile Layer 1",
|
||||||
|
Width = 5,
|
||||||
|
Height = 5,
|
||||||
|
Data = new Data
|
||||||
|
{
|
||||||
|
Encoding = DataEncoding.Csv,
|
||||||
|
GlobalTileIDs = new Optional<uint[]>([
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0
|
||||||
|
]),
|
||||||
|
FlippingFlags = new Optional<FlippingFlags[]>([
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
Properties = [
|
||||||
|
new StringProperty { Name = "Name", Value = "John Doe" },
|
||||||
|
new IntProperty { Name = "Age", Value = 42 }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = loader.LoadMap("map.tmx");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
DotTiledAssert.AssertMap(expectedMap, result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,45 +0,0 @@
|
||||||
using System.Xml;
|
|
||||||
|
|
||||||
namespace DotTiled.Tests;
|
|
||||||
|
|
||||||
public static partial class TestData
|
|
||||||
{
|
|
||||||
public static XmlReader GetXmlReaderFor(string testDataFile)
|
|
||||||
{
|
|
||||||
var fullyQualifiedTestDataFile = $"DotTiled.Tests.{ConvertPathToAssemblyResourcePath(testDataFile)}";
|
|
||||||
using var stream = typeof(TestData).Assembly.GetManifestResourceStream(fullyQualifiedTestDataFile)
|
|
||||||
?? throw new ArgumentException($"Test data file '{fullyQualifiedTestDataFile}' not found");
|
|
||||||
|
|
||||||
using var stringReader = new StreamReader(stream);
|
|
||||||
var xml = stringReader.ReadToEnd();
|
|
||||||
var xmlStringReader = new StringReader(xml);
|
|
||||||
return XmlReader.Create(xmlStringReader);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetRawStringFor(string testDataFile)
|
|
||||||
{
|
|
||||||
var fullyQualifiedTestDataFile = $"DotTiled.Tests.{ConvertPathToAssemblyResourcePath(testDataFile)}";
|
|
||||||
using var stream = typeof(TestData).Assembly.GetManifestResourceStream(fullyQualifiedTestDataFile)
|
|
||||||
?? throw new ArgumentException($"Test data file '{fullyQualifiedTestDataFile}' not found");
|
|
||||||
|
|
||||||
using var stringReader = new StreamReader(stream);
|
|
||||||
return stringReader.ReadToEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ConvertPathToAssemblyResourcePath(string path) =>
|
|
||||||
path.Replace("/", ".").Replace("\\", ".").Replace(" ", "_");
|
|
||||||
|
|
||||||
public static IEnumerable<object[]> MapTests =>
|
|
||||||
[
|
|
||||||
["Serialization/TestData/Map/default_map/default-map", (string f) => DefaultMap(), Array.Empty<ICustomTypeDefinition>()],
|
|
||||||
["Serialization/TestData/Map/map_with_common_props/map-with-common-props", (string f) => MapWithCommonProps(), Array.Empty<ICustomTypeDefinition>()],
|
|
||||||
["Serialization/TestData/Map/map_with_custom_type_props/map-with-custom-type-props", (string f) => MapWithCustomTypeProps(), MapWithCustomTypePropsCustomTypeDefinitions()],
|
|
||||||
["Serialization/TestData/Map/map_with_embedded_tileset/map-with-embedded-tileset", (string f) => MapWithEmbeddedTileset(), Array.Empty<ICustomTypeDefinition>()],
|
|
||||||
["Serialization/TestData/Map/map_with_external_tileset/map-with-external-tileset", (string f) => MapWithExternalTileset(f), Array.Empty<ICustomTypeDefinition>()],
|
|
||||||
["Serialization/TestData/Map/map_with_flippingflags/map-with-flippingflags", (string f) => MapWithFlippingFlags(f), Array.Empty<ICustomTypeDefinition>()],
|
|
||||||
["Serialization/TestData/Map/map_external_tileset_multi/map-external-tileset-multi", (string f) => MapExternalTilesetMulti(f), Array.Empty<ICustomTypeDefinition>()],
|
|
||||||
["Serialization/TestData/Map/map_external_tileset_wangset/map-external-tileset-wangset", (string f) => MapExternalTilesetWangset(f), Array.Empty<ICustomTypeDefinition>()],
|
|
||||||
["Serialization/TestData/Map/map_with_many_layers/map-with-many-layers", (string f) => MapWithManyLayers(f), Array.Empty<ICustomTypeDefinition>()],
|
|
||||||
["Serialization/TestData/Map/map_with_deep_props/map-with-deep-props", (string f) => MapWithDeepProps(), MapWithDeepPropsCustomTypeDefinitions()],
|
|
||||||
];
|
|
||||||
}
|
|
|
@ -11,9 +11,6 @@ public partial class TestData
|
||||||
TileWidth = 32,
|
TileWidth = 32,
|
||||||
TileHeight = 32,
|
TileHeight = 32,
|
||||||
Infinite = false,
|
Infinite = false,
|
||||||
HexSideLength = null,
|
|
||||||
StaggerAxis = null,
|
|
||||||
StaggerIndex = null,
|
|
||||||
ParallaxOriginX = 0,
|
ParallaxOriginX = 0,
|
||||||
ParallaxOriginY = 0,
|
ParallaxOriginY = 0,
|
||||||
RenderOrder = RenderOrder.RightDown,
|
RenderOrder = RenderOrder.RightDown,
|
||||||
|
@ -33,22 +30,20 @@ public partial class TestData
|
||||||
Data = new Data
|
Data = new Data
|
||||||
{
|
{
|
||||||
Encoding = DataEncoding.Csv,
|
Encoding = DataEncoding.Csv,
|
||||||
Chunks = null,
|
GlobalTileIDs = new Optional<uint[]>([
|
||||||
Compression = null,
|
|
||||||
GlobalTileIDs = [
|
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0
|
0, 0, 0, 0, 0
|
||||||
],
|
]),
|
||||||
FlippingFlags = [
|
FlippingFlags = new Optional<FlippingFlags[]>([
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
||||||
]
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
|
@ -13,9 +13,6 @@ public partial class TestData
|
||||||
TileWidth = 32,
|
TileWidth = 32,
|
||||||
TileHeight = 32,
|
TileHeight = 32,
|
||||||
Infinite = false,
|
Infinite = false,
|
||||||
HexSideLength = null,
|
|
||||||
StaggerAxis = null,
|
|
||||||
StaggerIndex = null,
|
|
||||||
ParallaxOriginX = 0,
|
ParallaxOriginX = 0,
|
||||||
ParallaxOriginY = 0,
|
ParallaxOriginY = 0,
|
||||||
RenderOrder = RenderOrder.RightDown,
|
RenderOrder = RenderOrder.RightDown,
|
||||||
|
@ -97,22 +94,20 @@ public partial class TestData
|
||||||
Data = new Data
|
Data = new Data
|
||||||
{
|
{
|
||||||
Encoding = DataEncoding.Csv,
|
Encoding = DataEncoding.Csv,
|
||||||
Chunks = null,
|
GlobalTileIDs = new Optional<uint[]>([
|
||||||
Compression = null,
|
|
||||||
GlobalTileIDs = [
|
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
1, 0, 0, 0, 0,
|
1, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 2, 0, 0, 0
|
0, 2, 0, 0, 0
|
||||||
],
|
]),
|
||||||
FlippingFlags = [
|
FlippingFlags = new Optional<FlippingFlags[]>([
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
||||||
]
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
@ -13,9 +13,6 @@ public partial class TestData
|
||||||
TileWidth = 24,
|
TileWidth = 24,
|
||||||
TileHeight = 24,
|
TileHeight = 24,
|
||||||
Infinite = false,
|
Infinite = false,
|
||||||
HexSideLength = null,
|
|
||||||
StaggerAxis = null,
|
|
||||||
StaggerIndex = null,
|
|
||||||
ParallaxOriginX = 0,
|
ParallaxOriginX = 0,
|
||||||
ParallaxOriginY = 0,
|
ParallaxOriginY = 0,
|
||||||
RenderOrder = RenderOrder.RightDown,
|
RenderOrder = RenderOrder.RightDown,
|
||||||
|
@ -56,7 +53,59 @@ public partial class TestData
|
||||||
Source = "tileset.png",
|
Source = "tileset.png",
|
||||||
Width = 256,
|
Width = 256,
|
||||||
Height = 96,
|
Height = 96,
|
||||||
}
|
},
|
||||||
|
Wangsets = [
|
||||||
|
new Wangset
|
||||||
|
{
|
||||||
|
Name = "test-terrain",
|
||||||
|
Tile = -1,
|
||||||
|
WangColors = [
|
||||||
|
new WangColor
|
||||||
|
{
|
||||||
|
Name = "Water",
|
||||||
|
Color = Color.Parse("#ff0000", CultureInfo.InvariantCulture),
|
||||||
|
Tile = 0,
|
||||||
|
Probability = 1
|
||||||
|
},
|
||||||
|
new WangColor
|
||||||
|
{
|
||||||
|
Name = "Grass",
|
||||||
|
Color = Color.Parse("#00ff00", CultureInfo.InvariantCulture),
|
||||||
|
Tile = -1,
|
||||||
|
Probability = 1
|
||||||
|
},
|
||||||
|
new WangColor
|
||||||
|
{
|
||||||
|
Name = "Stone",
|
||||||
|
Color = Color.Parse("#0000ff", CultureInfo.InvariantCulture),
|
||||||
|
Tile = 29,
|
||||||
|
Probability = 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
WangTiles = [
|
||||||
|
new WangTile
|
||||||
|
{
|
||||||
|
TileID = 0,
|
||||||
|
WangID = [1,1,0,0,0,1,1,1]
|
||||||
|
},
|
||||||
|
new WangTile
|
||||||
|
{
|
||||||
|
TileID = 1,
|
||||||
|
WangID = [1,1,1,1,0,0,0,1]
|
||||||
|
},
|
||||||
|
new WangTile
|
||||||
|
{
|
||||||
|
TileID = 10,
|
||||||
|
WangID = [0,0,0,1,1,1,1,1]
|
||||||
|
},
|
||||||
|
new WangTile
|
||||||
|
{
|
||||||
|
TileID = 11,
|
||||||
|
WangID = [0,1,1,1,1,1,0,0]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
Layers = [
|
Layers = [
|
||||||
|
@ -69,22 +118,20 @@ public partial class TestData
|
||||||
Data = new Data
|
Data = new Data
|
||||||
{
|
{
|
||||||
Encoding = DataEncoding.Csv,
|
Encoding = DataEncoding.Csv,
|
||||||
Chunks = null,
|
GlobalTileIDs = new Optional<uint[]>([
|
||||||
Compression = null,
|
|
||||||
GlobalTileIDs = [
|
|
||||||
2, 2, 12, 11, 0,
|
2, 2, 12, 11, 0,
|
||||||
1, 12, 1, 11, 0,
|
1, 12, 1, 11, 0,
|
||||||
2, 1, 0, 1, 0,
|
2, 1, 0, 1, 0,
|
||||||
12, 11, 12, 2, 0,
|
12, 11, 12, 2, 0,
|
||||||
0, 0, 0, 0, 0
|
0, 0, 0, 0, 0
|
||||||
],
|
]),
|
||||||
FlippingFlags = [
|
FlippingFlags = new Optional<FlippingFlags[]>([
|
||||||
FlippingFlags.FlippedHorizontally, FlippingFlags.None, FlippingFlags.FlippedHorizontally, FlippingFlags.FlippedHorizontally, FlippingFlags.None,
|
FlippingFlags.FlippedHorizontally, FlippingFlags.None, FlippingFlags.FlippedHorizontally, FlippingFlags.FlippedHorizontally, FlippingFlags.None,
|
||||||
FlippingFlags.FlippedVertically, FlippingFlags.None, FlippingFlags.None, FlippingFlags.FlippedVertically | FlippingFlags.FlippedHorizontally, FlippingFlags.None,
|
FlippingFlags.FlippedVertically, FlippingFlags.None, FlippingFlags.None, FlippingFlags.FlippedVertically | FlippingFlags.FlippedHorizontally, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.FlippedVertically | FlippingFlags.FlippedHorizontally, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.FlippedVertically | FlippingFlags.FlippedHorizontally, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.FlippedHorizontally, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.FlippedHorizontally, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
||||||
]
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,83 @@
|
||||||
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
|
public partial class TestData
|
||||||
|
{
|
||||||
|
public static Map MapWithClassAndProps() => new Map
|
||||||
|
{
|
||||||
|
Class = "TestClass",
|
||||||
|
Orientation = MapOrientation.Orthogonal,
|
||||||
|
Width = 5,
|
||||||
|
Height = 5,
|
||||||
|
TileWidth = 32,
|
||||||
|
TileHeight = 32,
|
||||||
|
Infinite = false,
|
||||||
|
ParallaxOriginX = 0,
|
||||||
|
ParallaxOriginY = 0,
|
||||||
|
RenderOrder = RenderOrder.RightDown,
|
||||||
|
CompressionLevel = -1,
|
||||||
|
BackgroundColor = new Color { R = 0, G = 0, B = 0, A = 0 },
|
||||||
|
Version = "1.10",
|
||||||
|
TiledVersion = "1.11.0",
|
||||||
|
NextLayerID = 2,
|
||||||
|
NextObjectID = 1,
|
||||||
|
Layers = [
|
||||||
|
new TileLayer
|
||||||
|
{
|
||||||
|
ID = 1,
|
||||||
|
Name = "Tile Layer 1",
|
||||||
|
Width = 5,
|
||||||
|
Height = 5,
|
||||||
|
Data = new Data
|
||||||
|
{
|
||||||
|
Encoding = DataEncoding.Csv,
|
||||||
|
GlobalTileIDs = new Optional<uint[]>([
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0
|
||||||
|
]),
|
||||||
|
FlippingFlags = new Optional<FlippingFlags[]>([
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
Properties = [
|
||||||
|
new BoolProperty
|
||||||
|
{
|
||||||
|
Name = "classbool",
|
||||||
|
Value = true
|
||||||
|
},
|
||||||
|
new StringProperty
|
||||||
|
{
|
||||||
|
Name = "classstring",
|
||||||
|
Value = "I am not default value"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
public static IReadOnlyCollection<ICustomTypeDefinition> MapWithClassAndPropsCustomTypeDefinitions() => [
|
||||||
|
new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = "TestClass",
|
||||||
|
UseAs = CustomClassUseAs.Map,
|
||||||
|
Members = [
|
||||||
|
new BoolProperty
|
||||||
|
{
|
||||||
|
Name = "classbool",
|
||||||
|
Value = true
|
||||||
|
},
|
||||||
|
new StringProperty
|
||||||
|
{
|
||||||
|
Name = "classstring",
|
||||||
|
Value = "Hello there default value"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
{ "class":"TestClass",
|
||||||
|
"compressionlevel":-1,
|
||||||
|
"height":5,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0],
|
||||||
|
"height":5,
|
||||||
|
"id":1,
|
||||||
|
"name":"Tile Layer 1",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":5,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":2,
|
||||||
|
"nextobjectid":1,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"classstring",
|
||||||
|
"type":"string",
|
||||||
|
"value":"I am not default value"
|
||||||
|
}],
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"1.11.0",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":"1.10",
|
||||||
|
"width":5
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" class="TestClass" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<properties>
|
||||||
|
<property name="classstring" value="I am not default value"/>
|
||||||
|
</properties>
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
|
@ -0,0 +1,83 @@
|
||||||
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
|
public partial class TestData
|
||||||
|
{
|
||||||
|
public static Map MapWithClass() => new Map
|
||||||
|
{
|
||||||
|
Class = "TestClass",
|
||||||
|
Orientation = MapOrientation.Orthogonal,
|
||||||
|
Width = 5,
|
||||||
|
Height = 5,
|
||||||
|
TileWidth = 32,
|
||||||
|
TileHeight = 32,
|
||||||
|
Infinite = false,
|
||||||
|
ParallaxOriginX = 0,
|
||||||
|
ParallaxOriginY = 0,
|
||||||
|
RenderOrder = RenderOrder.RightDown,
|
||||||
|
CompressionLevel = -1,
|
||||||
|
BackgroundColor = new Color { R = 0, G = 0, B = 0, A = 0 },
|
||||||
|
Version = "1.10",
|
||||||
|
TiledVersion = "1.11.0",
|
||||||
|
NextLayerID = 2,
|
||||||
|
NextObjectID = 1,
|
||||||
|
Layers = [
|
||||||
|
new TileLayer
|
||||||
|
{
|
||||||
|
ID = 1,
|
||||||
|
Name = "Tile Layer 1",
|
||||||
|
Width = 5,
|
||||||
|
Height = 5,
|
||||||
|
Data = new Data
|
||||||
|
{
|
||||||
|
Encoding = DataEncoding.Csv,
|
||||||
|
GlobalTileIDs = new Optional<uint[]>([
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0
|
||||||
|
]),
|
||||||
|
FlippingFlags = new Optional<FlippingFlags[]>([
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
Properties = [
|
||||||
|
new BoolProperty
|
||||||
|
{
|
||||||
|
Name = "classbool",
|
||||||
|
Value = true
|
||||||
|
},
|
||||||
|
new StringProperty
|
||||||
|
{
|
||||||
|
Name = "classstring",
|
||||||
|
Value = "Hello there default value"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
public static IReadOnlyCollection<ICustomTypeDefinition> MapWithClassCustomTypeDefinitions() => [
|
||||||
|
new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = "TestClass",
|
||||||
|
UseAs = CustomClassUseAs.Map,
|
||||||
|
Members = [
|
||||||
|
new BoolProperty
|
||||||
|
{
|
||||||
|
Name = "classbool",
|
||||||
|
Value = true
|
||||||
|
},
|
||||||
|
new StringProperty
|
||||||
|
{
|
||||||
|
Name = "classstring",
|
||||||
|
Value = "Hello there default value"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
{ "class":"TestClass",
|
||||||
|
"compressionlevel":-1,
|
||||||
|
"height":5,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0],
|
||||||
|
"height":5,
|
||||||
|
"id":1,
|
||||||
|
"name":"Tile Layer 1",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":5,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":2,
|
||||||
|
"nextobjectid":1,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"1.11.0",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":"1.10",
|
||||||
|
"width":5
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" class="TestClass" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
|
@ -13,9 +13,6 @@ public partial class TestData
|
||||||
TileWidth = 32,
|
TileWidth = 32,
|
||||||
TileHeight = 16,
|
TileHeight = 16,
|
||||||
Infinite = false,
|
Infinite = false,
|
||||||
HexSideLength = null,
|
|
||||||
StaggerAxis = null,
|
|
||||||
StaggerIndex = null,
|
|
||||||
ParallaxOriginX = 0,
|
ParallaxOriginX = 0,
|
||||||
ParallaxOriginY = 0,
|
ParallaxOriginY = 0,
|
||||||
RenderOrder = RenderOrder.RightDown,
|
RenderOrder = RenderOrder.RightDown,
|
||||||
|
@ -35,22 +32,20 @@ public partial class TestData
|
||||||
Data = new Data
|
Data = new Data
|
||||||
{
|
{
|
||||||
Encoding = DataEncoding.Csv,
|
Encoding = DataEncoding.Csv,
|
||||||
Chunks = null,
|
GlobalTileIDs = new Optional<uint[]>([
|
||||||
Compression = null,
|
|
||||||
GlobalTileIDs = [
|
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0
|
0, 0, 0, 0, 0
|
||||||
],
|
]),
|
||||||
FlippingFlags = [
|
FlippingFlags = new Optional<FlippingFlags[]>([
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
||||||
]
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
|
@ -13,9 +13,6 @@ public partial class TestData
|
||||||
TileWidth = 32,
|
TileWidth = 32,
|
||||||
TileHeight = 32,
|
TileHeight = 32,
|
||||||
Infinite = false,
|
Infinite = false,
|
||||||
HexSideLength = null,
|
|
||||||
StaggerAxis = null,
|
|
||||||
StaggerIndex = null,
|
|
||||||
ParallaxOriginX = 0,
|
ParallaxOriginX = 0,
|
||||||
ParallaxOriginY = 0,
|
ParallaxOriginY = 0,
|
||||||
RenderOrder = RenderOrder.RightDown,
|
RenderOrder = RenderOrder.RightDown,
|
||||||
|
@ -35,22 +32,20 @@ public partial class TestData
|
||||||
Data = new Data
|
Data = new Data
|
||||||
{
|
{
|
||||||
Encoding = DataEncoding.Csv,
|
Encoding = DataEncoding.Csv,
|
||||||
Chunks = null,
|
GlobalTileIDs = new Optional<uint[]>([
|
||||||
Compression = null,
|
|
||||||
GlobalTileIDs = [
|
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0
|
0, 0, 0, 0, 0
|
||||||
],
|
]),
|
||||||
FlippingFlags = [
|
FlippingFlags = new Optional<FlippingFlags[]>([
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
||||||
]
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
|
@ -13,9 +13,6 @@ public partial class TestData
|
||||||
TileWidth = 32,
|
TileWidth = 32,
|
||||||
TileHeight = 32,
|
TileHeight = 32,
|
||||||
Infinite = false,
|
Infinite = false,
|
||||||
HexSideLength = null,
|
|
||||||
StaggerAxis = null,
|
|
||||||
StaggerIndex = null,
|
|
||||||
ParallaxOriginX = 0,
|
ParallaxOriginX = 0,
|
||||||
ParallaxOriginY = 0,
|
ParallaxOriginY = 0,
|
||||||
RenderOrder = RenderOrder.RightDown,
|
RenderOrder = RenderOrder.RightDown,
|
||||||
|
@ -35,22 +32,20 @@ public partial class TestData
|
||||||
Data = new Data
|
Data = new Data
|
||||||
{
|
{
|
||||||
Encoding = DataEncoding.Csv,
|
Encoding = DataEncoding.Csv,
|
||||||
Chunks = null,
|
GlobalTileIDs = new Optional<uint[]>([
|
||||||
Compression = null,
|
|
||||||
GlobalTileIDs = [
|
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0
|
0, 0, 0, 0, 0
|
||||||
],
|
]),
|
||||||
FlippingFlags = [
|
FlippingFlags = new Optional<FlippingFlags[]>([
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
||||||
]
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
|
@ -13,9 +13,6 @@ public partial class TestData
|
||||||
TileWidth = 32,
|
TileWidth = 32,
|
||||||
TileHeight = 32,
|
TileHeight = 32,
|
||||||
Infinite = false,
|
Infinite = false,
|
||||||
HexSideLength = null,
|
|
||||||
StaggerAxis = null,
|
|
||||||
StaggerIndex = null,
|
|
||||||
ParallaxOriginX = 0,
|
ParallaxOriginX = 0,
|
||||||
ParallaxOriginY = 0,
|
ParallaxOriginY = 0,
|
||||||
RenderOrder = RenderOrder.RightDown,
|
RenderOrder = RenderOrder.RightDown,
|
||||||
|
@ -28,6 +25,8 @@ public partial class TestData
|
||||||
Tilesets = [
|
Tilesets = [
|
||||||
new Tileset
|
new Tileset
|
||||||
{
|
{
|
||||||
|
Version = "1.10",
|
||||||
|
TiledVersion = "1.11.0",
|
||||||
FirstGID = 1,
|
FirstGID = 1,
|
||||||
Name = "tileset",
|
Name = "tileset",
|
||||||
TileWidth = 32,
|
TileWidth = 32,
|
||||||
|
@ -53,22 +52,20 @@ public partial class TestData
|
||||||
Data = new Data
|
Data = new Data
|
||||||
{
|
{
|
||||||
Encoding = DataEncoding.Csv,
|
Encoding = DataEncoding.Csv,
|
||||||
Chunks = null,
|
GlobalTileIDs = new Optional<uint[]>([
|
||||||
Compression = null,
|
|
||||||
GlobalTileIDs = [
|
|
||||||
1, 1, 0, 0, 7,
|
1, 1, 0, 0, 7,
|
||||||
1, 1, 0, 0, 7,
|
1, 1, 0, 0, 7,
|
||||||
0, 0, 0, 0, 7,
|
0, 0, 0, 0, 7,
|
||||||
9, 10, 0, 0, 7,
|
9, 10, 0, 0, 7,
|
||||||
17, 18, 0, 0, 0
|
17, 18, 0, 0, 0
|
||||||
],
|
]),
|
||||||
FlippingFlags = [
|
FlippingFlags = new Optional<FlippingFlags[]>([
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
||||||
]
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
@ -13,9 +13,6 @@ public partial class TestData
|
||||||
TileWidth = 32,
|
TileWidth = 32,
|
||||||
TileHeight = 32,
|
TileHeight = 32,
|
||||||
Infinite = false,
|
Infinite = false,
|
||||||
HexSideLength = null,
|
|
||||||
StaggerAxis = null,
|
|
||||||
StaggerIndex = null,
|
|
||||||
ParallaxOriginX = 0,
|
ParallaxOriginX = 0,
|
||||||
ParallaxOriginY = 0,
|
ParallaxOriginY = 0,
|
||||||
RenderOrder = RenderOrder.RightDown,
|
RenderOrder = RenderOrder.RightDown,
|
||||||
|
@ -56,22 +53,20 @@ public partial class TestData
|
||||||
Data = new Data
|
Data = new Data
|
||||||
{
|
{
|
||||||
Encoding = DataEncoding.Csv,
|
Encoding = DataEncoding.Csv,
|
||||||
Chunks = null,
|
GlobalTileIDs = new Optional<uint[]>([
|
||||||
Compression = null,
|
|
||||||
GlobalTileIDs = [
|
|
||||||
1, 1, 0, 0, 7,
|
1, 1, 0, 0, 7,
|
||||||
1, 1, 0, 0, 7,
|
1, 1, 0, 0, 7,
|
||||||
0, 0, 1, 0, 7,
|
0, 0, 1, 0, 7,
|
||||||
0, 0, 0, 1, 7,
|
0, 0, 0, 1, 7,
|
||||||
21, 21, 21, 21, 1
|
21, 21, 21, 21, 1
|
||||||
],
|
]),
|
||||||
FlippingFlags = [
|
FlippingFlags = new Optional<FlippingFlags[]>([
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
||||||
]
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
@ -13,9 +13,6 @@ public partial class TestData
|
||||||
TileWidth = 32,
|
TileWidth = 32,
|
||||||
TileHeight = 32,
|
TileHeight = 32,
|
||||||
Infinite = false,
|
Infinite = false,
|
||||||
HexSideLength = null,
|
|
||||||
StaggerAxis = null,
|
|
||||||
StaggerIndex = null,
|
|
||||||
ParallaxOriginX = 0,
|
ParallaxOriginX = 0,
|
||||||
ParallaxOriginY = 0,
|
ParallaxOriginY = 0,
|
||||||
RenderOrder = RenderOrder.RightDown,
|
RenderOrder = RenderOrder.RightDown,
|
||||||
|
@ -56,22 +53,20 @@ public partial class TestData
|
||||||
Data = new Data
|
Data = new Data
|
||||||
{
|
{
|
||||||
Encoding = DataEncoding.Csv,
|
Encoding = DataEncoding.Csv,
|
||||||
Chunks = null,
|
GlobalTileIDs = new Optional<uint[]>([
|
||||||
Compression = null,
|
|
||||||
GlobalTileIDs = [
|
|
||||||
1, 1, 0, 0, 7,
|
1, 1, 0, 0, 7,
|
||||||
1, 1, 0, 0, 7,
|
1, 1, 0, 0, 7,
|
||||||
0, 0, 1, 0, 7,
|
0, 0, 1, 0, 7,
|
||||||
0, 0, 0, 1, 7,
|
0, 0, 0, 1, 7,
|
||||||
21, 21, 21, 21, 1
|
21, 21, 21, 21, 1
|
||||||
],
|
]),
|
||||||
FlippingFlags = [
|
FlippingFlags = new Optional<FlippingFlags[]>([
|
||||||
FlippingFlags.None, FlippingFlags.FlippedDiagonally | FlippingFlags.FlippedHorizontally, FlippingFlags.None, FlippingFlags.None, FlippingFlags.FlippedVertically,
|
FlippingFlags.None, FlippingFlags.FlippedDiagonally | FlippingFlags.FlippedHorizontally, FlippingFlags.None, FlippingFlags.None, FlippingFlags.FlippedVertically,
|
||||||
FlippingFlags.FlippedDiagonally | FlippingFlags.FlippedVertically, FlippingFlags.FlippedVertically | FlippingFlags.FlippedHorizontally, FlippingFlags.None, FlippingFlags.None, FlippingFlags.FlippedVertically,
|
FlippingFlags.FlippedDiagonally | FlippingFlags.FlippedVertically, FlippingFlags.FlippedVertically | FlippingFlags.FlippedHorizontally, FlippingFlags.None, FlippingFlags.None, FlippingFlags.FlippedVertically,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.FlippedVertically,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.FlippedVertically,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.FlippedVertically,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.FlippedVertically,
|
||||||
FlippingFlags.FlippedHorizontally, FlippingFlags.FlippedHorizontally, FlippingFlags.FlippedHorizontally, FlippingFlags.FlippedHorizontally, FlippingFlags.None
|
FlippingFlags.FlippedHorizontally, FlippingFlags.FlippedHorizontally, FlippingFlags.FlippedHorizontally, FlippingFlags.FlippedHorizontally, FlippingFlags.None
|
||||||
]
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
@ -13,9 +13,6 @@ public partial class TestData
|
||||||
TileWidth = 32,
|
TileWidth = 32,
|
||||||
TileHeight = 32,
|
TileHeight = 32,
|
||||||
Infinite = false,
|
Infinite = false,
|
||||||
HexSideLength = null,
|
|
||||||
StaggerAxis = null,
|
|
||||||
StaggerIndex = null,
|
|
||||||
ParallaxOriginX = 0,
|
ParallaxOriginX = 0,
|
||||||
ParallaxOriginY = 0,
|
ParallaxOriginY = 0,
|
||||||
RenderOrder = RenderOrder.RightDown,
|
RenderOrder = RenderOrder.RightDown,
|
||||||
|
@ -124,22 +121,20 @@ public partial class TestData
|
||||||
Data = new Data
|
Data = new Data
|
||||||
{
|
{
|
||||||
Encoding = DataEncoding.Csv,
|
Encoding = DataEncoding.Csv,
|
||||||
Chunks = null,
|
GlobalTileIDs = new Optional<uint[]>([
|
||||||
Compression = null,
|
|
||||||
GlobalTileIDs = [
|
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0
|
0, 0, 0, 0, 0
|
||||||
],
|
]),
|
||||||
FlippingFlags = [
|
FlippingFlags = new Optional<FlippingFlags[]>([
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
||||||
]
|
])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new TileLayer
|
new TileLayer
|
||||||
|
@ -151,22 +146,20 @@ public partial class TestData
|
||||||
Data = new Data
|
Data = new Data
|
||||||
{
|
{
|
||||||
Encoding = DataEncoding.Csv,
|
Encoding = DataEncoding.Csv,
|
||||||
Chunks = null,
|
GlobalTileIDs = new Optional<uint[]>([
|
||||||
Compression = null,
|
|
||||||
GlobalTileIDs = [
|
|
||||||
0, 15, 15, 0, 0,
|
0, 15, 15, 0, 0,
|
||||||
0, 15, 15, 0, 0,
|
0, 15, 15, 0, 0,
|
||||||
0, 15, 15, 15, 0,
|
0, 15, 15, 15, 0,
|
||||||
15, 15, 15, 0, 0,
|
15, 15, 15, 0, 0,
|
||||||
0, 0, 0, 0, 0
|
0, 0, 0, 0, 0
|
||||||
],
|
]),
|
||||||
FlippingFlags = [
|
FlippingFlags = new Optional<FlippingFlags[]>([
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
||||||
]
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -193,22 +186,20 @@ public partial class TestData
|
||||||
Data = new Data
|
Data = new Data
|
||||||
{
|
{
|
||||||
Encoding = DataEncoding.Csv,
|
Encoding = DataEncoding.Csv,
|
||||||
Chunks = null,
|
GlobalTileIDs = new Optional<uint[]>([
|
||||||
Compression = null,
|
|
||||||
GlobalTileIDs = [
|
|
||||||
1, 1, 1, 1, 1,
|
1, 1, 1, 1, 1,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0
|
0, 0, 0, 0, 0
|
||||||
],
|
]),
|
||||||
FlippingFlags = [
|
FlippingFlags = new Optional<FlippingFlags[]>([
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None,
|
||||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
||||||
]
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
317
src/DotTiled.Tests/UnitTests/OptionalTests.cs
Normal file
|
@ -0,0 +1,317 @@
|
||||||
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
|
public class OptionalTests
|
||||||
|
{
|
||||||
|
// Constructor Tests
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WithNonNullValue_ShouldSetHasValueToTrue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
string expectedValue = "test";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var optional = new Optional<string>(expectedValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(optional.HasValue);
|
||||||
|
Assert.Equal(expectedValue, optional.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WithNullValue_ShouldSetHasValueToTrue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
string expectedValue = null;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var optional = new Optional<string>(expectedValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(optional.HasValue);
|
||||||
|
Assert.Null(optional.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DefaultConstructor_ShouldSetHasValueToFalse()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var optional = new Optional<string>();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(optional.HasValue);
|
||||||
|
_ = Assert.Throws<InvalidOperationException>(() => optional.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implicit Conversion Tests
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ImplicitConversion_FromValueToOptional_ShouldSetHasValueToTrue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
int expectedValue = 5;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
Optional<int> optional = expectedValue;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(optional.HasValue);
|
||||||
|
Assert.Equal(expectedValue, optional.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ImplicitConversion_FromOptionalToValue_ShouldReturnValue_WhenHasValueIsTrue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
int expectedValue = 10;
|
||||||
|
var optional = new Optional<int>(expectedValue);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
int value = optional;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedValue, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ImplicitConversion_FromOptionalToValue_ShouldThrowException_WhenHasValueIsFalse()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var optional = new Optional<int>();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
_ = Assert.Throws<InvalidOperationException>(() => { int value = optional; });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToString Method Tests
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToString_WithValue_ShouldReturnValueToString()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
int expectedValue = 42;
|
||||||
|
var optional = new Optional<int>(expectedValue);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
string result = optional.ToString();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
#pragma warning disable CA1305 // Specify IFormatProvider
|
||||||
|
Assert.Equal(expectedValue.ToString(), result);
|
||||||
|
#pragma warning restore CA1305 // Specify IFormatProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToString_WithoutValue_ShouldReturnEmpty()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var optional = new Optional<int>();
|
||||||
|
string result = optional.ToString();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Empty", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equality Tests
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_WithSameValue_ShouldReturnTrue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var optional1 = new Optional<int>(10);
|
||||||
|
var optional2 = new Optional<int>(10);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
bool areEqual = optional1.Equals(optional2);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(areEqual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_WithDifferentValues_ShouldReturnFalse()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var optional1 = new Optional<int>(10);
|
||||||
|
var optional2 = new Optional<int>(20);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
bool areEqual = optional1.Equals(optional2);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(areEqual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_WithNull_ShouldReturnFalse()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var optional = new Optional<string>("test");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
bool areEqual = optional.Equals(null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(areEqual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_WithEmptyOptional_ShouldReturnTrue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var optional1 = new Optional<string>();
|
||||||
|
var optional2 = new Optional<string>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
bool areEqual = optional1.Equals(optional2);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(areEqual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_WithSameReferenceTypeValue_ShouldReturnTrue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var value = new object();
|
||||||
|
var optional1 = new Optional<object>(value);
|
||||||
|
var optional2 = new Optional<object>(value);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
bool areEqual = optional1.Equals(optional2);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(areEqual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_WithDifferentReferenceTypeValues_ShouldReturnFalse()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var optional1 = new Optional<object>(new object());
|
||||||
|
var optional2 = new Optional<object>(new object());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
bool areEqual = optional1.Equals(optional2);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(areEqual);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHashCode Method Tests
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetHashCode_WithValue_ShouldReturnValueHashCode()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
int value = 42;
|
||||||
|
var optional = new Optional<int>(value);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
int hashCode = optional.GetHashCode();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(value.GetHashCode(), hashCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetHashCode_WithoutValue_ShouldReturnZero()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var optional = new Optional<int>();
|
||||||
|
int hashCode = optional.GetHashCode();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(0, hashCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exception Tests
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Value_WhenHasValueIsFalse_ShouldThrowInvalidOperationException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var optional = new Optional<string>();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
_ = Assert.Throws<InvalidOperationException>(() => optional.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ImplicitConversion_WhenHasValueIsFalse_ShouldThrowInvalidOperationException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var optional = new Optional<int>();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
_ = Assert.Throws<InvalidOperationException>(() => { int value = optional; });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edge Cases
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EmptyOptionalEquality_ShouldReturnTrue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var optional1 = new Optional<int>();
|
||||||
|
var optional2 = new Optional<int>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
bool areEqual = optional1.Equals(optional2);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(areEqual);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special Scenarios
|
||||||
|
|
||||||
|
public struct CustomStruct
|
||||||
|
{
|
||||||
|
public int X { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OptionalWithCustomStruct_ShouldBehaveCorrectly()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var structValue = new CustomStruct { X = 10 };
|
||||||
|
var optional = new Optional<CustomStruct>(structValue);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
CustomStruct value = optional.Value;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(optional.HasValue);
|
||||||
|
Assert.Equal(structValue, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OptionalWithNullableInt_ShouldBehaveCorrectly()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
int? nullableValue = 5;
|
||||||
|
var optional = new Optional<int?>(nullableValue);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
int? value = optional.Value;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(optional.HasValue);
|
||||||
|
Assert.Equal(nullableValue, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OptionalWithNullNullableInt_ShouldBehaveCorrectly()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
int? nullableValue = null;
|
||||||
|
var optional = new Optional<int?>(nullableValue);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
int? value = optional.Value;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(optional.HasValue);
|
||||||
|
Assert.Null(value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
|
public class CustomClassDefinitionTests
|
||||||
|
{
|
||||||
|
private sealed class TestClass1
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = "John Doe";
|
||||||
|
public int Age { get; set; } = 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CustomClassDefinition ExpectedTestClass1Definition => new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = "TestClass1",
|
||||||
|
UseAs = CustomClassUseAs.All,
|
||||||
|
Members = new List<IProperty>
|
||||||
|
{
|
||||||
|
new StringProperty { Name = "Name", Value = "John Doe" },
|
||||||
|
new IntProperty { Name = "Age", Value = 42 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private sealed class TestClass2WithNestedClass
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = "John Doe";
|
||||||
|
public int Age { get; set; } = 42;
|
||||||
|
public TestClass1 Nested { get; set; } = new TestClass1();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CustomClassDefinition ExpectedTestClass2WithNestedClassDefinition => new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = "TestClass2WithNestedClass",
|
||||||
|
UseAs = CustomClassUseAs.All,
|
||||||
|
Members = [
|
||||||
|
new StringProperty { Name = "Name", Value = "John Doe" },
|
||||||
|
new IntProperty { Name = "Age", Value = 42 },
|
||||||
|
new ClassProperty
|
||||||
|
{
|
||||||
|
Name = "Nested",
|
||||||
|
PropertyType = "TestClass1",
|
||||||
|
Value = []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
private sealed class TestClass3WithOverridenNestedClass
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = "John Doe";
|
||||||
|
public int Age { get; set; } = 42;
|
||||||
|
public TestClass1 Nested { get; set; } = new TestClass1
|
||||||
|
{
|
||||||
|
Name = "Jane Doe"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CustomClassDefinition ExpectedTestClass3WithOverridenNestedClassDefinition => new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = "TestClass3WithOverridenNestedClass",
|
||||||
|
UseAs = CustomClassUseAs.All,
|
||||||
|
Members = [
|
||||||
|
new StringProperty { Name = "Name", Value = "John Doe" },
|
||||||
|
new IntProperty { Name = "Age", Value = 42 },
|
||||||
|
new ClassProperty
|
||||||
|
{
|
||||||
|
Name = "Nested",
|
||||||
|
PropertyType = "TestClass1",
|
||||||
|
Value = [
|
||||||
|
new StringProperty { Name = "Name", Value = "Jane Doe" },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
private static IEnumerable<(Type, CustomClassDefinition)> GetCustomClassDefinitionTestData()
|
||||||
|
{
|
||||||
|
yield return (typeof(TestClass1), ExpectedTestClass1Definition);
|
||||||
|
yield return (typeof(TestClass2WithNestedClass), ExpectedTestClass2WithNestedClassDefinition);
|
||||||
|
yield return (typeof(TestClass3WithOverridenNestedClass), ExpectedTestClass3WithOverridenNestedClassDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> CustomClassDefinitionTestData =>
|
||||||
|
GetCustomClassDefinitionTestData().Select(data => new object[] { data.Item1, data.Item2 });
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(CustomClassDefinitionTestData))]
|
||||||
|
public void FromClass_Type_WhenTypeIsCustomClass_ReturnsCustomClassDefinition(Type type, CustomClassDefinition expected)
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var result = CustomClassDefinition.FromClass(type);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
DotTiledAssert.AssertCustomClassDefinitionEqual(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromClass_Type_WhenTypeIsNull_ThrowsArgumentNullException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
Type type = null;
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<ArgumentNullException>(() => CustomClassDefinition.FromClass(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromClass_Type_WhenTypeIsString_ThrowsArgumentException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
Type type = typeof(string);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<ArgumentException>(() => CustomClassDefinition.FromClass(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromClass_Type_WhenTypeIsNotClass_ThrowsArgumentException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
Type type = typeof(int);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<ArgumentException>(() => CustomClassDefinition.FromClass(type));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
|
public class CustomEnumDefinitionTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void FromEnum_Type_WhenTypeIsNotEnum_ThrowsArgumentException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var type = typeof(string);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<ArgumentException>(() => CustomEnumDefinition.FromEnum(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum TestEnum1 { Value1, Value2, Value3 }
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromEnum_Type_WhenTypeIsEnum_ReturnsCustomEnumDefinition()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var type = typeof(TestEnum1);
|
||||||
|
var expected = new CustomEnumDefinition
|
||||||
|
{
|
||||||
|
ID = 0,
|
||||||
|
Name = "TestEnum1",
|
||||||
|
StorageType = CustomEnumStorageType.Int,
|
||||||
|
Values = ["Value1", "Value2", "Value3"],
|
||||||
|
ValueAsFlags = false
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = CustomEnumDefinition.FromEnum(type);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
DotTiledAssert.AssertCustomEnumDefinitionEqual(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
private enum TestEnum2 { Value1, Value2, Value3 }
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromEnum_Type_WhenEnumIsFlags_ReturnsCustomEnumDefinition()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var type = typeof(TestEnum2);
|
||||||
|
var expected = new CustomEnumDefinition
|
||||||
|
{
|
||||||
|
ID = 0,
|
||||||
|
Name = "TestEnum2",
|
||||||
|
StorageType = CustomEnumStorageType.Int,
|
||||||
|
Values = ["Value1", "Value2", "Value3"],
|
||||||
|
ValueAsFlags = true
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = CustomEnumDefinition.FromEnum(type);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
DotTiledAssert.AssertCustomEnumDefinitionEqual(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromEnum_T_WhenTypeIsEnum_ReturnsCustomEnumDefinition()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = new CustomEnumDefinition
|
||||||
|
{
|
||||||
|
ID = 0,
|
||||||
|
Name = "TestEnum1",
|
||||||
|
StorageType = CustomEnumStorageType.Int,
|
||||||
|
Values = ["Value1", "Value2", "Value3"],
|
||||||
|
ValueAsFlags = false
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = CustomEnumDefinition.FromEnum<TestEnum1>();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
DotTiledAssert.AssertCustomEnumDefinitionEqual(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromEnum_T_WhenEnumIsFlags_ReturnsCustomEnumDefinition()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = new CustomEnumDefinition
|
||||||
|
{
|
||||||
|
ID = 0,
|
||||||
|
Name = "TestEnum2",
|
||||||
|
StorageType = CustomEnumStorageType.Int,
|
||||||
|
Values = ["Value1", "Value2", "Value3"],
|
||||||
|
ValueAsFlags = true
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = CustomEnumDefinition.FromEnum<TestEnum2>();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
DotTiledAssert.AssertCustomEnumDefinitionEqual(expected, result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,300 @@
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
|
public class HasPropertiesBaseTests
|
||||||
|
{
|
||||||
|
private sealed class TestHasProperties(IList<IProperty> props) : HasPropertiesBase
|
||||||
|
{
|
||||||
|
public override IList<IProperty> GetProperties() => props;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TryGetProperty_PropertyNotFound_ReturnsFalseAndOutIsNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var hasProperties = new TestHasProperties([]);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = hasProperties.TryGetProperty<BoolProperty>("Test", out var property);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Null(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TryGetProperty_PropertyFound_ReturnsTrueAndOutIsProperty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
List<IProperty> props = [new BoolProperty { Name = "Test", Value = true }];
|
||||||
|
var hasProperties = new TestHasProperties(props);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = hasProperties.TryGetProperty<BoolProperty>("Test", out var property);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.NotNull(property);
|
||||||
|
Assert.Equal("Test", property.Name);
|
||||||
|
Assert.True(property.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetProperty_PropertyNotFound_ThrowsKeyNotFoundException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var hasProperties = new TestHasProperties([]);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
_ = Assert.Throws<KeyNotFoundException>(() => hasProperties.GetProperty<BoolProperty>("Test"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetProperty_PropertyFound_ReturnsProperty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
List<IProperty> props = [new BoolProperty { Name = "Test", Value = true }];
|
||||||
|
var hasProperties = new TestHasProperties(props);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var property = hasProperties.GetProperty<BoolProperty>("Test");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(property);
|
||||||
|
Assert.Equal("Test", property.Name);
|
||||||
|
Assert.True(property.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetProperty_PropertyIsWrongType_ThrowsInvalidCastException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
List<IProperty> props = [new BoolProperty { Name = "Test", Value = true }];
|
||||||
|
var hasProperties = new TestHasProperties(props);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
_ = Assert.Throws<InvalidCastException>(() => hasProperties.GetProperty<IntProperty>("Test"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class MapTo
|
||||||
|
{
|
||||||
|
public bool MapToBool { get; set; } = false;
|
||||||
|
public Color MapToColor { get; set; } = Color.Parse("#00000000", CultureInfo.InvariantCulture);
|
||||||
|
public float MapToFloat { get; set; } = 0.0f;
|
||||||
|
public string MapToFile { get; set; } = "";
|
||||||
|
public int MapToInt { get; set; } = 0;
|
||||||
|
public int MapToObject { get; set; } = 0;
|
||||||
|
public string MapToString { get; set; } = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MapPropertiesTo_NestedPropertyNotFound_ThrowsKeyNotFoundException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
List<IProperty> props = [
|
||||||
|
new ClassProperty {
|
||||||
|
Name = "ClassInObject",
|
||||||
|
PropertyType = "MapTo",
|
||||||
|
Value = [
|
||||||
|
new StringProperty { Name = "PropertyThatDoesNotExistInMapTo", Value = "Test" }
|
||||||
|
],
|
||||||
|
}
|
||||||
|
];
|
||||||
|
var hasProperties = new TestHasProperties(props);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
_ = Assert.Throws<KeyNotFoundException>(() => hasProperties.GetProperty<ClassProperty>("ClassInObject").MapPropertiesTo<MapTo>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MapPropertiesTo_NestedPropertyIsNotClassProperty_ThrowsInvalidCastException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
List<IProperty> props = [
|
||||||
|
new StringProperty { Name = "ClassInObject", Value = "Test" }
|
||||||
|
];
|
||||||
|
var hasProperties = new TestHasProperties(props);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
_ = Assert.Throws<InvalidCastException>(() => hasProperties.GetProperty<ClassProperty>("ClassInObject").MapPropertiesTo<MapTo>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MapPropertiesTo_NestedAllBasicValidProperties_ReturnsMappedProperty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
List<IProperty> props = [
|
||||||
|
new ClassProperty {
|
||||||
|
Name = "ClassInObject",
|
||||||
|
PropertyType = "MapTo",
|
||||||
|
Value = [
|
||||||
|
new BoolProperty { Name = "MapToBool", Value = true },
|
||||||
|
new ColorProperty { Name = "MapToColor", Value = Color.Parse("#FF0000FF", CultureInfo.InvariantCulture) },
|
||||||
|
new FloatProperty { Name = "MapToFloat", Value = 1.0f },
|
||||||
|
new StringProperty { Name = "MapToFile", Value = "Test" },
|
||||||
|
new IntProperty { Name = "MapToInt", Value = 1 },
|
||||||
|
new IntProperty { Name = "MapToObject", Value = 1 },
|
||||||
|
new StringProperty { Name = "MapToString", Value = "Test" },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
];
|
||||||
|
var hasProperties = new TestHasProperties(props);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var mappedProperty = hasProperties.GetProperty<ClassProperty>("ClassInObject").MapPropertiesTo<MapTo>();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(mappedProperty.MapToBool);
|
||||||
|
Assert.Equal(Color.Parse("#FF0000FF", CultureInfo.InvariantCulture), mappedProperty.MapToColor);
|
||||||
|
Assert.Equal(1.0f, mappedProperty.MapToFloat);
|
||||||
|
Assert.Equal("Test", mappedProperty.MapToFile);
|
||||||
|
Assert.Equal(1, mappedProperty.MapToInt);
|
||||||
|
Assert.Equal(1, mappedProperty.MapToObject);
|
||||||
|
Assert.Equal("Test", mappedProperty.MapToString);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class NestedMapTo
|
||||||
|
{
|
||||||
|
public string NestedMapToString { get; set; } = "";
|
||||||
|
public MapTo MapToInNested { get; set; } = new MapTo();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MapPropertiesTo_NestedNestedMapTo_ReturnsMappedProperty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
List<IProperty> props = [
|
||||||
|
new ClassProperty {
|
||||||
|
Name = "ClassInObject",
|
||||||
|
PropertyType = "NestedMapTo",
|
||||||
|
Value = [
|
||||||
|
new StringProperty { Name = "NestedMapToString", Value = "Test" },
|
||||||
|
new ClassProperty {
|
||||||
|
Name = "MapToInNested",
|
||||||
|
PropertyType = "MapTo",
|
||||||
|
Value = [
|
||||||
|
new BoolProperty { Name = "MapToBool", Value = true },
|
||||||
|
new ColorProperty { Name = "MapToColor", Value = Color.Parse("#FF0000FF", CultureInfo.InvariantCulture) },
|
||||||
|
new FloatProperty { Name = "MapToFloat", Value = 1.0f },
|
||||||
|
new StringProperty { Name = "MapToFile", Value = "Test" },
|
||||||
|
new IntProperty { Name = "MapToInt", Value = 1 },
|
||||||
|
new IntProperty { Name = "MapToObject", Value = 1 },
|
||||||
|
new StringProperty { Name = "MapToString", Value = "Test" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
];
|
||||||
|
var hasProperties = new TestHasProperties(props);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var mappedProperty = hasProperties.GetProperty<ClassProperty>("ClassInObject").MapPropertiesTo<NestedMapTo>();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Test", mappedProperty.NestedMapToString);
|
||||||
|
Assert.True(mappedProperty.MapToInNested.MapToBool);
|
||||||
|
Assert.Equal(Color.Parse("#FF0000FF", CultureInfo.InvariantCulture), mappedProperty.MapToInNested.MapToColor);
|
||||||
|
Assert.Equal(1.0f, mappedProperty.MapToInNested.MapToFloat);
|
||||||
|
Assert.Equal("Test", mappedProperty.MapToInNested.MapToFile);
|
||||||
|
Assert.Equal(1, mappedProperty.MapToInNested.MapToInt);
|
||||||
|
Assert.Equal(1, mappedProperty.MapToInNested.MapToObject);
|
||||||
|
Assert.Equal("Test", mappedProperty.MapToInNested.MapToString);
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum TestEnum
|
||||||
|
{
|
||||||
|
TestValue1,
|
||||||
|
TestValue2,
|
||||||
|
TestValue3
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class EnumMapTo
|
||||||
|
{
|
||||||
|
public TestEnum EnumMapToEnum { get; set; } = TestEnum.TestValue1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MapPropertiesTo_NestedEnumProperty_ReturnsMappedProperty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
List<IProperty> props = [
|
||||||
|
new ClassProperty {
|
||||||
|
Name = "ClassInObject",
|
||||||
|
PropertyType = "EnumMapTo",
|
||||||
|
Value = [
|
||||||
|
new EnumProperty { Name = "EnumMapToEnum", PropertyType = "TestEnum", Value = new HashSet<string> { "TestValue1" } },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
];
|
||||||
|
var hasProperties = new TestHasProperties(props);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var mappedProperty = hasProperties.GetProperty<ClassProperty>("ClassInObject").MapPropertiesTo<EnumMapTo>();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(TestEnum.TestValue1, mappedProperty.EnumMapToEnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum TestEnumWithFlags
|
||||||
|
{
|
||||||
|
TestValue1 = 1,
|
||||||
|
TestValue2 = 2,
|
||||||
|
TestValue3 = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class EnumWithFlagsMapTo
|
||||||
|
{
|
||||||
|
public TestEnumWithFlags EnumWithFlagsMapToEnum { get; set; } = TestEnumWithFlags.TestValue1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MapPropertiesTo_NestedEnumWithFlagsProperty_ReturnsMappedProperty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
List<IProperty> props = [
|
||||||
|
new ClassProperty {
|
||||||
|
Name = "ClassInObject",
|
||||||
|
PropertyType = "EnumWithFlagsMapTo",
|
||||||
|
Value = [
|
||||||
|
new EnumProperty { Name = "EnumWithFlagsMapToEnum", PropertyType = "TestEnumWithFlags", Value = new HashSet<string> { "TestValue1", "TestValue2" } },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
];
|
||||||
|
var hasProperties = new TestHasProperties(props);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var mappedProperty = hasProperties.GetProperty<ClassProperty>("ClassInObject").MapPropertiesTo<EnumWithFlagsMapTo>();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(TestEnumWithFlags.TestValue1 | TestEnumWithFlags.TestValue2, mappedProperty.EnumWithFlagsMapToEnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MapPropertiesTo_WithProperties_ReturnsMappedProperty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
List<IProperty> props = [
|
||||||
|
new BoolProperty { Name = "MapToBool", Value = true },
|
||||||
|
new ColorProperty { Name = "MapToColor", Value = Color.Parse("#FF0000FF", CultureInfo.InvariantCulture) },
|
||||||
|
new FloatProperty { Name = "MapToFloat", Value = 1.0f },
|
||||||
|
new StringProperty { Name = "MapToFile", Value = "Test" },
|
||||||
|
new IntProperty { Name = "MapToInt", Value = 1 },
|
||||||
|
new IntProperty { Name = "MapToObject", Value = 1 },
|
||||||
|
new StringProperty { Name = "MapToString", Value = "Test" },
|
||||||
|
];
|
||||||
|
var hasProperties = new TestHasProperties(props);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var mappedProperty = hasProperties.MapPropertiesTo<MapTo>();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(mappedProperty.MapToBool);
|
||||||
|
Assert.Equal(Color.Parse("#FF0000FF", CultureInfo.InvariantCulture), mappedProperty.MapToColor);
|
||||||
|
Assert.Equal(1.0f, mappedProperty.MapToFloat);
|
||||||
|
Assert.Equal("Test", mappedProperty.MapToFile);
|
||||||
|
Assert.Equal(1, mappedProperty.MapToInt);
|
||||||
|
Assert.Equal(1, mappedProperty.MapToObject);
|
||||||
|
Assert.Equal("Test", mappedProperty.MapToString);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
using DotTiled.Serialization;
|
||||||
|
|
||||||
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
|
public class DefaultResourceCacheTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void GetTemplate_TemplateDoesNotExist_ReturnsEmptyOptional()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var cache = new DefaultResourceCache();
|
||||||
|
var path = "template.tsx";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = cache.GetTemplate(path);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result.HasValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetTemplate_TemplateHasBeenInserted_ReturnsTemplate()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var cache = new DefaultResourceCache();
|
||||||
|
var path = "template.tsx";
|
||||||
|
var template = new Template
|
||||||
|
{
|
||||||
|
Object = new EllipseObject { }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
cache.InsertTemplate(path, template);
|
||||||
|
var result = cache.GetTemplate(path);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result.HasValue);
|
||||||
|
Assert.Same(template, result.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetTileset_TilesetDoesNotExist_ReturnsEmptyOptional()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var cache = new DefaultResourceCache();
|
||||||
|
var path = "tileset.tsx";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = cache.GetTileset(path);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result.HasValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetTileset_TilesetHasBeenInserted_ReturnsTileset()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var cache = new DefaultResourceCache();
|
||||||
|
var path = "tileset.tsx";
|
||||||
|
var tileset = new Tileset
|
||||||
|
{
|
||||||
|
Name = "Tileset",
|
||||||
|
TileWidth = 32,
|
||||||
|
TileHeight = 32,
|
||||||
|
TileCount = 1,
|
||||||
|
Columns = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
cache.InsertTileset(path, tileset);
|
||||||
|
var result = cache.GetTileset(path);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result.HasValue);
|
||||||
|
Assert.Same(tileset, result.Value);
|
||||||
|
}
|
||||||
|
}
|
315
src/DotTiled.Tests/UnitTests/Serialization/LoaderTests.cs
Normal file
|
@ -0,0 +1,315 @@
|
||||||
|
using System.Numerics;
|
||||||
|
using DotTiled.Serialization;
|
||||||
|
using NSubstitute;
|
||||||
|
|
||||||
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
|
public class LoaderTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void LoadMap_Always_ReadsFromResourceReader()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceReader = Substitute.For<IResourceReader>();
|
||||||
|
resourceReader.Read("map.tmx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
||||||
|
""");
|
||||||
|
|
||||||
|
var resourceCache = Substitute.For<IResourceCache>();
|
||||||
|
var customTypeDefinitions = Enumerable.Empty<ICustomTypeDefinition>();
|
||||||
|
var loader = new Loader(resourceReader, resourceCache, customTypeDefinitions);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
loader.LoadMap("map.tmx");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
resourceReader.Received(1).Read("map.tmx");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadMap_MapReferencesExternalTileset_ReadsTilesetFromResourceReaderAndAttemptsToRetrieveFromCache()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceReader = Substitute.For<IResourceReader>();
|
||||||
|
resourceReader.Read("map.tmx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<tileset firstgid="1" source="tileset.tsx"/>
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
||||||
|
""");
|
||||||
|
|
||||||
|
resourceReader.Read("tileset.tsx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<tileset version="1.2" tiledversion="1.11.0" name="Tileset" tilewidth="32" tileheight="32" tilecount="1" columns="1">
|
||||||
|
<tile id="1">
|
||||||
|
<image width="32" height="32" source="tile.png"/>
|
||||||
|
</tile>
|
||||||
|
</tileset>
|
||||||
|
""");
|
||||||
|
|
||||||
|
var resourceCache = Substitute.For<IResourceCache>();
|
||||||
|
resourceCache.GetTileset(Arg.Any<string>()).Returns(Optional<Tileset>.Empty);
|
||||||
|
resourceCache.GetTemplate(Arg.Any<string>()).Returns(Optional<Template>.Empty);
|
||||||
|
|
||||||
|
var customTypeDefinitions = Enumerable.Empty<ICustomTypeDefinition>();
|
||||||
|
var loader = new Loader(resourceReader, resourceCache, customTypeDefinitions);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
loader.LoadMap("map.tmx");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
resourceReader.Received(1).Read("tileset.tsx");
|
||||||
|
resourceCache.Received(1).GetTileset("tileset.tsx");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadMap_MapReferencesExternalTemplate_ReadsTemplateFromResourceReaderAndAttemptsToRetrieveFromCache()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceReader = Substitute.For<IResourceReader>();
|
||||||
|
resourceReader.Read("map.tmx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<tileset firstgid="1" source="tileset.tsx"/>
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
<objectgroup id="2" name="Object Layer 1" width="5" height="5">
|
||||||
|
<object id="1" name="Template" template="template.tx" x="0" y="0" width="32" height="32" gid="1"/>
|
||||||
|
</objectgroup>
|
||||||
|
</map>
|
||||||
|
""");
|
||||||
|
|
||||||
|
resourceReader.Read("tileset.tsx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<tileset version="1.2" tiledversion="1.11.0" name="Tileset" tilewidth="32" tileheight="32" tilecount="1" columns="1">
|
||||||
|
<tile id="1">
|
||||||
|
<image width="32" height="32" source="tile.png"/>
|
||||||
|
</tile>
|
||||||
|
</tileset>
|
||||||
|
""");
|
||||||
|
|
||||||
|
resourceReader.Read("template.tx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<template>
|
||||||
|
<object name="Poly">
|
||||||
|
<properties>
|
||||||
|
<property name="templateprop" value="helo there"/>
|
||||||
|
</properties>
|
||||||
|
<polygon points="0,0 104,20 35.6667,32.3333"/>
|
||||||
|
</object>
|
||||||
|
</template>
|
||||||
|
""");
|
||||||
|
|
||||||
|
var resourceCache = Substitute.For<IResourceCache>();
|
||||||
|
resourceCache.GetTileset(Arg.Any<string>()).Returns(Optional<Tileset>.Empty);
|
||||||
|
resourceCache.GetTemplate(Arg.Any<string>()).Returns(Optional<Template>.Empty);
|
||||||
|
|
||||||
|
var customTypeDefinitions = Enumerable.Empty<ICustomTypeDefinition>();
|
||||||
|
var loader = new Loader(resourceReader, resourceCache, customTypeDefinitions);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
loader.LoadMap("map.tmx");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
resourceReader.Received(1).Read("template.tx");
|
||||||
|
resourceCache.Received(1).GetTemplate("template.tx");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadMap_CacheReturnsTileset_ReturnsTilesetFromCache()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceReader = Substitute.For<IResourceReader>();
|
||||||
|
resourceReader.Read("map.tmx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<tileset firstgid="1" source="tileset.tsx"/>
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
||||||
|
""");
|
||||||
|
|
||||||
|
var resourceCache = Substitute.For<IResourceCache>();
|
||||||
|
resourceCache.GetTileset("tileset.tsx").Returns(new Optional<Tileset>(new Tileset { Name = "Tileset", TileWidth = 32, TileHeight = 32, TileCount = 1, Columns = 1 }));
|
||||||
|
|
||||||
|
var customTypeDefinitions = Enumerable.Empty<ICustomTypeDefinition>();
|
||||||
|
var loader = new Loader(resourceReader, resourceCache, customTypeDefinitions);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
loader.LoadMap("map.tmx");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
resourceReader.DidNotReceive().Read("tileset.tsx");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadMap_CacheReturnsTemplate_ReturnsTemplateFromCache()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceReader = Substitute.For<IResourceReader>();
|
||||||
|
resourceReader.Read("map.tmx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<tileset firstgid="1" source="tileset.tsx"/>
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
<objectgroup id="2" name="Object Layer 1" width="5" height="5">
|
||||||
|
<object id="1" name="Template" template="template.tx" x="0" y="0" width="32" height="32" gid="1"/>
|
||||||
|
</objectgroup>
|
||||||
|
</map>
|
||||||
|
""");
|
||||||
|
|
||||||
|
resourceReader.Read("tileset.tsx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<tileset version="1.2" tiledversion="1.11.0" name="Tileset" tilewidth="32" tileheight="32" tilecount="1" columns="1">
|
||||||
|
<tile id="1">
|
||||||
|
<image width="32" height="32" source="tile.png"/>
|
||||||
|
</tile>
|
||||||
|
</tileset>
|
||||||
|
""");
|
||||||
|
|
||||||
|
var resourceCache = Substitute.For<IResourceCache>();
|
||||||
|
resourceCache.GetTileset(Arg.Any<string>()).Returns(Optional<Tileset>.Empty);
|
||||||
|
resourceCache.GetTemplate("template.tx").Returns(new Optional<Template>(new Template
|
||||||
|
{
|
||||||
|
Object = new PolygonObject
|
||||||
|
{
|
||||||
|
Points = [
|
||||||
|
new Vector2(0,0),
|
||||||
|
new Vector2(104,20),
|
||||||
|
new Vector2(35.6667f,32.3333f)
|
||||||
|
],
|
||||||
|
Properties = [
|
||||||
|
new StringProperty { Name = "templateprop", Value = "helo there" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
var customTypeDefinitions = Enumerable.Empty<ICustomTypeDefinition>();
|
||||||
|
var loader = new Loader(resourceReader, resourceCache, customTypeDefinitions);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
loader.LoadMap("map.tmx");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
resourceReader.DidNotReceive().Read("template.tx");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadMap_MapHasClassAndLoaderHasNoCustomTypes_ThrowsException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceReader = Substitute.For<IResourceReader>();
|
||||||
|
resourceReader.Read("map.tmx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" class="TestClass" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
||||||
|
""");
|
||||||
|
|
||||||
|
var resourceCache = Substitute.For<IResourceCache>();
|
||||||
|
var customTypeDefinitions = Enumerable.Empty<ICustomTypeDefinition>();
|
||||||
|
var loader = new Loader(resourceReader, resourceCache, customTypeDefinitions);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<KeyNotFoundException>(() => loader.LoadMap("map.tmx"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadMap_MapHasClassAndLoaderHasCustomTypeWithSameName_ReturnsMapWithPropertiesFromCustomClass()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceReader = Substitute.For<IResourceReader>();
|
||||||
|
resourceReader.Read("map.tmx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" class="TestClass" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
||||||
|
""");
|
||||||
|
var resourceCache = Substitute.For<IResourceCache>();
|
||||||
|
var customClassDefinition = new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = "TestClass",
|
||||||
|
UseAs = CustomClassUseAs.All,
|
||||||
|
Members = [
|
||||||
|
new StringProperty { Name = "Test1", Value = "Hello" },
|
||||||
|
new IntProperty { Name = "Test2", Value = 42 }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
var loader = new Loader(resourceReader, resourceCache, [customClassDefinition]);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = loader.LoadMap("map.tmx");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
DotTiledAssert.AssertProperties(customClassDefinition.Members, result.Properties);
|
||||||
|
}
|
||||||
|
}
|
49
src/DotTiled.Tests/UnitTests/Serialization/TestData.cs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
using System.Xml;
|
||||||
|
|
||||||
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
|
public static partial class TestData
|
||||||
|
{
|
||||||
|
public static XmlReader GetXmlReaderFor(string testDataFile)
|
||||||
|
{
|
||||||
|
var fullyQualifiedTestDataFile = $"DotTiled.Tests.{ConvertPathToAssemblyResourcePath(testDataFile)}";
|
||||||
|
using var stream = typeof(TestData).Assembly.GetManifestResourceStream(fullyQualifiedTestDataFile)
|
||||||
|
?? throw new ArgumentException($"Test data file '{fullyQualifiedTestDataFile}' not found");
|
||||||
|
|
||||||
|
using var stringReader = new StreamReader(stream);
|
||||||
|
var xml = stringReader.ReadToEnd();
|
||||||
|
var xmlStringReader = new StringReader(xml);
|
||||||
|
return XmlReader.Create(xmlStringReader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetRawStringFor(string testDataFile)
|
||||||
|
{
|
||||||
|
var fullyQualifiedTestDataFile = $"DotTiled.Tests.{ConvertPathToAssemblyResourcePath(testDataFile)}";
|
||||||
|
using var stream = typeof(TestData).Assembly.GetManifestResourceStream(fullyQualifiedTestDataFile)
|
||||||
|
?? throw new ArgumentException($"Test data file '{fullyQualifiedTestDataFile}' not found");
|
||||||
|
|
||||||
|
using var stringReader = new StreamReader(stream);
|
||||||
|
return stringReader.ReadToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ConvertPathToAssemblyResourcePath(string path) =>
|
||||||
|
path.Replace("/", ".").Replace("\\", ".").Replace(" ", "_");
|
||||||
|
|
||||||
|
private static string GetMapPath(string mapName) => $"TestData/Maps/{mapName.Replace('-', '_')}/{mapName}";
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> MapTests =>
|
||||||
|
[
|
||||||
|
[GetMapPath("default-map"), (string f) => DefaultMap(), Array.Empty<ICustomTypeDefinition>()],
|
||||||
|
[GetMapPath("map-with-common-props"), (string f) => MapWithCommonProps(), Array.Empty<ICustomTypeDefinition>()],
|
||||||
|
[GetMapPath("map-with-custom-type-props"), (string f) => MapWithCustomTypeProps(), MapWithCustomTypePropsCustomTypeDefinitions()],
|
||||||
|
[GetMapPath("map-with-embedded-tileset"), (string f) => MapWithEmbeddedTileset(), Array.Empty<ICustomTypeDefinition>()],
|
||||||
|
[GetMapPath("map-with-external-tileset"), (string f) => MapWithExternalTileset(f), Array.Empty<ICustomTypeDefinition>()],
|
||||||
|
[GetMapPath("map-with-flippingflags"), (string f) => MapWithFlippingFlags(f), Array.Empty<ICustomTypeDefinition>()],
|
||||||
|
[GetMapPath("map-external-tileset-multi"), (string f) => MapExternalTilesetMulti(f), Array.Empty<ICustomTypeDefinition>()],
|
||||||
|
[GetMapPath("map-external-tileset-wangset"), (string f) => MapExternalTilesetWangset(f), Array.Empty<ICustomTypeDefinition>()],
|
||||||
|
[GetMapPath("map-with-many-layers"), (string f) => MapWithManyLayers(f), Array.Empty<ICustomTypeDefinition>()],
|
||||||
|
[GetMapPath("map-with-deep-props"), (string f) => MapWithDeepProps(), MapWithDeepPropsCustomTypeDefinitions()],
|
||||||
|
[GetMapPath("map-with-class"), (string f) => MapWithClass(), MapWithClassCustomTypeDefinitions()],
|
||||||
|
[GetMapPath("map-with-class-and-props"), (string f) => MapWithClassAndProps(), MapWithClassAndPropsCustomTypeDefinitions()],
|
||||||
|
];
|
||||||
|
}
|
|
@ -37,7 +37,7 @@ public class Color : IParsable<Color>, IEquatable<Color>
|
||||||
/// <param name="provider">An object that supplies culture-specific information about the format of s.</param>
|
/// <param name="provider">An object that supplies culture-specific information about the format of s.</param>
|
||||||
/// <returns>The parsed <see cref="Color"/></returns>
|
/// <returns>The parsed <see cref="Color"/></returns>
|
||||||
/// <exception cref="FormatException">Thrown in case the provided string <paramref name="s"/> is not in a valid format.</exception>
|
/// <exception cref="FormatException">Thrown in case the provided string <paramref name="s"/> is not in a valid format.</exception>
|
||||||
public static Color Parse(string s, IFormatProvider? provider)
|
public static Color Parse(string s, IFormatProvider provider)
|
||||||
{
|
{
|
||||||
_ = TryParse(s, provider, out var result);
|
_ = TryParse(s, provider, out var result);
|
||||||
return result ?? throw new FormatException($"Invalid format for TiledColor: {s}");
|
return result ?? throw new FormatException($"Invalid format for TiledColor: {s}");
|
||||||
|
@ -52,8 +52,8 @@ public class Color : IParsable<Color>, IEquatable<Color>
|
||||||
/// <param name="result">When this method returns, contains the parsed <see cref="Color"/> or <c>null</c> on failure.</param>
|
/// <param name="result">When this method returns, contains the parsed <see cref="Color"/> or <c>null</c> on failure.</param>
|
||||||
/// <returns><c>true</c> if <paramref name="s"/> was successfully parsed; otherwise, <c>false</c>.</returns>
|
/// <returns><c>true</c> if <paramref name="s"/> was successfully parsed; otherwise, <c>false</c>.</returns>
|
||||||
public static bool TryParse(
|
public static bool TryParse(
|
||||||
[NotNullWhen(true)] string? s,
|
[NotNullWhen(true)] string s,
|
||||||
IFormatProvider? provider,
|
IFormatProvider provider,
|
||||||
[MaybeNullWhen(false)] out Color result)
|
[MaybeNullWhen(false)] out Color result)
|
||||||
{
|
{
|
||||||
if (s is not null && !s.StartsWith('#'))
|
if (s is not null && !s.StartsWith('#'))
|
||||||
|
@ -90,7 +90,7 @@ public class Color : IParsable<Color>, IEquatable<Color>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool Equals(Color? other)
|
public bool Equals(Color other)
|
||||||
{
|
{
|
||||||
if (other is null)
|
if (other is null)
|
||||||
return false;
|
return false;
|
||||||
|
@ -99,7 +99,7 @@ public class Color : IParsable<Color>, IEquatable<Color>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override bool Equals(object? obj) => obj is Color other && Equals(other);
|
public override bool Equals(object obj) => obj is Color other && Equals(other);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override int GetHashCode() => HashCode.Combine(R, G, B, A);
|
public override int GetHashCode() => HashCode.Combine(R, G, B, A);
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@ -14,16 +13,16 @@
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<RepositoryUrl>https://github.com/dcronqvist/DotTiled</RepositoryUrl>
|
<RepositoryUrl>https://github.com/dcronqvist/DotTiled</RepositoryUrl>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<PackageTags>gamedev;window;parser;tiled;mapeditor</PackageTags>
|
<PackageTags>gamedev;parser;tiled;mapeditor;tmx;tsx;maps</PackageTags>
|
||||||
<PackageProjectUrl>https://github.com/dcronqvist/DotTiled</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/dcronqvist/DotTiled</PackageProjectUrl>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<Copyright>Copyright © 2024 dcronqvist</Copyright>
|
<Copyright>Copyright © 2024 dcronqvist</Copyright>
|
||||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||||
<Version>0.1.0</Version>
|
<Version>0.2.0</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="../../README.md" Pack="true" PackagePath="" />
|
<None Include="README.md" Pack="true" PackagePath="" />
|
||||||
<None Include="../../LICENSE" Pack="true" PackagePath="" />
|
<None Include="../../LICENSE" Pack="true" PackagePath="" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ public abstract class BaseLayer : HasPropertiesBase
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A tint color that is multiplied with any tiles drawn by this layer.
|
/// A tint color that is multiplied with any tiles drawn by this layer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Color? TintColor { get; set; }
|
public Optional<Color> TintColor { get; set; } = Optional<Color>.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Horizontal offset for this layer in pixels.
|
/// Horizontal offset for this layer in pixels.
|
||||||
|
|
|
@ -118,27 +118,27 @@ public class Data
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The encoding used to encode the tile layer data.
|
/// The encoding used to encode the tile layer data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DataEncoding? Encoding { get; set; }
|
public Optional<DataEncoding> Encoding { get; set; } = Optional<DataEncoding>.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The compression method used to compress the tile layer data.
|
/// The compression method used to compress the tile layer data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DataCompression? Compression { get; set; }
|
public Optional<DataCompression> Compression { get; set; } = Optional<DataCompression>.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The parsed tile layer data, as a list of tile GIDs.
|
/// The parsed tile layer data, as a list of tile GIDs.
|
||||||
/// To get an actual tile ID, you map it to a local tile ID using the correct tileset. Please refer to
|
/// To get an actual tile ID, you map it to a local tile ID using the correct tileset. Please refer to
|
||||||
/// <see href="https://doc.mapeditor.org/en/stable/reference/global-tile-ids/#mapping-a-gid-to-a-local-tile-id">the documentation on how to do this</see>.
|
/// <see href="https://doc.mapeditor.org/en/stable/reference/global-tile-ids/#mapping-a-gid-to-a-local-tile-id">the documentation on how to do this</see>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint[]? GlobalTileIDs { get; set; }
|
public Optional<uint[]> GlobalTileIDs { get; set; } = Optional<uint[]>.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The parsed flipping flags for each tile in the layer. Appear in the same order as the tiles in the layer in <see cref="GlobalTileIDs"/>.
|
/// The parsed flipping flags for each tile in the layer. Appear in the same order as the tiles in the layer in <see cref="GlobalTileIDs"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public FlippingFlags[]? FlippingFlags { get; set; }
|
public Optional<FlippingFlags[]> FlippingFlags { get; set; } = Optional<FlippingFlags[]>.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If the map is infinite, it will instead contain a list of chunks.
|
/// If the map is infinite, it will instead contain a list of chunks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Chunk[]? Chunks { get; set; }
|
public Optional<Chunk[]> Chunks { get; set; } = Optional<Chunk[]>.Empty;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,20 +13,20 @@ public class ImageLayer : BaseLayer
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Y position of the image layer in pixels.
|
/// The Y position of the image layer in pixels.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint Y { get; set; } = 0;
|
public Optional<uint> Y { get; set; } = 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the image drawn by this layer is repeated along the X axis.
|
/// Whether the image drawn by this layer is repeated along the X axis.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool RepeatX { get; set; } = false;
|
public Optional<bool> RepeatX { get; set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the image drawn by this layer is repeated along the Y axis.
|
/// Whether the image drawn by this layer is repeated along the Y axis.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool RepeatY { get; set; } = false;
|
public Optional<bool> RepeatY { get; set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The image to be drawn by this image layer.
|
/// The image to be drawn by this image layer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Image? Image { get; set; }
|
public Optional<Image> Image { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,17 +36,17 @@ public class ObjectLayer : BaseLayer
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The width of the object layer in tiles. Meaningless.
|
/// The width of the object layer in tiles. Meaningless.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint? Width { get; set; }
|
public uint Width { get; set; } = 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The height of the object layer in tiles. Meaningless.
|
/// The height of the object layer in tiles. Meaningless.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint? Height { get; set; }
|
public uint Height { get; set; } = 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A color that is multiplied with any tile objects drawn by this layer.
|
/// A color that is multiplied with any tile objects drawn by this layer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Color? Color { get; set; }
|
public Optional<Color> Color { get; set; } = Optional<Color>.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the objects are drawn according to the order of appearance (<see cref="DrawOrder.Index"/>) or sorted by their Y coordinate (<see cref="DrawOrder.TopDown"/>).
|
/// Whether the objects are drawn according to the order of appearance (<see cref="DrawOrder.Index"/>) or sorted by their Y coordinate (<see cref="DrawOrder.TopDown"/>).
|
||||||
|
|
|
@ -10,7 +10,7 @@ public abstract class Object : HasPropertiesBase
|
||||||
/// <summary>
|
/// <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.
|
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint? ID { get; set; }
|
public Optional<uint> ID { get; set; } = Optional<uint>.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the object. An arbitrary string.
|
/// The name of the object. An arbitrary string.
|
||||||
|
@ -55,7 +55,7 @@ public abstract class Object : HasPropertiesBase
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A reference to a template file.
|
/// A reference to a template file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Template { get; set; }
|
public Optional<string> Template { get; set; } = Optional<string>.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Object properties.
|
/// Object properties.
|
||||||
|
|
|
@ -28,5 +28,5 @@ public class TileLayer : BaseLayer
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The tile layer data.
|
/// The tile layer data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Data? Data { get; set; }
|
public Optional<Data> Data { get; set; } = Optional<Data>.Empty;
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ public class Map : HasPropertiesBase
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Tiled version used to save the file.
|
/// The Tiled version used to save the file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public required string TiledVersion { get; set; }
|
public Optional<string> TiledVersion { get; set; } = Optional<string>.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The class of this map.
|
/// The class of this map.
|
||||||
|
@ -146,17 +146,17 @@ public class Map : HasPropertiesBase
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Only for hexagonal maps. Determines the width or height (depending on the staggered axis) of the tile's edge, in pixels.
|
/// Only for hexagonal maps. Determines the width or height (depending on the staggered axis) of the tile's edge, in pixels.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint? HexSideLength { get; set; }
|
public Optional<uint> HexSideLength { get; set; } = Optional<uint>.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// For staggered and hexagonal maps, determines which axis (X or Y) is staggered.
|
/// For staggered and hexagonal maps, determines which axis (X or Y) is staggered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public StaggerAxis? StaggerAxis { get; set; }
|
public Optional<StaggerAxis> StaggerAxis { get; set; } = Optional<StaggerAxis>.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// For staggered and hexagonal maps, determines whether the "even" or "odd" indexes along the staggered axis are shifted.
|
/// For staggered and hexagonal maps, determines whether the "even" or "odd" indexes along the staggered axis are shifted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public StaggerIndex? StaggerIndex { get; set; }
|
public Optional<StaggerIndex> StaggerIndex { get; set; } = Optional<StaggerIndex>.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// X coordinate of the parallax origin in pixels.
|
/// X coordinate of the parallax origin in pixels.
|
||||||
|
|
137
src/DotTiled/Optional.cs
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace DotTiled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a value that may or may not be present.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the optionally present value.</typeparam>
|
||||||
|
public class Optional<T>
|
||||||
|
{
|
||||||
|
private readonly T _value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the current <see cref="Optional{T}"/> object has a value.
|
||||||
|
/// </summary>
|
||||||
|
public bool HasValue { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value of the current <see cref="Optional{T}"/> object if it has been set; otherwise, throws an exception.
|
||||||
|
/// </summary>
|
||||||
|
public T Value => HasValue ? _value : throw new InvalidOperationException("Value is not set");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Optional{T}"/> class with the specified value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to be set.</param>
|
||||||
|
public Optional(T value)
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
HasValue = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Optional{T}"/> class with no value.
|
||||||
|
/// </summary>
|
||||||
|
public Optional()
|
||||||
|
{
|
||||||
|
_value = default!;
|
||||||
|
HasValue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implicitly converts a value to an <see cref="Optional{T}"/> object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to be converted.</param>
|
||||||
|
public static implicit operator Optional<T>(T value)
|
||||||
|
{
|
||||||
|
if (value is null)
|
||||||
|
return new();
|
||||||
|
|
||||||
|
return new(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implicitly converts an <see cref="Optional{T}"/> object to a value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="optional">The <see cref="Optional{T}"/> object to be converted.</param>
|
||||||
|
public static implicit operator T(Optional<T> optional)
|
||||||
|
{
|
||||||
|
return optional.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified <see cref="Optional{T}"/> objects are equal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left"></param>
|
||||||
|
/// <param name="right"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool operator ==(Optional<T> left, Optional<T> right)
|
||||||
|
{
|
||||||
|
return left.Equals(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified <see cref="Optional{T}"/> objects are not equal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left"></param>
|
||||||
|
/// <param name="right"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool operator !=(Optional<T> left, Optional<T> right)
|
||||||
|
{
|
||||||
|
return !left.Equals(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the value of the current <see cref="Optional{T}"/> object if it has been set; otherwise, returns the specified default value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="defaultValue">The value to be returned if the current <see cref="Optional{T}"/> object has no value.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public T GetValueOr(T defaultValue) => HasValue ? _value : defaultValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the current <see cref="Optional{T}"/> object if it has a value; otherwise, returns the specified default value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="defaultValue">The <see cref="Optional{T}"/> object to be returned if the current <see cref="Optional{T}"/> object has no value.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Optional<T> GetValueOrOptional(Optional<T> defaultValue) => HasValue ? this : defaultValue;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString() => HasValue ? _value.ToString() : "Empty";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (obj is null)
|
||||||
|
{
|
||||||
|
return !HasValue;
|
||||||
|
}
|
||||||
|
else if (obj.GetType() == typeof(T))
|
||||||
|
{
|
||||||
|
return HasValue && _value.Equals(obj);
|
||||||
|
}
|
||||||
|
else if (obj is Optional<T> opt)
|
||||||
|
{
|
||||||
|
if (HasValue && opt.HasValue)
|
||||||
|
{
|
||||||
|
return Equals(opt.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return !HasValue && !opt.HasValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override int GetHashCode() => HasValue ? _value!.GetHashCode() : 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an empty <see cref="Optional{T}"/> object.
|
||||||
|
/// </summary>
|
||||||
|
#pragma warning disable CA1000 // Do not declare static members on generic types
|
||||||
|
public static Optional<T> Empty => new();
|
||||||
|
#pragma warning restore CA1000 // Do not declare static members on generic types
|
||||||
|
}
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace DotTiled;
|
namespace DotTiled;
|
||||||
|
@ -8,7 +6,7 @@ namespace DotTiled;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a class property.
|
/// Represents a class property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ClassProperty : IHasProperties, IProperty<IList<IProperty>>
|
public class ClassProperty : HasPropertiesBase, IProperty<IList<IProperty>>
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
|
@ -36,30 +34,5 @@ public class ClassProperty : IHasProperties, IProperty<IList<IProperty>>
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IList<IProperty> GetProperties() => Value;
|
public override IList<IProperty> GetProperties() => Value;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public T GetProperty<T>(string name) where T : IProperty
|
|
||||||
{
|
|
||||||
var property = Value.FirstOrDefault(_properties => _properties.Name == name) ?? throw new InvalidOperationException($"Property '{name}' not found.");
|
|
||||||
if (property is T prop)
|
|
||||||
{
|
|
||||||
return prop;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new InvalidOperationException($"Property '{name}' is not of type '{typeof(T).Name}'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace DotTiled;
|
namespace DotTiled;
|
||||||
|
|
||||||
|
@ -76,7 +78,7 @@ public class CustomClassDefinition : HasPropertiesBase, ICustomTypeDefinition
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The color of the custom class inside the Tiled editor.
|
/// The color of the custom class inside the Tiled editor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Color? Color { get; set; }
|
public Color Color { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the custom class should be drawn with a fill color.
|
/// Whether the custom class should be drawn with a fill color.
|
||||||
|
@ -95,4 +97,95 @@ public class CustomClassDefinition : HasPropertiesBase, ICustomTypeDefinition
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override IList<IProperty> GetProperties() => Members;
|
public override IList<IProperty> GetProperties() => Members;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="CustomClassDefinition"/> from the specified class type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The type of the class to create a custom class definition from.</param>
|
||||||
|
/// <returns>A new <see cref="CustomClassDefinition"/> instance.</returns>
|
||||||
|
/// <exception cref="ArgumentException">Thrown when the specified type is not a class.</exception>
|
||||||
|
public static CustomClassDefinition FromClass(Type type)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(type, nameof(type));
|
||||||
|
|
||||||
|
if (type == typeof(string) || !type.IsClass)
|
||||||
|
throw new ArgumentException("Type must be a class.", nameof(type));
|
||||||
|
|
||||||
|
var instance = Activator.CreateInstance(type);
|
||||||
|
var properties = type.GetProperties();
|
||||||
|
|
||||||
|
return new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = type.Name,
|
||||||
|
UseAs = CustomClassUseAs.All,
|
||||||
|
Members = properties.Select(p => ConvertPropertyInfoToIProperty(instance, p)).ToList()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="CustomClassDefinition"/> from the specified constructible class type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the class to create a custom class definition from.</typeparam>
|
||||||
|
/// <returns>A new <see cref="CustomClassDefinition"/> instance.</returns>
|
||||||
|
public static CustomClassDefinition FromClass<T>() where T : class, new() => FromClass(() => new T());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="CustomClassDefinition"/> from the specified factory function of a class instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the class to create a custom class definition from.</typeparam>
|
||||||
|
/// <param name="factory">The factory function that creates an instance of the class.</param>
|
||||||
|
/// <returns>A new <see cref="CustomClassDefinition"/> instance.</returns>
|
||||||
|
public static CustomClassDefinition FromClass<T>(Func<T> factory) where T : class
|
||||||
|
{
|
||||||
|
var instance = factory();
|
||||||
|
var type = typeof(T);
|
||||||
|
var properties = type.GetProperties();
|
||||||
|
|
||||||
|
return new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = type.Name,
|
||||||
|
UseAs = CustomClassUseAs.All,
|
||||||
|
Members = properties.Select(p => ConvertPropertyInfoToIProperty(instance, p)).ToList()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IProperty ConvertPropertyInfoToIProperty(object instance, PropertyInfo propertyInfo)
|
||||||
|
{
|
||||||
|
switch (propertyInfo.PropertyType)
|
||||||
|
{
|
||||||
|
case Type t when t == typeof(bool):
|
||||||
|
return new BoolProperty { Name = propertyInfo.Name, Value = (bool)propertyInfo.GetValue(instance) };
|
||||||
|
case Type t when t == typeof(Color):
|
||||||
|
return new ColorProperty { Name = propertyInfo.Name, Value = (Color)propertyInfo.GetValue(instance) };
|
||||||
|
case Type t when t == typeof(float):
|
||||||
|
return new FloatProperty { Name = propertyInfo.Name, Value = (float)propertyInfo.GetValue(instance) };
|
||||||
|
case Type t when t == typeof(string):
|
||||||
|
return new StringProperty { Name = propertyInfo.Name, Value = (string)propertyInfo.GetValue(instance) };
|
||||||
|
case Type t when t == typeof(int):
|
||||||
|
return new IntProperty { Name = propertyInfo.Name, Value = (int)propertyInfo.GetValue(instance) };
|
||||||
|
case Type t when t.IsClass:
|
||||||
|
return new ClassProperty { Name = propertyInfo.Name, PropertyType = t.Name, Value = GetNestedProperties(propertyInfo.PropertyType, propertyInfo.GetValue(instance)) };
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotSupportedException($"Type '{propertyInfo.PropertyType.Name}' is not supported in custom classes.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<IProperty> GetNestedProperties(Type type, object instance)
|
||||||
|
{
|
||||||
|
var defaultInstance = Activator.CreateInstance(type);
|
||||||
|
var properties = type.GetProperties();
|
||||||
|
|
||||||
|
bool IsPropertyDefaultValue(PropertyInfo propertyInfo)
|
||||||
|
{
|
||||||
|
var defaultValue = propertyInfo.GetValue(defaultInstance);
|
||||||
|
var value = propertyInfo.GetValue(instance);
|
||||||
|
return value.Equals(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return properties
|
||||||
|
.Where(p => !IsPropertyDefaultValue(p))
|
||||||
|
.Select(p => ConvertPropertyInfoToIProperty(instance, p)).ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace DotTiled;
|
namespace DotTiled;
|
||||||
|
|
||||||
|
@ -44,4 +46,43 @@ public class CustomEnumDefinition : ICustomTypeDefinition
|
||||||
/// Whether the value should be treated as flags.
|
/// Whether the value should be treated as flags.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool ValueAsFlags { get; set; }
|
public bool ValueAsFlags { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a custom enum definition from the specified enum type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static CustomEnumDefinition FromEnum<T>() where T : Enum
|
||||||
|
{
|
||||||
|
var type = typeof(T);
|
||||||
|
var isFlags = type.GetCustomAttributes(typeof(FlagsAttribute), false).Length != 0;
|
||||||
|
|
||||||
|
return new CustomEnumDefinition
|
||||||
|
{
|
||||||
|
Name = type.Name,
|
||||||
|
StorageType = CustomEnumStorageType.Int,
|
||||||
|
Values = Enum.GetNames(type).ToList(),
|
||||||
|
ValueAsFlags = isFlags
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a custom enum definition from the specified enum type.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static CustomEnumDefinition FromEnum(Type type)
|
||||||
|
{
|
||||||
|
if (!type.IsEnum)
|
||||||
|
throw new ArgumentException("Type must be an enum.", nameof(type));
|
||||||
|
|
||||||
|
var isFlags = type.GetCustomAttributes(typeof(FlagsAttribute), false).Length != 0;
|
||||||
|
|
||||||
|
return new CustomEnumDefinition
|
||||||
|
{
|
||||||
|
Name = type.Name,
|
||||||
|
StorageType = CustomEnumStorageType.Int,
|
||||||
|
Values = Enum.GetNames(type).ToList(),
|
||||||
|
ValueAsFlags = isFlags
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|