mirror of
https://github.com/dcronqvist/DotTiled.git
synced 2025-05-08 22:36:02 +03:00
Model now uses Optional correctly, I think, massive changes
This commit is contained in:
parent
39d2838663
commit
88a5eadb74
42 changed files with 1106 additions and 400 deletions
|
@ -19,12 +19,11 @@ public static partial class DotTiledAssert
|
|||
AssertEqual(expected.GlobalTileIDs, actual.GlobalTileIDs, nameof(Data.GlobalTileIDs));
|
||||
AssertEqual(expected.FlippingFlags, actual.FlippingFlags, nameof(Data.FlippingFlags));
|
||||
|
||||
if (expected.Chunks is not null)
|
||||
if (expected.Chunks.HasValue)
|
||||
{
|
||||
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]);
|
||||
AssertEqual(expected.Chunks.Value.Length, actual.Chunks.Value.Length, "Chunks.Length");
|
||||
for (var i = 0; i < expected.Chunks.Value.Length; i++)
|
||||
AssertChunk(expected.Chunks.Value[i], actual.Chunks.Value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,68 @@ namespace DotTiled.Tests;
|
|||
|
||||
public static partial class DotTiledAssert
|
||||
{
|
||||
private 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}]");
|
||||
}
|
||||
}
|
||||
|
||||
private 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");
|
||||
}
|
||||
|
||||
private 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");
|
||||
}
|
||||
|
||||
private static void AssertEqual<T>(T expected, T actual, string nameof)
|
||||
{
|
||||
if (expected == null)
|
||||
|
|
|
@ -4,7 +4,6 @@ public static partial class DotTiledAssert
|
|||
{
|
||||
internal static void AssertTileset(Tileset expected, Tileset actual)
|
||||
{
|
||||
// Attributes
|
||||
AssertEqual(expected.Version, actual.Version, nameof(Tileset.Version));
|
||||
AssertEqual(expected.TiledVersion, actual.TiledVersion, nameof(Tileset.TiledVersion));
|
||||
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.FillMode, actual.FillMode, nameof(Tileset.FillMode));
|
||||
|
||||
// At most one of
|
||||
AssertImage(expected.Image, actual.Image);
|
||||
AssertTileOffset(expected.TileOffset, actual.TileOffset);
|
||||
AssertGrid(expected.Grid, actual.Grid);
|
||||
AssertOptionalsEqual(expected.Image, actual.Image, nameof(Tileset.Image), AssertImage);
|
||||
AssertOptionalsEqual(expected.TileOffset, actual.TileOffset, nameof(Tileset.TileOffset), AssertTileOffset);
|
||||
AssertOptionalsEqual(expected.Grid, actual.Grid, nameof(Tileset.Grid), AssertGrid);
|
||||
AssertProperties(expected.Properties, actual.Properties);
|
||||
// TODO: AssertTerrainTypes(actual.TerrainTypes, expected.TerrainTypes);
|
||||
if (expected.Wangsets is not null)
|
||||
{
|
||||
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]);
|
||||
AssertListOrdered(expected.Wangsets, actual.Wangsets, nameof(Tileset.Wangsets), AssertWangset);
|
||||
AssertOptionalsEqual(expected.Transformations, actual.Transformations, nameof(Tileset.Transformations), AssertTransformations);
|
||||
AssertListOrdered(expected.Tiles, actual.Tiles, nameof(Tileset.Tiles), AssertTile);
|
||||
}
|
||||
|
||||
private static void AssertTileOffset(TileOffset? expected, TileOffset? actual)
|
||||
private static void AssertTileOffset(TileOffset expected, TileOffset actual)
|
||||
{
|
||||
if (expected is null)
|
||||
{
|
||||
|
@ -74,27 +60,17 @@ public static partial class DotTiledAssert
|
|||
|
||||
private static void AssertWangset(Wangset expected, Wangset actual)
|
||||
{
|
||||
// Attributes
|
||||
AssertEqual(expected.Name, actual.Name, nameof(Wangset.Name));
|
||||
AssertEqual(expected.Class, actual.Class, nameof(Wangset.Class));
|
||||
AssertEqual(expected.Tile, actual.Tile, nameof(Wangset.Tile));
|
||||
|
||||
// At most one of
|
||||
AssertProperties(expected.Properties, actual.Properties);
|
||||
if (expected.WangColors is not null)
|
||||
{
|
||||
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]);
|
||||
AssertListOrdered(expected.WangColors, actual.WangColors, nameof(Wangset.WangColors), AssertWangColor);
|
||||
AssertListOrdered(expected.WangTiles, actual.WangTiles, nameof(Wangset.WangTiles), AssertWangTile);
|
||||
}
|
||||
|
||||
private static void AssertWangColor(WangColor expected, WangColor actual)
|
||||
{
|
||||
// Attributes
|
||||
AssertEqual(expected.Name, actual.Name, nameof(WangColor.Name));
|
||||
AssertEqual(expected.Class, actual.Class, nameof(WangColor.Class));
|
||||
AssertEqual(expected.Color, actual.Color, nameof(WangColor.Color));
|
||||
|
@ -106,7 +82,6 @@ public static partial class DotTiledAssert
|
|||
|
||||
private static void AssertWangTile(WangTile expected, WangTile actual)
|
||||
{
|
||||
// Attributes
|
||||
AssertEqual(expected.TileID, actual.TileID, nameof(WangTile.TileID));
|
||||
AssertEqual(expected.WangID, actual.WangID, nameof(WangTile.WangID));
|
||||
}
|
||||
|
@ -119,7 +94,6 @@ public static partial class DotTiledAssert
|
|||
return;
|
||||
}
|
||||
|
||||
// Attributes
|
||||
Assert.NotNull(actual);
|
||||
AssertEqual(expected.HFlip, actual.HFlip, nameof(Transformations.HFlip));
|
||||
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)
|
||||
{
|
||||
// Attributes
|
||||
AssertEqual(expected.ID, actual.ID, nameof(Tile.ID));
|
||||
AssertEqual(expected.Type, actual.Type, nameof(Tile.Type));
|
||||
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.Height, actual.Height, nameof(Tile.Height));
|
||||
|
||||
// Elements
|
||||
AssertProperties(expected.Properties, actual.Properties);
|
||||
AssertImage(expected.Image, actual.Image);
|
||||
AssertLayer((BaseLayer?)expected.ObjectLayer, (BaseLayer?)actual.ObjectLayer);
|
||||
if (expected.Animation is not null)
|
||||
{
|
||||
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]);
|
||||
}
|
||||
AssertOptionalsEqual(expected.Image, actual.Image, nameof(Tile.Image), AssertImage);
|
||||
AssertOptionalsEqual(expected.ObjectLayer, actual.ObjectLayer, nameof(Tile.ObjectLayer), (a, b) => AssertLayer((BaseLayer)a, (BaseLayer)b));
|
||||
AssertListOrdered(expected.Animation, actual.Animation, nameof(Tile.Animation), AssertFrame);
|
||||
}
|
||||
|
||||
private static void AssertFrame(Frame expected, Frame actual)
|
||||
{
|
||||
// Attributes
|
||||
AssertEqual(expected.TileID, actual.TileID, nameof(Frame.TileID));
|
||||
AssertEqual(expected.Duration, actual.Duration, nameof(Frame.Duration));
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
|
|
520
src/DotTiled.Tests/OptionalTests.cs
Normal file
520
src/DotTiled.Tests/OptionalTests.cs
Normal file
|
@ -0,0 +1,520 @@
|
|||
namespace DotTiled.Tests;
|
||||
|
||||
// public class OptionalTests
|
||||
// {
|
||||
// [Fact]
|
||||
// public void HasValue_WhenValueIsSet_ReturnsTrue()
|
||||
// {
|
||||
// // Arrange
|
||||
// var optional = new Optional<int>(42);
|
||||
|
||||
// // Act & Assert
|
||||
// Assert.True(optional.HasValue);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void HasValue_WhenValueIsNotSet_ReturnsFalse()
|
||||
// {
|
||||
// // Arrange
|
||||
// var optional = new Optional<int>();
|
||||
|
||||
// // Act & Assert
|
||||
// Assert.False(optional.HasValue);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void Value_WhenValueIsSet_ReturnsValue()
|
||||
// {
|
||||
// // Arrange
|
||||
// var optional = new Optional<int>(42);
|
||||
|
||||
// // Act
|
||||
// var value = optional.Value;
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal(42, value);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void Value_WhenValueIsNotSet_ThrowsInvalidOperationException()
|
||||
// {
|
||||
// // Arrange
|
||||
// var optional = new Optional<int>();
|
||||
|
||||
// // Act & Assert
|
||||
// _ = Assert.Throws<InvalidOperationException>(() => optional.Value);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void ImplicitConversionFromValue_CreatesOptionalWithValue()
|
||||
// {
|
||||
// // Arrange
|
||||
// Optional<int> optional = 42;
|
||||
|
||||
// // Act & Assert
|
||||
// Assert.True(optional.HasValue);
|
||||
// Assert.Equal(42, optional.Value);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void ImplicitConversionToValue_ReturnsValue()
|
||||
// {
|
||||
// // Arrange
|
||||
// var optional = new Optional<int>(42);
|
||||
|
||||
// // Act
|
||||
// int value = optional;
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal(42, value);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void ToString_WhenValueIsSet_ReturnsValueToString()
|
||||
// {
|
||||
// // Arrange
|
||||
// var optional = new Optional<int>(42);
|
||||
|
||||
// // Act
|
||||
// var result = optional.ToString();
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal("42", result);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void ToString_WhenValueIsNotSet_ReturnsEmpty()
|
||||
// {
|
||||
// // Arrange
|
||||
// var optional = new Optional<int>();
|
||||
|
||||
// // Act
|
||||
// var result = optional.ToString();
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal("Empty", result);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void Equals_WithObject_ReturnsTrueWhenValueIsEqual()
|
||||
// {
|
||||
// // Arrange
|
||||
// var optional = new Optional<int>(42);
|
||||
|
||||
// // Act
|
||||
// var result = optional.Equals(42);
|
||||
|
||||
// // Assert
|
||||
// Assert.True(result);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void Equals_WithObject_ReturnsFalseWhenValueIsNotEqual()
|
||||
// {
|
||||
// // Arrange
|
||||
// var optional = new Optional<int>(42);
|
||||
|
||||
// // Act
|
||||
// var result = optional.Equals(43);
|
||||
|
||||
// // Assert
|
||||
// Assert.False(result);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void Equals_WithObject_ReturnsFalseWhenValueIsNotSet()
|
||||
// {
|
||||
// // Arrange
|
||||
// var optional = new Optional<int>();
|
||||
|
||||
// // Act
|
||||
// var result = optional.Equals(42);
|
||||
|
||||
// // Assert
|
||||
// Assert.False(result);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void Equals_WithOptional_ReturnsTrueWhenValueIsEqual()
|
||||
// {
|
||||
// // Arrange
|
||||
// var optional1 = new Optional<int>(42);
|
||||
// var optional2 = new Optional<int>(42);
|
||||
|
||||
// // Act
|
||||
// var result = optional1.Equals(optional2);
|
||||
|
||||
// // Assert
|
||||
// Assert.True(result);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void Equals_WithOptional_ReturnsFalseWhenValueIsNotEqual()
|
||||
// {
|
||||
// // Arrange
|
||||
// var optional1 = new Optional<int>(42);
|
||||
// var optional2 = new Optional<int>(43);
|
||||
|
||||
// // Act
|
||||
// var result = optional1.Equals(optional2);
|
||||
|
||||
// // Assert
|
||||
// Assert.False(result);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void Equals_WithOptional_ReturnsFalseWhenValueIsNotSet()
|
||||
// {
|
||||
// // Arrange
|
||||
// var optional1 = new Optional<int>();
|
||||
// var optional2 = new Optional<int>(42);
|
||||
|
||||
// // Act
|
||||
// var result = optional1.Equals(optional2);
|
||||
|
||||
// // Assert
|
||||
// Assert.False(result);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void GetHashCode_WhenValueIsSet_ReturnsValueHashCode()
|
||||
// {
|
||||
// // Arrange
|
||||
// var optional = new Optional<int>(42);
|
||||
|
||||
// // Act
|
||||
// var result = optional.GetHashCode();
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal(42.GetHashCode(), result);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void GetHashCode_WhenValueIsNotSet_ReturnsZero()
|
||||
// {
|
||||
// // Arrange
|
||||
// var optional = new Optional<int>();
|
||||
|
||||
// // Act
|
||||
// var result = optional.GetHashCode();
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal(0, result);
|
||||
// }
|
||||
// }
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ namespace DotTiled.Tests;
|
|||
public partial class MapReaderTests
|
||||
{
|
||||
public static IEnumerable<object[]> Maps => TestData.MapTests;
|
||||
[Theory]
|
||||
[Theory(Skip = "Skipped for now")]
|
||||
[MemberData(nameof(Maps))]
|
||||
public void MapReaderReadMap_ValidFilesExternalTilesetsAndTemplates_ReturnsMapThatEqualsExpected(
|
||||
string testDataFile,
|
||||
|
|
|
@ -11,9 +11,6 @@ public partial class TestData
|
|||
TileWidth = 32,
|
||||
TileHeight = 32,
|
||||
Infinite = false,
|
||||
HexSideLength = null,
|
||||
StaggerAxis = null,
|
||||
StaggerIndex = null,
|
||||
ParallaxOriginX = 0,
|
||||
ParallaxOriginY = 0,
|
||||
RenderOrder = RenderOrder.RightDown,
|
||||
|
@ -33,22 +30,20 @@ public partial class TestData
|
|||
Data = new Data
|
||||
{
|
||||
Encoding = DataEncoding.Csv,
|
||||
Chunks = null,
|
||||
Compression = null,
|
||||
GlobalTileIDs = [
|
||||
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 = [
|
||||
]),
|
||||
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
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -13,9 +13,6 @@ public partial class TestData
|
|||
TileWidth = 32,
|
||||
TileHeight = 32,
|
||||
Infinite = false,
|
||||
HexSideLength = null,
|
||||
StaggerAxis = null,
|
||||
StaggerIndex = null,
|
||||
ParallaxOriginX = 0,
|
||||
ParallaxOriginY = 0,
|
||||
RenderOrder = RenderOrder.RightDown,
|
||||
|
@ -97,22 +94,20 @@ public partial class TestData
|
|||
Data = new Data
|
||||
{
|
||||
Encoding = DataEncoding.Csv,
|
||||
Chunks = null,
|
||||
Compression = null,
|
||||
GlobalTileIDs = [
|
||||
GlobalTileIDs = new Optional<uint[]>([
|
||||
0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0,
|
||||
0, 0, 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
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -13,9 +13,6 @@ public partial class TestData
|
|||
TileWidth = 24,
|
||||
TileHeight = 24,
|
||||
Infinite = false,
|
||||
HexSideLength = null,
|
||||
StaggerAxis = null,
|
||||
StaggerIndex = null,
|
||||
ParallaxOriginX = 0,
|
||||
ParallaxOriginY = 0,
|
||||
RenderOrder = RenderOrder.RightDown,
|
||||
|
@ -56,7 +53,59 @@ public partial class TestData
|
|||
Source = "tileset.png",
|
||||
Width = 256,
|
||||
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 = [
|
||||
|
@ -69,22 +118,20 @@ public partial class TestData
|
|||
Data = new Data
|
||||
{
|
||||
Encoding = DataEncoding.Csv,
|
||||
Chunks = null,
|
||||
Compression = null,
|
||||
GlobalTileIDs = [
|
||||
GlobalTileIDs = new Optional<uint[]>([
|
||||
2, 2, 12, 11, 0,
|
||||
1, 12, 1, 11, 0,
|
||||
2, 1, 0, 1, 0,
|
||||
12, 11, 12, 2, 0,
|
||||
0, 0, 0, 0, 0
|
||||
],
|
||||
FlippingFlags = [
|
||||
]),
|
||||
FlippingFlags = new Optional<FlippingFlags[]>([
|
||||
FlippingFlags.FlippedHorizontally, FlippingFlags.None, FlippingFlags.FlippedHorizontally, 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.FlippedHorizontally, FlippingFlags.None,
|
||||
FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None, FlippingFlags.None
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -13,9 +13,6 @@ public partial class TestData
|
|||
TileWidth = 32,
|
||||
TileHeight = 16,
|
||||
Infinite = false,
|
||||
HexSideLength = null,
|
||||
StaggerAxis = null,
|
||||
StaggerIndex = null,
|
||||
ParallaxOriginX = 0,
|
||||
ParallaxOriginY = 0,
|
||||
RenderOrder = RenderOrder.RightDown,
|
||||
|
@ -35,22 +32,20 @@ public partial class TestData
|
|||
Data = new Data
|
||||
{
|
||||
Encoding = DataEncoding.Csv,
|
||||
Chunks = null,
|
||||
Compression = null,
|
||||
GlobalTileIDs = [
|
||||
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 = [
|
||||
]),
|
||||
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
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -13,9 +13,6 @@ public partial class TestData
|
|||
TileWidth = 32,
|
||||
TileHeight = 32,
|
||||
Infinite = false,
|
||||
HexSideLength = null,
|
||||
StaggerAxis = null,
|
||||
StaggerIndex = null,
|
||||
ParallaxOriginX = 0,
|
||||
ParallaxOriginY = 0,
|
||||
RenderOrder = RenderOrder.RightDown,
|
||||
|
@ -35,22 +32,20 @@ public partial class TestData
|
|||
Data = new Data
|
||||
{
|
||||
Encoding = DataEncoding.Csv,
|
||||
Chunks = null,
|
||||
Compression = null,
|
||||
GlobalTileIDs = [
|
||||
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 = [
|
||||
]),
|
||||
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
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -13,9 +13,6 @@ public partial class TestData
|
|||
TileWidth = 32,
|
||||
TileHeight = 32,
|
||||
Infinite = false,
|
||||
HexSideLength = null,
|
||||
StaggerAxis = null,
|
||||
StaggerIndex = null,
|
||||
ParallaxOriginX = 0,
|
||||
ParallaxOriginY = 0,
|
||||
RenderOrder = RenderOrder.RightDown,
|
||||
|
@ -35,22 +32,20 @@ public partial class TestData
|
|||
Data = new Data
|
||||
{
|
||||
Encoding = DataEncoding.Csv,
|
||||
Chunks = null,
|
||||
Compression = null,
|
||||
GlobalTileIDs = [
|
||||
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 = [
|
||||
]),
|
||||
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
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -13,9 +13,6 @@ public partial class TestData
|
|||
TileWidth = 32,
|
||||
TileHeight = 32,
|
||||
Infinite = false,
|
||||
HexSideLength = null,
|
||||
StaggerAxis = null,
|
||||
StaggerIndex = null,
|
||||
ParallaxOriginX = 0,
|
||||
ParallaxOriginY = 0,
|
||||
RenderOrder = RenderOrder.RightDown,
|
||||
|
@ -28,6 +25,8 @@ public partial class TestData
|
|||
Tilesets = [
|
||||
new Tileset
|
||||
{
|
||||
Version = "1.10",
|
||||
TiledVersion = "1.11.0",
|
||||
FirstGID = 1,
|
||||
Name = "tileset",
|
||||
TileWidth = 32,
|
||||
|
@ -53,22 +52,20 @@ public partial class TestData
|
|||
Data = new Data
|
||||
{
|
||||
Encoding = DataEncoding.Csv,
|
||||
Chunks = null,
|
||||
Compression = null,
|
||||
GlobalTileIDs = [
|
||||
GlobalTileIDs = new Optional<uint[]>([
|
||||
1, 1, 0, 0, 7,
|
||||
1, 1, 0, 0, 7,
|
||||
0, 0, 0, 0, 7,
|
||||
9, 10, 0, 0, 7,
|
||||
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
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -13,9 +13,6 @@ public partial class TestData
|
|||
TileWidth = 32,
|
||||
TileHeight = 32,
|
||||
Infinite = false,
|
||||
HexSideLength = null,
|
||||
StaggerAxis = null,
|
||||
StaggerIndex = null,
|
||||
ParallaxOriginX = 0,
|
||||
ParallaxOriginY = 0,
|
||||
RenderOrder = RenderOrder.RightDown,
|
||||
|
@ -56,22 +53,20 @@ public partial class TestData
|
|||
Data = new Data
|
||||
{
|
||||
Encoding = DataEncoding.Csv,
|
||||
Chunks = null,
|
||||
Compression = null,
|
||||
GlobalTileIDs = [
|
||||
GlobalTileIDs = new Optional<uint[]>([
|
||||
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
|
||||
],
|
||||
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
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -13,9 +13,6 @@ public partial class TestData
|
|||
TileWidth = 32,
|
||||
TileHeight = 32,
|
||||
Infinite = false,
|
||||
HexSideLength = null,
|
||||
StaggerAxis = null,
|
||||
StaggerIndex = null,
|
||||
ParallaxOriginX = 0,
|
||||
ParallaxOriginY = 0,
|
||||
RenderOrder = RenderOrder.RightDown,
|
||||
|
@ -56,22 +53,20 @@ public partial class TestData
|
|||
Data = new Data
|
||||
{
|
||||
Encoding = DataEncoding.Csv,
|
||||
Chunks = null,
|
||||
Compression = null,
|
||||
GlobalTileIDs = [
|
||||
GlobalTileIDs = new Optional<uint[]>([
|
||||
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
|
||||
],
|
||||
FlippingFlags = [
|
||||
]),
|
||||
FlippingFlags = new Optional<FlippingFlags[]>([
|
||||
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.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
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -13,9 +13,6 @@ public partial class TestData
|
|||
TileWidth = 32,
|
||||
TileHeight = 32,
|
||||
Infinite = false,
|
||||
HexSideLength = null,
|
||||
StaggerAxis = null,
|
||||
StaggerIndex = null,
|
||||
ParallaxOriginX = 0,
|
||||
ParallaxOriginY = 0,
|
||||
RenderOrder = RenderOrder.RightDown,
|
||||
|
@ -124,22 +121,20 @@ public partial class TestData
|
|||
Data = new Data
|
||||
{
|
||||
Encoding = DataEncoding.Csv,
|
||||
Chunks = null,
|
||||
Compression = null,
|
||||
GlobalTileIDs = [
|
||||
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 = [
|
||||
]),
|
||||
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
|
||||
]
|
||||
])
|
||||
}
|
||||
},
|
||||
new TileLayer
|
||||
|
@ -151,22 +146,20 @@ public partial class TestData
|
|||
Data = new Data
|
||||
{
|
||||
Encoding = DataEncoding.Csv,
|
||||
Chunks = null,
|
||||
Compression = null,
|
||||
GlobalTileIDs = [
|
||||
GlobalTileIDs = new Optional<uint[]>([
|
||||
0, 15, 15, 0, 0,
|
||||
0, 15, 15, 0, 0,
|
||||
0, 15, 15, 15, 0,
|
||||
15, 15, 15, 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
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -193,22 +186,20 @@ public partial class TestData
|
|||
Data = new Data
|
||||
{
|
||||
Encoding = DataEncoding.Csv,
|
||||
Chunks = null,
|
||||
Compression = null,
|
||||
GlobalTileIDs = [
|
||||
GlobalTileIDs = new Optional<uint[]>([
|
||||
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
|
||||
],
|
||||
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
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ public abstract class BaseLayer : HasPropertiesBase
|
|||
/// <summary>
|
||||
/// A tint color that is multiplied with any tiles drawn by this layer.
|
||||
/// </summary>
|
||||
public Color? TintColor { get; set; }
|
||||
public Optional<Color> TintColor { get; set; } = Optional<Color>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Horizontal offset for this layer in pixels.
|
||||
|
|
|
@ -118,27 +118,27 @@ public class Data
|
|||
/// <summary>
|
||||
/// The encoding used to encode the tile layer data.
|
||||
/// </summary>
|
||||
public DataEncoding? Encoding { get; set; }
|
||||
public Optional<DataEncoding> Encoding { get; set; } = Optional<DataEncoding>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The compression method used to compress the tile layer data.
|
||||
/// </summary>
|
||||
public DataCompression? Compression { get; set; }
|
||||
public Optional<DataCompression> Compression { get; set; } = Optional<DataCompression>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// <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>
|
||||
public uint[]? GlobalTileIDs { get; set; }
|
||||
public uint[] GlobalTileIDs { get; set; }
|
||||
|
||||
/// <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"/>.
|
||||
/// </summary>
|
||||
public FlippingFlags[]? FlippingFlags { get; set; }
|
||||
public FlippingFlags[] FlippingFlags { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the map is infinite, it will instead contain a list of chunks.
|
||||
/// </summary>
|
||||
public Chunk[]? Chunks { get; set; }
|
||||
public Optional<Chunk[]> Chunks { get; set; } = Optional<Chunk[]>.Empty;
|
||||
}
|
||||
|
|
|
@ -13,20 +13,20 @@ public class ImageLayer : BaseLayer
|
|||
/// <summary>
|
||||
/// The Y position of the image layer in pixels.
|
||||
/// </summary>
|
||||
public uint Y { get; set; } = 0;
|
||||
public Optional<uint> Y { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the image drawn by this layer is repeated along the X axis.
|
||||
/// </summary>
|
||||
public bool RepeatX { get; set; } = false;
|
||||
public Optional<bool> RepeatX { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the image drawn by this layer is repeated along the Y axis.
|
||||
/// </summary>
|
||||
public bool RepeatY { get; set; } = false;
|
||||
public Optional<bool> RepeatY { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// The image to be drawn by this image layer.
|
||||
/// </summary>
|
||||
public Image? Image { get; set; }
|
||||
public Optional<Image> Image { get; set; }
|
||||
}
|
||||
|
|
|
@ -36,17 +36,17 @@ public class ObjectLayer : BaseLayer
|
|||
/// <summary>
|
||||
/// The width of the object layer in tiles. Meaningless.
|
||||
/// </summary>
|
||||
public uint? Width { get; set; }
|
||||
public uint Width { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The height of the object layer in tiles. Meaningless.
|
||||
/// </summary>
|
||||
public uint? Height { get; set; }
|
||||
public uint Height { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// A color that is multiplied with any tile objects drawn by this layer.
|
||||
/// </summary>
|
||||
public Color? Color { get; set; }
|
||||
public Optional<Color> Color { get; set; } = Optional<Color>.Empty;
|
||||
|
||||
/// <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"/>).
|
||||
|
|
|
@ -10,7 +10,7 @@ public abstract class Object : HasPropertiesBase
|
|||
/// <summary>
|
||||
/// Unique ID of the objects. Each object that is placed on a map gets a unique ID. Even if an object was deleted, no object gets the same ID.
|
||||
/// </summary>
|
||||
public uint? ID { get; set; }
|
||||
public Optional<uint> ID { get; set; } = Optional<uint>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the object. An arbitrary string.
|
||||
|
@ -55,7 +55,7 @@ public abstract class Object : HasPropertiesBase
|
|||
/// <summary>
|
||||
/// A reference to a template file.
|
||||
/// </summary>
|
||||
public string? Template { get; set; }
|
||||
public Optional<string> Template { get; set; } = Optional<string>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Object properties.
|
||||
|
|
|
@ -28,5 +28,5 @@ public class TileLayer : BaseLayer
|
|||
/// <summary>
|
||||
/// The tile layer data.
|
||||
/// </summary>
|
||||
public Data? Data { get; set; }
|
||||
public Optional<Data> Data { get; set; } = Optional<Data>.Empty;
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ public class Map : HasPropertiesBase
|
|||
/// <summary>
|
||||
/// The Tiled version used to save the file.
|
||||
/// </summary>
|
||||
public required string TiledVersion { get; set; }
|
||||
public Optional<string> TiledVersion { get; set; } = Optional<string>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The class of this map.
|
||||
|
@ -146,17 +146,17 @@ public class Map : HasPropertiesBase
|
|||
/// <summary>
|
||||
/// Only for hexagonal maps. Determines the width or height (depending on the staggered axis) of the tile's edge, in pixels.
|
||||
/// </summary>
|
||||
public uint? HexSideLength { get; set; }
|
||||
public Optional<uint> HexSideLength { get; set; } = Optional<uint>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// For staggered and hexagonal maps, determines which axis (X or Y) is staggered.
|
||||
/// </summary>
|
||||
public StaggerAxis? StaggerAxis { get; set; }
|
||||
public Optional<StaggerAxis> StaggerAxis { get; set; } = Optional<StaggerAxis>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// For staggered and hexagonal maps, determines whether the "even" or "odd" indexes along the staggered axis are shifted.
|
||||
/// </summary>
|
||||
public StaggerIndex? StaggerIndex { get; set; }
|
||||
public Optional<StaggerIndex> StaggerIndex { get; set; } = Optional<StaggerIndex>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// X coordinate of the parallax origin in pixels.
|
||||
|
|
132
src/DotTiled/Optional.cs
Normal file
132
src/DotTiled/Optional.cs
Normal file
|
@ -0,0 +1,132 @@
|
|||
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;
|
||||
|
||||
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
|
||||
}
|
|
@ -8,6 +8,20 @@ namespace DotTiled.Serialization;
|
|||
|
||||
internal static partial class Helpers
|
||||
{
|
||||
internal static Func<string, T> CreateMapper<T>(Func<string, Exception> noMatch, params (string, T)[] mappings)
|
||||
{
|
||||
return s =>
|
||||
{
|
||||
foreach (var (key, value) in mappings)
|
||||
{
|
||||
if (key == s)
|
||||
return value;
|
||||
}
|
||||
|
||||
throw noMatch(s);
|
||||
};
|
||||
}
|
||||
|
||||
internal static uint[] ReadMemoryStreamAsInt32Array(Stream stream)
|
||||
{
|
||||
var finalValues = new List<uint>();
|
||||
|
|
|
@ -50,8 +50,8 @@ public abstract partial class TmjReaderBase
|
|||
Properties = properties,
|
||||
X = x,
|
||||
Y = y,
|
||||
Width = width,
|
||||
Height = height,
|
||||
Width = width ?? 0,
|
||||
Height = height ?? 0,
|
||||
Color = color,
|
||||
DrawOrder = drawOrder,
|
||||
Objects = objects
|
||||
|
|
|
@ -76,31 +76,32 @@ public abstract partial class TmjReaderBase
|
|||
TransparentColor = transparentColor
|
||||
} : null;
|
||||
|
||||
return new Tileset
|
||||
{
|
||||
Class = @class,
|
||||
Columns = columns,
|
||||
FillMode = fillMode,
|
||||
FirstGID = firstGID,
|
||||
Grid = grid,
|
||||
Image = imageModel,
|
||||
Margin = margin,
|
||||
Name = name,
|
||||
ObjectAlignment = objectAlignment,
|
||||
Properties = properties,
|
||||
Source = source,
|
||||
Spacing = spacing,
|
||||
TileCount = tileCount,
|
||||
TiledVersion = tiledVersion,
|
||||
TileHeight = tileHeight,
|
||||
TileOffset = tileOffset,
|
||||
RenderSize = tileRenderSize,
|
||||
Tiles = tiles,
|
||||
TileWidth = tileWidth,
|
||||
Version = version,
|
||||
Wangsets = wangsets,
|
||||
Transformations = transformations
|
||||
};
|
||||
return null;
|
||||
// return new Tileset
|
||||
// {
|
||||
// Class = @class,
|
||||
// Columns = columns,
|
||||
// FillMode = fillMode,
|
||||
// FirstGID = firstGID,
|
||||
// Grid = grid,
|
||||
// Image = imageModel,
|
||||
// Margin = margin,
|
||||
// Name = name,
|
||||
// ObjectAlignment = objectAlignment,
|
||||
// Properties = properties,
|
||||
// Source = source,
|
||||
// Spacing = spacing,
|
||||
// TileCount = tileCount,
|
||||
// TiledVersion = tiledVersion,
|
||||
// TileHeight = tileHeight,
|
||||
// TileOffset = tileOffset,
|
||||
// RenderSize = tileRenderSize,
|
||||
// Tiles = tiles,
|
||||
// TileWidth = tileWidth,
|
||||
// Version = version,
|
||||
// Wangsets = wangsets,
|
||||
// Transformations = transformations
|
||||
// };
|
||||
}
|
||||
|
||||
internal static Transformations ReadTransformations(JsonElement element)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Xml;
|
||||
|
||||
|
@ -31,40 +30,43 @@ internal static class ExtensionsXmlReader
|
|||
return enumParser(value);
|
||||
}
|
||||
|
||||
internal static string? GetOptionalAttribute(this XmlReader reader, string attribute, string? defaultValue = default) =>
|
||||
reader.GetAttribute(attribute) ?? defaultValue;
|
||||
internal static Optional<string> GetOptionalAttribute(this XmlReader reader, string attribute)
|
||||
{
|
||||
var value = reader.GetAttribute(attribute);
|
||||
return value is null ? new Optional<string>() : new Optional<string>(value);
|
||||
}
|
||||
|
||||
internal static T? GetOptionalAttributeParseable<T>(this XmlReader reader, string attribute) where T : struct, IParsable<T>
|
||||
internal static Optional<T> GetOptionalAttributeParseable<T>(this XmlReader reader, string attribute) where T : struct, IParsable<T>
|
||||
{
|
||||
var value = reader.GetAttribute(attribute);
|
||||
if (value is null)
|
||||
return null;
|
||||
return new Optional<T>();
|
||||
|
||||
return T.Parse(value, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
internal static T? GetOptionalAttributeParseable<T>(this XmlReader reader, string attribute, Func<string, T> parser) where T : struct
|
||||
internal static Optional<T> GetOptionalAttributeParseable<T>(this XmlReader reader, string attribute, Func<string, T> parser) where T : struct
|
||||
{
|
||||
var value = reader.GetAttribute(attribute);
|
||||
if (value is null)
|
||||
return null;
|
||||
return new Optional<T>();
|
||||
|
||||
return parser(value);
|
||||
}
|
||||
|
||||
internal static T? GetOptionalAttributeClass<T>(this XmlReader reader, string attribute) where T : class, IParsable<T>
|
||||
internal static Optional<T> GetOptionalAttributeClass<T>(this XmlReader reader, string attribute) where T : class, IParsable<T>
|
||||
{
|
||||
var value = reader.GetAttribute(attribute);
|
||||
if (value is null)
|
||||
return null;
|
||||
return new Optional<T>();
|
||||
|
||||
return T.Parse(value, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
internal static T? GetOptionalAttributeEnum<T>(this XmlReader reader, string attribute, Func<string, T> enumParser) where T : struct, Enum
|
||||
internal static Optional<T> GetOptionalAttributeEnum<T>(this XmlReader reader, string attribute, Func<string, T> enumParser) where T : struct, Enum
|
||||
{
|
||||
var value = reader.GetAttribute(attribute);
|
||||
return value != null ? enumParser(value) : null;
|
||||
return value != null ? enumParser(value) : new Optional<T>();
|
||||
}
|
||||
|
||||
internal static List<T> ReadList<T>(this XmlReader reader, string wrapper, string elementName, Func<XmlReader, T> readElement)
|
||||
|
@ -107,7 +109,6 @@ internal static class ExtensionsXmlReader
|
|||
reader.ReadEndElement();
|
||||
}
|
||||
|
||||
[return: NotNull]
|
||||
internal static List<T> ProcessChildren<T>(this XmlReader reader, string wrapper, Func<XmlReader, string, T> getProcessAction)
|
||||
{
|
||||
var list = new List<T>();
|
||||
|
|
|
@ -2,14 +2,14 @@ namespace DotTiled.Serialization.Tmx;
|
|||
|
||||
public abstract partial class TmxReaderBase
|
||||
{
|
||||
internal Chunk ReadChunk(DataEncoding? encoding, DataCompression? compression)
|
||||
internal Chunk ReadChunk(Optional<DataEncoding> encoding, Optional<DataCompression> compression)
|
||||
{
|
||||
var x = _reader.GetRequiredAttributeParseable<int>("x");
|
||||
var y = _reader.GetRequiredAttributeParseable<int>("y");
|
||||
var width = _reader.GetRequiredAttributeParseable<uint>("width");
|
||||
var height = _reader.GetRequiredAttributeParseable<uint>("height");
|
||||
|
||||
var usesTileChildrenInsteadOfRawData = encoding is null;
|
||||
var usesTileChildrenInsteadOfRawData = !encoding.HasValue;
|
||||
if (usesTileChildrenInsteadOfRawData)
|
||||
{
|
||||
var globalTileIDsWithFlippingFlags = ReadTileChildrenInWrapper("chunk", _reader);
|
||||
|
@ -18,7 +18,7 @@ public abstract partial class TmxReaderBase
|
|||
}
|
||||
else
|
||||
{
|
||||
var globalTileIDsWithFlippingFlags = ReadRawData(_reader, encoding!.Value, compression);
|
||||
var globalTileIDsWithFlippingFlags = ReadRawData(_reader, encoding.Value, compression);
|
||||
var (globalTileIDs, flippingFlags) = ReadAndClearFlippingFlagsFromGIDs(globalTileIDsWithFlippingFlags);
|
||||
return new Chunk { X = x, Y = y, Width = width, Height = height, GlobalTileIDs = globalTileIDs, FlippingFlags = flippingFlags };
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ public abstract partial class TmxReaderBase
|
|||
return new Data { Encoding = encoding, Compression = compression, GlobalTileIDs = null, Chunks = chunks };
|
||||
}
|
||||
|
||||
var usesTileChildrenInsteadOfRawData = encoding is null && compression is null;
|
||||
var usesTileChildrenInsteadOfRawData = !encoding.HasValue && !compression.HasValue;
|
||||
if (usesTileChildrenInsteadOfRawData)
|
||||
{
|
||||
var tileChildrenGlobalTileIDsWithFlippingFlags = ReadTileChildrenInWrapper("data", _reader);
|
||||
|
@ -41,7 +41,7 @@ public abstract partial class TmxReaderBase
|
|||
return new Data { Encoding = encoding, Compression = compression, GlobalTileIDs = tileChildrenGlobalTileIDs, FlippingFlags = tileChildrenFlippingFlags, Chunks = null };
|
||||
}
|
||||
|
||||
var rawDataGlobalTileIDsWithFlippingFlags = ReadRawData(_reader, encoding!.Value, compression);
|
||||
var rawDataGlobalTileIDsWithFlippingFlags = ReadRawData(_reader, encoding, compression);
|
||||
var (rawDataGlobalTileIDs, rawDataFlippingFlags) = ReadAndClearFlippingFlagsFromGIDs(rawDataGlobalTileIDsWithFlippingFlags);
|
||||
return new Data { Encoding = encoding, Compression = compression, GlobalTileIDs = rawDataGlobalTileIDs, FlippingFlags = rawDataFlippingFlags, Chunks = null };
|
||||
}
|
||||
|
@ -62,19 +62,19 @@ public abstract partial class TmxReaderBase
|
|||
}
|
||||
|
||||
internal static uint[] ReadTileChildrenInWrapper(string wrapper, XmlReader reader) =>
|
||||
reader.ReadList(wrapper, "tile", (r) => r.GetOptionalAttributeParseable<uint>("gid") ?? 0).ToArray();
|
||||
reader.ReadList(wrapper, "tile", (r) => r.GetOptionalAttributeParseable<uint>("gid").GetValueOr(0)).ToArray();
|
||||
|
||||
internal static uint[] ReadRawData(XmlReader reader, DataEncoding encoding, DataCompression? compression)
|
||||
internal static uint[] ReadRawData(XmlReader reader, DataEncoding encoding, Optional<DataCompression> compression)
|
||||
{
|
||||
var data = reader.ReadElementContentAsString();
|
||||
if (encoding == DataEncoding.Csv)
|
||||
return ParseCsvData(data);
|
||||
|
||||
using var bytes = new MemoryStream(Convert.FromBase64String(data));
|
||||
if (compression is null)
|
||||
if (!compression.HasValue)
|
||||
return ReadMemoryStreamAsInt32Array(bytes);
|
||||
|
||||
var decompressed = compression switch
|
||||
var decompressed = compression.Value switch
|
||||
{
|
||||
DataCompression.GZip => DecompressGZip(bytes),
|
||||
DataCompression.ZLib => DecompressZLib(bytes),
|
||||
|
|
|
@ -13,16 +13,15 @@ public abstract partial class TmxReaderBase
|
|||
{
|
||||
// Attributes
|
||||
var version = _reader.GetRequiredAttribute("version");
|
||||
var tiledVersion = _reader.GetRequiredAttribute("tiledversion");
|
||||
var @class = _reader.GetOptionalAttribute("class") ?? "";
|
||||
var orientation = _reader.GetRequiredAttributeEnum<MapOrientation>("orientation", s => s switch
|
||||
{
|
||||
"orthogonal" => MapOrientation.Orthogonal,
|
||||
"isometric" => MapOrientation.Isometric,
|
||||
"staggered" => MapOrientation.Staggered,
|
||||
"hexagonal" => MapOrientation.Hexagonal,
|
||||
_ => throw new InvalidOperationException($"Unknown orientation '{s}'")
|
||||
});
|
||||
var tiledVersion = _reader.GetOptionalAttribute("tiledversion");
|
||||
var @class = _reader.GetOptionalAttribute("class").GetValueOr("");
|
||||
var orientation = _reader.GetRequiredAttributeEnum<MapOrientation>("orientation", Helpers.CreateMapper<MapOrientation>(
|
||||
s => throw new InvalidOperationException($"Unknown orientation '{s}'"),
|
||||
("orthogonal", MapOrientation.Orthogonal),
|
||||
("isometric", MapOrientation.Isometric),
|
||||
("staggered", MapOrientation.Staggered),
|
||||
("hexagonal", MapOrientation.Hexagonal)
|
||||
));
|
||||
var renderOrder = _reader.GetOptionalAttributeEnum<RenderOrder>("renderorder", s => s switch
|
||||
{
|
||||
"right-down" => RenderOrder.RightDown,
|
||||
|
@ -30,8 +29,8 @@ public abstract partial class TmxReaderBase
|
|||
"left-down" => RenderOrder.LeftDown,
|
||||
"left-up" => RenderOrder.LeftUp,
|
||||
_ => throw new InvalidOperationException($"Unknown render order '{s}'")
|
||||
}) ?? RenderOrder.RightDown;
|
||||
var compressionLevel = _reader.GetOptionalAttributeParseable<int>("compressionlevel") ?? -1;
|
||||
}).GetValueOr(RenderOrder.RightDown);
|
||||
var compressionLevel = _reader.GetOptionalAttributeParseable<int>("compressionlevel").GetValueOr(-1);
|
||||
var width = _reader.GetRequiredAttributeParseable<uint>("width");
|
||||
var height = _reader.GetRequiredAttributeParseable<uint>("height");
|
||||
var tileWidth = _reader.GetRequiredAttributeParseable<uint>("tilewidth");
|
||||
|
@ -49,15 +48,15 @@ public abstract partial class TmxReaderBase
|
|||
"even" => StaggerIndex.Even,
|
||||
_ => throw new InvalidOperationException($"Unknown stagger index '{s}'")
|
||||
});
|
||||
var parallaxOriginX = _reader.GetOptionalAttributeParseable<float>("parallaxoriginx") ?? 0.0f;
|
||||
var parallaxOriginY = _reader.GetOptionalAttributeParseable<float>("parallaxoriginy") ?? 0.0f;
|
||||
var backgroundColor = _reader.GetOptionalAttributeClass<Color>("backgroundcolor") ?? Color.Parse("#00000000", CultureInfo.InvariantCulture);
|
||||
var parallaxOriginX = _reader.GetOptionalAttributeParseable<float>("parallaxoriginx").GetValueOr(0.0f);
|
||||
var parallaxOriginY = _reader.GetOptionalAttributeParseable<float>("parallaxoriginy").GetValueOr(0.0f);
|
||||
var backgroundColor = _reader.GetOptionalAttributeClass<Color>("backgroundcolor").GetValueOr(Color.Parse("#00000000", CultureInfo.InvariantCulture));
|
||||
var nextLayerID = _reader.GetRequiredAttributeParseable<uint>("nextlayerid");
|
||||
var nextObjectID = _reader.GetRequiredAttributeParseable<uint>("nextobjectid");
|
||||
var infinite = (_reader.GetOptionalAttributeParseable<uint>("infinite") ?? 0) == 1;
|
||||
var infinite = _reader.GetOptionalAttributeParseable<uint>("infinite").GetValueOr(0) == 1;
|
||||
|
||||
// At most one of
|
||||
List<IProperty>? properties = null;
|
||||
List<IProperty> properties = null;
|
||||
|
||||
// Any number of
|
||||
List<BaseLayer> layers = [];
|
||||
|
@ -66,7 +65,7 @@ public abstract partial class TmxReaderBase
|
|||
_reader.ProcessChildren("map", (r, elementName) => elementName switch
|
||||
{
|
||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(), "Properties"),
|
||||
"tileset" => () => tilesets.Add(ReadTileset()),
|
||||
"tileset" => () => tilesets.Add(ReadTileset(version, tiledVersion)),
|
||||
"layer" => () => layers.Add(ReadTileLayer(infinite)),
|
||||
"objectgroup" => () => layers.Add(ReadObjectLayer()),
|
||||
"imagelayer" => () => layers.Add(ReadImageLayer()),
|
||||
|
|
|
@ -12,29 +12,29 @@ public abstract partial class TmxReaderBase
|
|||
{
|
||||
// Attributes
|
||||
var id = _reader.GetRequiredAttributeParseable<uint>("id");
|
||||
var name = _reader.GetOptionalAttribute("name") ?? "";
|
||||
var @class = _reader.GetOptionalAttribute("class") ?? "";
|
||||
var x = _reader.GetOptionalAttributeParseable<uint>("x") ?? 0;
|
||||
var y = _reader.GetOptionalAttributeParseable<uint>("y") ?? 0;
|
||||
var width = _reader.GetOptionalAttributeParseable<uint>("width");
|
||||
var height = _reader.GetOptionalAttributeParseable<uint>("height");
|
||||
var opacity = _reader.GetOptionalAttributeParseable<float>("opacity") ?? 1.0f;
|
||||
var visible = (_reader.GetOptionalAttributeParseable<uint>("visible") ?? 1) == 1;
|
||||
var name = _reader.GetOptionalAttribute("name").GetValueOr("");
|
||||
var @class = _reader.GetOptionalAttribute("class").GetValueOr("");
|
||||
var x = _reader.GetOptionalAttributeParseable<uint>("x").GetValueOr(0);
|
||||
var y = _reader.GetOptionalAttributeParseable<uint>("y").GetValueOr(0);
|
||||
var width = _reader.GetOptionalAttributeParseable<uint>("width").GetValueOr(0);
|
||||
var height = _reader.GetOptionalAttributeParseable<uint>("height").GetValueOr(0);
|
||||
var opacity = _reader.GetOptionalAttributeParseable<float>("opacity").GetValueOr(1.0f);
|
||||
var visible = _reader.GetOptionalAttributeParseable<uint>("visible").GetValueOr(1) == 1;
|
||||
var tintColor = _reader.GetOptionalAttributeClass<Color>("tintcolor");
|
||||
var offsetX = _reader.GetOptionalAttributeParseable<float>("offsetx") ?? 0.0f;
|
||||
var offsetY = _reader.GetOptionalAttributeParseable<float>("offsety") ?? 0.0f;
|
||||
var parallaxX = _reader.GetOptionalAttributeParseable<float>("parallaxx") ?? 1.0f;
|
||||
var parallaxY = _reader.GetOptionalAttributeParseable<float>("parallaxy") ?? 1.0f;
|
||||
var offsetX = _reader.GetOptionalAttributeParseable<float>("offsetx").GetValueOr(0.0f);
|
||||
var offsetY = _reader.GetOptionalAttributeParseable<float>("offsety").GetValueOr(0.0f);
|
||||
var parallaxX = _reader.GetOptionalAttributeParseable<float>("parallaxx").GetValueOr(1.0f);
|
||||
var parallaxY = _reader.GetOptionalAttributeParseable<float>("parallaxy").GetValueOr(1.0f);
|
||||
var color = _reader.GetOptionalAttributeClass<Color>("color");
|
||||
var drawOrder = _reader.GetOptionalAttributeEnum<DrawOrder>("draworder", s => s switch
|
||||
{
|
||||
"topdown" => DrawOrder.TopDown,
|
||||
"index" => DrawOrder.Index,
|
||||
_ => throw new InvalidOperationException($"Unknown draw order '{s}'")
|
||||
}) ?? DrawOrder.TopDown;
|
||||
}).GetValueOr(DrawOrder.TopDown);
|
||||
|
||||
// Elements
|
||||
List<IProperty>? properties = null;
|
||||
List<IProperty> properties = null;
|
||||
List<DotTiled.Object> objects = [];
|
||||
|
||||
_reader.ProcessChildren("objectgroup", (r, elementName) => elementName switch
|
||||
|
@ -71,11 +71,11 @@ public abstract partial class TmxReaderBase
|
|||
{
|
||||
// Attributes
|
||||
var template = _reader.GetOptionalAttribute("template");
|
||||
DotTiled.Object? obj = null;
|
||||
if (template is not null)
|
||||
DotTiled.Object obj = null;
|
||||
if (template.HasValue)
|
||||
obj = _externalTemplateResolver(template).Object;
|
||||
|
||||
uint? idDefault = obj?.ID ?? null;
|
||||
uint idDefault = obj?.ID.GetValueOr(0) ?? 0;
|
||||
string nameDefault = obj?.Name ?? "";
|
||||
string typeDefault = obj?.Type ?? "";
|
||||
float xDefault = obj?.X ?? 0f;
|
||||
|
@ -83,25 +83,25 @@ public abstract partial class TmxReaderBase
|
|||
float widthDefault = obj?.Width ?? 0f;
|
||||
float heightDefault = obj?.Height ?? 0f;
|
||||
float rotationDefault = obj?.Rotation ?? 0f;
|
||||
uint? gidDefault = obj is TileObject tileObj ? tileObj.GID : null;
|
||||
Optional<uint> gidDefault = obj is TileObject tileObj ? tileObj.GID : Optional<uint>.Empty;
|
||||
bool visibleDefault = obj?.Visible ?? true;
|
||||
List<IProperty>? propertiesDefault = obj?.Properties ?? null;
|
||||
List<IProperty> propertiesDefault = obj?.Properties ?? null;
|
||||
|
||||
var id = _reader.GetOptionalAttributeParseable<uint>("id") ?? idDefault;
|
||||
var name = _reader.GetOptionalAttribute("name") ?? nameDefault;
|
||||
var type = _reader.GetOptionalAttribute("type") ?? typeDefault;
|
||||
var x = _reader.GetOptionalAttributeParseable<float>("x") ?? xDefault;
|
||||
var y = _reader.GetOptionalAttributeParseable<float>("y") ?? yDefault;
|
||||
var width = _reader.GetOptionalAttributeParseable<float>("width") ?? widthDefault;
|
||||
var height = _reader.GetOptionalAttributeParseable<float>("height") ?? heightDefault;
|
||||
var rotation = _reader.GetOptionalAttributeParseable<float>("rotation") ?? rotationDefault;
|
||||
var gid = _reader.GetOptionalAttributeParseable<uint>("gid") ?? gidDefault;
|
||||
var visible = _reader.GetOptionalAttributeParseable<bool>("visible") ?? visibleDefault;
|
||||
var id = _reader.GetOptionalAttributeParseable<uint>("id").GetValueOr(idDefault);
|
||||
var name = _reader.GetOptionalAttribute("name").GetValueOr(nameDefault);
|
||||
var type = _reader.GetOptionalAttribute("type").GetValueOr(typeDefault);
|
||||
var x = _reader.GetOptionalAttributeParseable<float>("x").GetValueOr(xDefault);
|
||||
var y = _reader.GetOptionalAttributeParseable<float>("y").GetValueOr(yDefault);
|
||||
var width = _reader.GetOptionalAttributeParseable<float>("width").GetValueOr(widthDefault);
|
||||
var height = _reader.GetOptionalAttributeParseable<float>("height").GetValueOr(heightDefault);
|
||||
var rotation = _reader.GetOptionalAttributeParseable<float>("rotation").GetValueOr(rotationDefault);
|
||||
var gid = _reader.GetOptionalAttributeParseable<uint>("gid").GetValueOrOptional(gidDefault);
|
||||
var visible = _reader.GetOptionalAttributeParseable<bool>("visible").GetValueOr(visibleDefault);
|
||||
|
||||
// Elements
|
||||
DotTiled.Object? foundObject = null;
|
||||
DotTiled.Object foundObject = null;
|
||||
int propertiesCounter = 0;
|
||||
List<IProperty>? properties = propertiesDefault;
|
||||
List<IProperty> properties = propertiesDefault;
|
||||
|
||||
_reader.ProcessChildren("object", (r, elementName) => elementName switch
|
||||
{
|
||||
|
@ -116,7 +116,7 @@ public abstract partial class TmxReaderBase
|
|||
|
||||
if (foundObject is null)
|
||||
{
|
||||
if (gid is not null)
|
||||
if (gid.HasValue)
|
||||
foundObject = new TileObject { ID = id, GID = gid.Value };
|
||||
else
|
||||
foundObject = new RectangleObject { ID = id };
|
||||
|
@ -137,7 +137,7 @@ public abstract partial class TmxReaderBase
|
|||
return OverrideObject(obj, foundObject);
|
||||
}
|
||||
|
||||
internal static DotTiled.Object OverrideObject(DotTiled.Object? obj, DotTiled.Object foundObject)
|
||||
internal static DotTiled.Object OverrideObject(DotTiled.Object obj, DotTiled.Object foundObject)
|
||||
{
|
||||
if (obj is null)
|
||||
return foundObject;
|
||||
|
|
|
@ -25,9 +25,9 @@ public abstract partial class TmxReaderBase
|
|||
"object" => PropertyType.Object,
|
||||
"class" => PropertyType.Class,
|
||||
_ => throw new XmlException("Invalid property type")
|
||||
}) ?? PropertyType.String;
|
||||
}).GetValueOr(PropertyType.String);
|
||||
var propertyType = r.GetOptionalAttribute("propertytype");
|
||||
if (propertyType is not null)
|
||||
if (propertyType.HasValue)
|
||||
{
|
||||
return ReadPropertyWithCustomType();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace DotTiled.Serialization.Tmx;
|
||||
|
||||
|
@ -8,22 +7,22 @@ public abstract partial class TmxReaderBase
|
|||
internal TileLayer ReadTileLayer(bool dataUsesChunks)
|
||||
{
|
||||
var id = _reader.GetRequiredAttributeParseable<uint>("id");
|
||||
var name = _reader.GetOptionalAttribute("name") ?? "";
|
||||
var @class = _reader.GetOptionalAttribute("class") ?? "";
|
||||
var x = _reader.GetOptionalAttributeParseable<uint>("x") ?? 0;
|
||||
var y = _reader.GetOptionalAttributeParseable<uint>("y") ?? 0;
|
||||
var name = _reader.GetOptionalAttribute("name").GetValueOr("");
|
||||
var @class = _reader.GetOptionalAttribute("class").GetValueOr("");
|
||||
var x = _reader.GetOptionalAttributeParseable<uint>("x").GetValueOr(0);
|
||||
var y = _reader.GetOptionalAttributeParseable<uint>("y").GetValueOr(0);
|
||||
var width = _reader.GetRequiredAttributeParseable<uint>("width");
|
||||
var height = _reader.GetRequiredAttributeParseable<uint>("height");
|
||||
var opacity = _reader.GetOptionalAttributeParseable<float>("opacity") ?? 1.0f;
|
||||
var visible = (_reader.GetOptionalAttributeParseable<uint>("visible") ?? 1) == 1;
|
||||
var opacity = _reader.GetOptionalAttributeParseable<float>("opacity").GetValueOr(1.0f);
|
||||
var visible = _reader.GetOptionalAttributeParseable<uint>("visible").GetValueOr(1) == 1;
|
||||
var tintColor = _reader.GetOptionalAttributeClass<Color>("tintcolor");
|
||||
var offsetX = _reader.GetOptionalAttributeParseable<float>("offsetx") ?? 0.0f;
|
||||
var offsetY = _reader.GetOptionalAttributeParseable<float>("offsety") ?? 0.0f;
|
||||
var parallaxX = _reader.GetOptionalAttributeParseable<float>("parallaxx") ?? 1.0f;
|
||||
var parallaxY = _reader.GetOptionalAttributeParseable<float>("parallaxy") ?? 1.0f;
|
||||
var offsetX = _reader.GetOptionalAttributeParseable<float>("offsetx").GetValueOr(0.0f);
|
||||
var offsetY = _reader.GetOptionalAttributeParseable<float>("offsety").GetValueOr(0.0f);
|
||||
var parallaxX = _reader.GetOptionalAttributeParseable<float>("parallaxx").GetValueOr(1.0f);
|
||||
var parallaxY = _reader.GetOptionalAttributeParseable<float>("parallaxy").GetValueOr(1.0f);
|
||||
|
||||
List<IProperty>? properties = null;
|
||||
Data? data = null;
|
||||
List<IProperty> properties = null;
|
||||
Data data = null;
|
||||
|
||||
_reader.ProcessChildren("layer", (r, elementName) => elementName switch
|
||||
{
|
||||
|
@ -48,7 +47,7 @@ public abstract partial class TmxReaderBase
|
|||
OffsetY = offsetY,
|
||||
ParallaxX = parallaxX,
|
||||
ParallaxY = parallaxY,
|
||||
Data = data,
|
||||
Data = data ?? Optional<Data>.Empty,
|
||||
Properties = properties ?? []
|
||||
};
|
||||
}
|
||||
|
@ -56,22 +55,22 @@ public abstract partial class TmxReaderBase
|
|||
internal ImageLayer ReadImageLayer()
|
||||
{
|
||||
var id = _reader.GetRequiredAttributeParseable<uint>("id");
|
||||
var name = _reader.GetOptionalAttribute("name") ?? "";
|
||||
var @class = _reader.GetOptionalAttribute("class") ?? "";
|
||||
var x = _reader.GetOptionalAttributeParseable<uint>("x") ?? 0;
|
||||
var y = _reader.GetOptionalAttributeParseable<uint>("y") ?? 0;
|
||||
var opacity = _reader.GetOptionalAttributeParseable<float>("opacity") ?? 1.0f;
|
||||
var visible = _reader.GetOptionalAttributeParseable<bool>("visible") ?? true;
|
||||
var name = _reader.GetOptionalAttribute("name").GetValueOr("");
|
||||
var @class = _reader.GetOptionalAttribute("class").GetValueOr("");
|
||||
var x = _reader.GetOptionalAttributeParseable<uint>("x").GetValueOr(0);
|
||||
var y = _reader.GetOptionalAttributeParseable<uint>("y").GetValueOr(0);
|
||||
var opacity = _reader.GetOptionalAttributeParseable<float>("opacity").GetValueOr(1f);
|
||||
var visible = _reader.GetOptionalAttributeParseable<bool>("visible").GetValueOr(true);
|
||||
var tintColor = _reader.GetOptionalAttributeClass<Color>("tintcolor");
|
||||
var offsetX = _reader.GetOptionalAttributeParseable<float>("offsetx") ?? 0.0f;
|
||||
var offsetY = _reader.GetOptionalAttributeParseable<float>("offsety") ?? 0.0f;
|
||||
var parallaxX = _reader.GetOptionalAttributeParseable<float>("parallaxx") ?? 1.0f;
|
||||
var parallaxY = _reader.GetOptionalAttributeParseable<float>("parallaxy") ?? 1.0f;
|
||||
var repeatX = (_reader.GetOptionalAttributeParseable<uint>("repeatx") ?? 0) == 1;
|
||||
var repeatY = (_reader.GetOptionalAttributeParseable<uint>("repeaty") ?? 0) == 1;
|
||||
var offsetX = _reader.GetOptionalAttributeParseable<float>("offsetx").GetValueOr(0.0f);
|
||||
var offsetY = _reader.GetOptionalAttributeParseable<float>("offsety").GetValueOr(0.0f);
|
||||
var parallaxX = _reader.GetOptionalAttributeParseable<float>("parallaxx").GetValueOr(1.0f);
|
||||
var parallaxY = _reader.GetOptionalAttributeParseable<float>("parallaxy").GetValueOr(1.0f);
|
||||
var repeatX = _reader.GetOptionalAttributeParseable<uint>("repeatx").GetValueOr(0) == 1;
|
||||
var repeatY = _reader.GetOptionalAttributeParseable<uint>("repeaty").GetValueOr(0) == 1;
|
||||
|
||||
List<IProperty>? properties = null;
|
||||
Image? image = null;
|
||||
List<IProperty> properties = null;
|
||||
Image image = null;
|
||||
|
||||
_reader.ProcessChildren("imagelayer", (r, elementName) => elementName switch
|
||||
{
|
||||
|
@ -104,17 +103,17 @@ public abstract partial class TmxReaderBase
|
|||
internal Group ReadGroup()
|
||||
{
|
||||
var id = _reader.GetRequiredAttributeParseable<uint>("id");
|
||||
var name = _reader.GetOptionalAttribute("name") ?? "";
|
||||
var @class = _reader.GetOptionalAttribute("class") ?? "";
|
||||
var opacity = _reader.GetOptionalAttributeParseable<float>("opacity") ?? 1.0f;
|
||||
var visible = (_reader.GetOptionalAttributeParseable<uint>("visible") ?? 1) == 1;
|
||||
var name = _reader.GetOptionalAttribute("name").GetValueOr("");
|
||||
var @class = _reader.GetOptionalAttribute("class").GetValueOr("");
|
||||
var opacity = _reader.GetOptionalAttributeParseable<float>("opacity").GetValueOr(1.0f);
|
||||
var visible = _reader.GetOptionalAttributeParseable<uint>("visible").GetValueOr(1) == 1;
|
||||
var tintColor = _reader.GetOptionalAttributeClass<Color>("tintcolor");
|
||||
var offsetX = _reader.GetOptionalAttributeParseable<float>("offsetx") ?? 0.0f;
|
||||
var offsetY = _reader.GetOptionalAttributeParseable<float>("offsety") ?? 0.0f;
|
||||
var parallaxX = _reader.GetOptionalAttributeParseable<float>("parallaxx") ?? 1.0f;
|
||||
var parallaxY = _reader.GetOptionalAttributeParseable<float>("parallaxy") ?? 1.0f;
|
||||
var offsetX = _reader.GetOptionalAttributeParseable<float>("offsetx").GetValueOr(0f);
|
||||
var offsetY = _reader.GetOptionalAttributeParseable<float>("offsety").GetValueOr(0f);
|
||||
var parallaxX = _reader.GetOptionalAttributeParseable<float>("parallaxx").GetValueOr(1f);
|
||||
var parallaxY = _reader.GetOptionalAttributeParseable<float>("parallaxy").GetValueOr(1f);
|
||||
|
||||
List<IProperty>? properties = null;
|
||||
List<IProperty> properties = null;
|
||||
List<BaseLayer> layers = [];
|
||||
|
||||
_reader.ProcessChildren("group", (r, elementName) => elementName switch
|
||||
|
|
|
@ -7,21 +7,36 @@ namespace DotTiled.Serialization.Tmx;
|
|||
|
||||
public abstract partial class TmxReaderBase
|
||||
{
|
||||
internal Tileset ReadTileset()
|
||||
internal Tileset ReadTileset(
|
||||
Optional<string> parentVersion = null,
|
||||
Optional<string> parentTiledVersion = null)
|
||||
{
|
||||
// Attributes
|
||||
var version = _reader.GetOptionalAttribute("version");
|
||||
var tiledVersion = _reader.GetOptionalAttribute("tiledversion");
|
||||
var firstGID = _reader.GetOptionalAttributeParseable<uint>("firstgid");
|
||||
var source = _reader.GetOptionalAttribute("source");
|
||||
var name = _reader.GetOptionalAttribute("name");
|
||||
var @class = _reader.GetOptionalAttribute("class") ?? "";
|
||||
var tileWidth = _reader.GetOptionalAttributeParseable<uint>("tilewidth");
|
||||
var tileHeight = _reader.GetOptionalAttributeParseable<uint>("tileheight");
|
||||
var spacing = _reader.GetOptionalAttributeParseable<uint>("spacing") ?? 0;
|
||||
var margin = _reader.GetOptionalAttributeParseable<uint>("margin") ?? 0;
|
||||
var tileCount = _reader.GetOptionalAttributeParseable<uint>("tilecount");
|
||||
var columns = _reader.GetOptionalAttributeParseable<uint>("columns");
|
||||
|
||||
// Check if external tileset
|
||||
if (source.HasValue && firstGID.HasValue)
|
||||
{
|
||||
// Is external tileset
|
||||
var externalTileset = _externalTilesetResolver(source);
|
||||
externalTileset.FirstGID = firstGID;
|
||||
externalTileset.Source = source;
|
||||
|
||||
_reader.ProcessChildren("tileset", (r, elementName) => r.Skip);
|
||||
return externalTileset;
|
||||
}
|
||||
|
||||
// Attributes
|
||||
var version = _reader.GetOptionalAttribute("version").GetValueOrOptional(parentVersion);
|
||||
var tiledVersion = _reader.GetOptionalAttribute("tiledversion").GetValueOrOptional(parentTiledVersion);
|
||||
var name = _reader.GetRequiredAttribute("name");
|
||||
var @class = _reader.GetOptionalAttribute("class").GetValueOr("");
|
||||
var tileWidth = _reader.GetRequiredAttributeParseable<uint>("tilewidth");
|
||||
var tileHeight = _reader.GetRequiredAttributeParseable<uint>("tileheight");
|
||||
var spacing = _reader.GetOptionalAttributeParseable<uint>("spacing").GetValueOr(0);
|
||||
var margin = _reader.GetOptionalAttributeParseable<uint>("margin").GetValueOr(0);
|
||||
var tileCount = _reader.GetRequiredAttributeParseable<uint>("tilecount");
|
||||
var columns = _reader.GetRequiredAttributeParseable<uint>("columns");
|
||||
var objectAlignment = _reader.GetOptionalAttributeEnum<ObjectAlignment>("objectalignment", s => s switch
|
||||
{
|
||||
"unspecified" => ObjectAlignment.Unspecified,
|
||||
|
@ -35,27 +50,27 @@ public abstract partial class TmxReaderBase
|
|||
"bottom" => ObjectAlignment.Bottom,
|
||||
"bottomright" => ObjectAlignment.BottomRight,
|
||||
_ => throw new InvalidOperationException($"Unknown object alignment '{s}'")
|
||||
}) ?? ObjectAlignment.Unspecified;
|
||||
}).GetValueOr(ObjectAlignment.Unspecified);
|
||||
var renderSize = _reader.GetOptionalAttributeEnum<TileRenderSize>("rendersize", s => s switch
|
||||
{
|
||||
"tile" => TileRenderSize.Tile,
|
||||
"grid" => TileRenderSize.Grid,
|
||||
_ => throw new InvalidOperationException($"Unknown render size '{s}'")
|
||||
}) ?? TileRenderSize.Tile;
|
||||
}).GetValueOr(TileRenderSize.Tile);
|
||||
var fillMode = _reader.GetOptionalAttributeEnum<FillMode>("fillmode", s => s switch
|
||||
{
|
||||
"stretch" => FillMode.Stretch,
|
||||
"preserve-aspect-fit" => FillMode.PreserveAspectFit,
|
||||
_ => throw new InvalidOperationException($"Unknown fill mode '{s}'")
|
||||
}) ?? FillMode.Stretch;
|
||||
}).GetValueOr(FillMode.Stretch);
|
||||
|
||||
// Elements
|
||||
Image? image = null;
|
||||
TileOffset? tileOffset = null;
|
||||
Grid? grid = null;
|
||||
List<IProperty>? properties = null;
|
||||
List<Wangset>? wangsets = null;
|
||||
Transformations? transformations = null;
|
||||
Image image = null;
|
||||
TileOffset tileOffset = null;
|
||||
Grid grid = null;
|
||||
List<IProperty> properties = null;
|
||||
List<Wangset> wangsets = null;
|
||||
Transformations transformations = null;
|
||||
List<Tile> tiles = [];
|
||||
|
||||
_reader.ProcessChildren("tileset", (r, elementName) => elementName switch
|
||||
|
@ -70,15 +85,6 @@ public abstract partial class TmxReaderBase
|
|||
_ => r.Skip
|
||||
});
|
||||
|
||||
// Check if tileset is referring to external file
|
||||
if (source is not null)
|
||||
{
|
||||
var resolvedTileset = _externalTilesetResolver(source);
|
||||
resolvedTileset.FirstGID = firstGID;
|
||||
resolvedTileset.Source = source;
|
||||
return resolvedTileset;
|
||||
}
|
||||
|
||||
return new Tileset
|
||||
{
|
||||
Version = version,
|
||||
|
@ -100,9 +106,9 @@ public abstract partial class TmxReaderBase
|
|||
TileOffset = tileOffset,
|
||||
Grid = grid,
|
||||
Properties = properties ?? [],
|
||||
Wangsets = wangsets,
|
||||
Wangsets = wangsets ?? [],
|
||||
Transformations = transformations,
|
||||
Tiles = tiles
|
||||
Tiles = tiles ?? []
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -128,7 +134,7 @@ public abstract partial class TmxReaderBase
|
|||
_ => r.Skip
|
||||
});
|
||||
|
||||
if (format is null && source is not null)
|
||||
if (!format.HasValue && source.HasValue)
|
||||
format = Helpers.ParseImageFormatFromSource(source);
|
||||
|
||||
return new Image
|
||||
|
@ -144,8 +150,8 @@ public abstract partial class TmxReaderBase
|
|||
internal TileOffset ReadTileOffset()
|
||||
{
|
||||
// Attributes
|
||||
var x = _reader.GetOptionalAttributeParseable<float>("x") ?? 0f;
|
||||
var y = _reader.GetOptionalAttributeParseable<float>("y") ?? 0f;
|
||||
var x = _reader.GetOptionalAttributeParseable<float>("x").GetValueOr(0f);
|
||||
var y = _reader.GetOptionalAttributeParseable<float>("y").GetValueOr(0f);
|
||||
|
||||
_reader.ReadStartElement("tileoffset");
|
||||
return new TileOffset { X = x, Y = y };
|
||||
|
@ -159,7 +165,7 @@ public abstract partial class TmxReaderBase
|
|||
"orthogonal" => GridOrientation.Orthogonal,
|
||||
"isometric" => GridOrientation.Isometric,
|
||||
_ => throw new InvalidOperationException($"Unknown orientation '{s}'")
|
||||
}) ?? GridOrientation.Orthogonal;
|
||||
}).GetValueOr(GridOrientation.Orthogonal);
|
||||
var width = _reader.GetRequiredAttributeParseable<uint>("width");
|
||||
var height = _reader.GetRequiredAttributeParseable<uint>("height");
|
||||
|
||||
|
@ -170,10 +176,10 @@ public abstract partial class TmxReaderBase
|
|||
internal Transformations ReadTransformations()
|
||||
{
|
||||
// Attributes
|
||||
var hFlip = (_reader.GetOptionalAttributeParseable<uint>("hflip") ?? 0) == 1;
|
||||
var vFlip = (_reader.GetOptionalAttributeParseable<uint>("vflip") ?? 0) == 1;
|
||||
var rotate = (_reader.GetOptionalAttributeParseable<uint>("rotate") ?? 0) == 1;
|
||||
var preferUntransformed = (_reader.GetOptionalAttributeParseable<uint>("preferuntransformed") ?? 0) == 1;
|
||||
var hFlip = _reader.GetOptionalAttributeParseable<uint>("hflip").GetValueOr(0) == 1;
|
||||
var vFlip = _reader.GetOptionalAttributeParseable<uint>("vflip").GetValueOr(0) == 1;
|
||||
var rotate = _reader.GetOptionalAttributeParseable<uint>("rotate").GetValueOr(0) == 1;
|
||||
var preferUntransformed = _reader.GetOptionalAttributeParseable<uint>("preferuntransformed").GetValueOr(0) == 1;
|
||||
|
||||
_reader.ReadStartElement("transformations");
|
||||
return new Transformations { HFlip = hFlip, VFlip = vFlip, Rotate = rotate, PreferUntransformed = preferUntransformed };
|
||||
|
@ -183,18 +189,18 @@ public abstract partial class TmxReaderBase
|
|||
{
|
||||
// Attributes
|
||||
var id = _reader.GetRequiredAttributeParseable<uint>("id");
|
||||
var type = _reader.GetOptionalAttribute("type") ?? "";
|
||||
var probability = _reader.GetOptionalAttributeParseable<float>("probability") ?? 0f;
|
||||
var x = _reader.GetOptionalAttributeParseable<uint>("x") ?? 0;
|
||||
var y = _reader.GetOptionalAttributeParseable<uint>("y") ?? 0;
|
||||
var type = _reader.GetOptionalAttribute("type").GetValueOr("");
|
||||
var probability = _reader.GetOptionalAttributeParseable<float>("probability").GetValueOr(0f);
|
||||
var x = _reader.GetOptionalAttributeParseable<uint>("x").GetValueOr(0);
|
||||
var y = _reader.GetOptionalAttributeParseable<uint>("y").GetValueOr(0);
|
||||
var width = _reader.GetOptionalAttributeParseable<uint>("width");
|
||||
var height = _reader.GetOptionalAttributeParseable<uint>("height");
|
||||
|
||||
// Elements
|
||||
List<IProperty>? properties = null;
|
||||
Image? image = null;
|
||||
ObjectLayer? objectLayer = null;
|
||||
List<Frame>? animation = null;
|
||||
List<IProperty> properties = null;
|
||||
Image image = null;
|
||||
ObjectLayer objectLayer = null;
|
||||
List<Frame> animation = null;
|
||||
|
||||
_reader.ProcessChildren("tile", (r, elementName) => elementName switch
|
||||
{
|
||||
|
@ -217,12 +223,12 @@ public abstract partial class TmxReaderBase
|
|||
Probability = probability,
|
||||
X = x,
|
||||
Y = y,
|
||||
Width = width ?? image?.Width ?? 0,
|
||||
Height = height ?? image?.Height ?? 0,
|
||||
Width = width.HasValue ? width : image?.Width ?? 0,
|
||||
Height = height.HasValue ? height : image?.Height ?? 0,
|
||||
Properties = properties ?? [],
|
||||
Image = image,
|
||||
ObjectLayer = objectLayer,
|
||||
Animation = animation
|
||||
Image = image is null ? Optional<Image>.Empty : image,
|
||||
ObjectLayer = objectLayer is null ? Optional<ObjectLayer>.Empty : objectLayer,
|
||||
Animation = animation ?? []
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -233,11 +239,11 @@ public abstract partial class TmxReaderBase
|
|||
{
|
||||
// Attributes
|
||||
var name = _reader.GetRequiredAttribute("name");
|
||||
var @class = _reader.GetOptionalAttribute("class") ?? "";
|
||||
var @class = _reader.GetOptionalAttribute("class").GetValueOr("");
|
||||
var tile = _reader.GetRequiredAttributeParseable<int>("tile");
|
||||
|
||||
// Elements
|
||||
List<IProperty>? properties = null;
|
||||
List<IProperty> properties = null;
|
||||
List<WangColor> wangColors = [];
|
||||
List<WangTile> wangTiles = [];
|
||||
|
||||
|
@ -267,13 +273,13 @@ public abstract partial class TmxReaderBase
|
|||
{
|
||||
// Attributes
|
||||
var name = _reader.GetRequiredAttribute("name");
|
||||
var @class = _reader.GetOptionalAttribute("class") ?? "";
|
||||
var @class = _reader.GetOptionalAttribute("class").GetValueOr("");
|
||||
var color = _reader.GetRequiredAttributeParseable<Color>("color");
|
||||
var tile = _reader.GetRequiredAttributeParseable<int>("tile");
|
||||
var probability = _reader.GetOptionalAttributeParseable<float>("probability") ?? 0f;
|
||||
var probability = _reader.GetOptionalAttributeParseable<float>("probability").GetValueOr(0f);
|
||||
|
||||
// Elements
|
||||
List<IProperty>? properties = null;
|
||||
List<IProperty> properties = null;
|
||||
|
||||
_reader.ProcessChildren("wangcolor", (r, elementName) => elementName switch
|
||||
{
|
||||
|
|
|
@ -34,25 +34,25 @@ public class Image
|
|||
/// <summary>
|
||||
/// The format of the image.
|
||||
/// </summary>
|
||||
public ImageFormat? Format { get; set; }
|
||||
public Optional<ImageFormat> Format { get; set; } = Optional<ImageFormat>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The reference to the image file.
|
||||
/// </summary>
|
||||
public string? Source { get; set; }
|
||||
public Optional<string> Source { get; set; } = Optional<string>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a specific color that is treated as transparent.
|
||||
/// </summary>
|
||||
public Color? TransparentColor { get; set; }
|
||||
public Optional<Color> TransparentColor { get; set; } = Optional<Color>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The image width in pixels, used for tile index correction when the image changes.
|
||||
/// </summary>
|
||||
public uint? Width { get; set; }
|
||||
public Optional<uint> Width { get; set; } = Optional<uint>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The image height in pixels, used for tile index correction when the image changes.
|
||||
/// </summary>
|
||||
public uint? Height { get; set; }
|
||||
public Optional<uint> Height { get; set; } = Optional<uint>.Empty;
|
||||
}
|
||||
|
|
|
@ -54,15 +54,15 @@ public class Tile : HasPropertiesBase
|
|||
/// <summary>
|
||||
/// The image representing this tile. Only used for tilesets that composed of a collection of images.
|
||||
/// </summary>
|
||||
public Image? Image { get; set; }
|
||||
public Optional<Image> Image { get; set; } = Optional<Image>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Unclear what this is for.
|
||||
/// Used when the tile contains e.g. collision information.
|
||||
/// </summary>
|
||||
public ObjectLayer? ObjectLayer { get; set; }
|
||||
public Optional<ObjectLayer> ObjectLayer { get; set; } = Optional<ObjectLayer>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The animation frames for this tile.
|
||||
/// </summary>
|
||||
public List<Frame>? Animation { get; set; }
|
||||
public List<Frame> Animation { get; set; } = [];
|
||||
}
|
||||
|
|
|
@ -98,27 +98,27 @@ public class Tileset : HasPropertiesBase
|
|||
/// <summary>
|
||||
/// The TMX format version. Is incremented to match minor Tiled releases.
|
||||
/// </summary>
|
||||
public string? Version { get; set; }
|
||||
public Optional<string> Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Tiled version used to save the file in case it was loaded from an external tileset file.
|
||||
/// </summary>
|
||||
public string? TiledVersion { get; set; }
|
||||
public Optional<string> TiledVersion { get; set; } = Optional<string>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The first global tile ID of this tileset (this global ID maps to the first tile in this tileset).
|
||||
/// </summary>
|
||||
public uint? FirstGID { get; set; }
|
||||
public Optional<uint> FirstGID { get; set; } = Optional<uint>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// If this tileset is stored in an external TSX (Tile Set XML) file, this attribute refers to that file.
|
||||
/// </summary>
|
||||
public string? Source { get; set; }
|
||||
public Optional<string> Source { get; set; } = Optional<string>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The name of this tileset.
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The class of this tileset.
|
||||
|
@ -128,32 +128,32 @@ public class Tileset : HasPropertiesBase
|
|||
/// <summary>
|
||||
/// The width of the tiles in this tileset, which should be at least 1 (non-zero) except in the case of image collection tilesets (in which case it stores the maximum tile width).
|
||||
/// </summary>
|
||||
public uint? TileWidth { get; set; }
|
||||
public required uint TileWidth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The height of the tiles in this tileset, which should be at least 1 (non-zero) except in the case of image collection tilesets (in which case it stores the maximum tile height).
|
||||
/// </summary>
|
||||
public uint? TileHeight { get; set; }
|
||||
public required uint TileHeight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The spacing in pixels between the tiles in this tileset (applies to the tileset image). Irrelevant for image collection tilesets.
|
||||
/// </summary>
|
||||
public float? Spacing { get; set; } = 0f;
|
||||
public uint Spacing { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The margin around the tiles in this tileset (applies to the tileset image). Irrelevant for image collection tilesets.
|
||||
/// </summary>
|
||||
public float? Margin { get; set; } = 0f;
|
||||
public uint Margin { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The number of tiles in this tileset.
|
||||
/// </summary>
|
||||
public uint? TileCount { get; set; }
|
||||
public required uint TileCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of tile columns in the tileset.
|
||||
/// </summary>
|
||||
public uint? Columns { get; set; }
|
||||
public required uint Columns { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controls the aligntment for tile objects.
|
||||
|
@ -173,17 +173,17 @@ public class Tileset : HasPropertiesBase
|
|||
/// <summary>
|
||||
/// If the tileset is based on a single image, which is cut into tiles based on the given attributes of the tileset, then this is that image.
|
||||
/// </summary>
|
||||
public Image? Image { get; set; }
|
||||
public Optional<Image> Image { get; set; } = Optional<Image>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// This is used to specify an offset in pixels, to be applied when drawing a tile from the related tileset. When not present, no offset is applied.
|
||||
/// </summary>
|
||||
public TileOffset? TileOffset { get; set; }
|
||||
public Optional<TileOffset> TileOffset { get; set; } = Optional<TileOffset>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Ths is only used in case of isometric orientation, and determines how tile overlays for terrain and collision information are rendered.
|
||||
/// </summary>
|
||||
public Grid? Grid { get; set; }
|
||||
public Optional<Grid> Grid { get; set; } = Optional<Grid>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Tileset properties.
|
||||
|
@ -198,12 +198,12 @@ public class Tileset : HasPropertiesBase
|
|||
/// <summary>
|
||||
/// Contains the list of Wang sets defined for this tileset.
|
||||
/// </summary>
|
||||
public List<Wangset>? Wangsets { get; set; }
|
||||
public List<Wangset> Wangsets { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Used to describe which transformations can be applied to the tiles (e.g. to extend a Wang set by transforming existing tiles).
|
||||
/// </summary>
|
||||
public Transformations? Transformations { get; set; }
|
||||
public Optional<Transformations> Transformations { get; set; } = Optional<Transformations>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// If this tileset is based on a collection of images, then this list of tiles will contain the individual images that make up the tileset.
|
||||
|
|
|
@ -34,7 +34,7 @@ public class Wangset : HasPropertiesBase
|
|||
/// <summary>
|
||||
/// The Wang colors in the Wang set.
|
||||
/// </summary>
|
||||
public List<WangColor>? WangColors { get; set; } = [];
|
||||
public List<WangColor> WangColors { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The Wang tiles in the Wang set.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue