|
|
|
using Dictionary = Godot.Collections.Dictionary;
|
|
|
|
|
|
|
|
public readonly record struct TilePos(int X, int Y)
|
|
|
|
{
|
|
|
|
public TilePos GetNeighbor(Side side)
|
|
|
|
=> side switch {
|
|
|
|
Side.Left => new(X - 1, Y),
|
|
|
|
Side.Top => new(X, Y - 1),
|
|
|
|
Side.Right => new(X + 1, Y),
|
|
|
|
Side.Bottom => new(X, Y + 1),
|
|
|
|
_ => throw new ArgumentException($"Invalid Side value '{side}'", nameof(side)),
|
|
|
|
};
|
|
|
|
|
|
|
|
public TilePos GetNeighbor(Corner corner)
|
|
|
|
=> corner switch {
|
|
|
|
Corner.TopLeft => new(X - 1, Y - 1),
|
|
|
|
Corner.TopRight => new(X + 1, Y - 1),
|
|
|
|
Corner.BottomRight => new(X + 1, Y + 1),
|
|
|
|
Corner.BottomLeft => new(X - 1, Y + 1),
|
|
|
|
_ => throw new ArgumentException($"Invalid Corner value '{corner}'", nameof(corner)),
|
|
|
|
};
|
|
|
|
|
|
|
|
public static TilePos From(Vector2I value) => new(value.X, value.Y);
|
|
|
|
public Vector2I ToVector2I() => new(X, Y);
|
|
|
|
}
|
|
|
|
|
|
|
|
public struct Tile
|
|
|
|
{
|
|
|
|
public Corners<float> Height;
|
|
|
|
|
|
|
|
// TODO: Replace with enum or something more permanent?
|
|
|
|
public int TexturePrimary;
|
|
|
|
public int TextureSecondary;
|
|
|
|
public int TextureBlend;
|
|
|
|
|
|
|
|
|
|
|
|
public static Tile FromDictionary(Dictionary dict)
|
|
|
|
{
|
|
|
|
if (dict == null) return default;
|
|
|
|
|
|
|
|
float topLeft, topRight, bottomRight, bottomLeft;
|
|
|
|
switch (dict["heights"]) {
|
|
|
|
case { VariantType: Variant.Type.Float } variant:
|
|
|
|
var height = (float)variant;
|
|
|
|
(topLeft, topRight, bottomRight, bottomLeft) = (height, height, height, height);
|
|
|
|
break;
|
|
|
|
case { VariantType: Variant.Type.PackedFloat32Array } variant:
|
|
|
|
var heights = (float[])variant;
|
|
|
|
(topLeft, topRight, bottomRight, bottomLeft) = (heights[0], heights[1], heights[2], heights[3]);
|
|
|
|
break;
|
|
|
|
default: throw new Exception("Invalid variant type");
|
|
|
|
};
|
|
|
|
|
|
|
|
int texturePrimary, textureSecondary, textureBlend;
|
|
|
|
switch (dict["texture"]) {
|
|
|
|
case { VariantType: Variant.Type.Int } variant:
|
|
|
|
var texture = (int)variant;
|
|
|
|
(texturePrimary, textureSecondary, textureBlend) = (texture, 0, 0);
|
|
|
|
break;
|
|
|
|
case { VariantType: Variant.Type.PackedInt32Array } variant:
|
|
|
|
var textures = (int[])variant;
|
|
|
|
(texturePrimary, textureSecondary, textureBlend) = (textures[0], textures[1], textures[2]);
|
|
|
|
break;
|
|
|
|
default: throw new Exception("Invalid variant type");
|
|
|
|
};
|
|
|
|
|
|
|
|
return new(){
|
|
|
|
Height = new(topLeft, topRight, bottomRight, bottomLeft),
|
|
|
|
TexturePrimary = texturePrimary,
|
|
|
|
TextureSecondary = textureSecondary,
|
|
|
|
TextureBlend = textureBlend,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
public readonly Dictionary ToDictionary()
|
|
|
|
{
|
|
|
|
if (Height.IsZeroApprox() && (TexturePrimary == 0) && (TextureBlend == 0))
|
|
|
|
return null;
|
|
|
|
return new(){
|
|
|
|
["heights"] = Height.IsEqualApprox() ? Height.TopLeft : new[]{ Height.TopLeft, Height.TopRight, Height.BottomRight, Height.BottomLeft },
|
|
|
|
["texture"] = (TextureBlend == 0) ? TexturePrimary : new[]{ TexturePrimary, TextureSecondary, TextureBlend },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public struct Corners<T>(T topLeft, T topRight, T bottomRight, T bottomLeft)
|
|
|
|
{
|
|
|
|
public T TopLeft = topLeft;
|
|
|
|
public T TopRight = topRight;
|
|
|
|
public T BottomRight = bottomRight;
|
|
|
|
public T BottomLeft = bottomLeft;
|
|
|
|
|
|
|
|
public T this[Corner corner] {
|
|
|
|
readonly get => corner switch {
|
|
|
|
Corner.TopLeft => TopLeft,
|
|
|
|
Corner.TopRight => TopRight,
|
|
|
|
Corner.BottomRight => BottomRight,
|
|
|
|
Corner.BottomLeft => BottomLeft,
|
|
|
|
_ => throw new ArgumentException($"Invalid Corner value '{corner}'", nameof(corner)),
|
|
|
|
};
|
|
|
|
set { switch (corner) {
|
|
|
|
case Corner.TopLeft : TopLeft = value; break;
|
|
|
|
case Corner.TopRight : TopRight = value; break;
|
|
|
|
case Corner.BottomRight : BottomRight = value; break;
|
|
|
|
case Corner.BottomLeft : BottomLeft = value; break;
|
|
|
|
default: throw new ArgumentException($"Invalid Corner value '{corner}'", nameof(corner));
|
|
|
|
} }
|
|
|
|
}
|
|
|
|
|
|
|
|
public readonly void Deconstruct(
|
|
|
|
out T topLeft , out T topRight,
|
|
|
|
out T bottomRight, out T bottomLeft)
|
|
|
|
{
|
|
|
|
topLeft = TopLeft;
|
|
|
|
topRight = TopRight;
|
|
|
|
bottomRight = BottomRight;
|
|
|
|
bottomLeft = BottomLeft;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class CornersExtensions
|
|
|
|
{
|
|
|
|
public static void Adjust(this ref Corners<float> self, float amount)
|
|
|
|
{
|
|
|
|
self.TopLeft += amount;
|
|
|
|
self.TopRight += amount;
|
|
|
|
self.BottomRight += amount;
|
|
|
|
self.BottomLeft += amount;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static bool IsZeroApprox(this Corners<float> self)
|
|
|
|
=> Mathf.IsZeroApprox(self.TopLeft ) && Mathf.IsZeroApprox(self.TopRight )
|
|
|
|
&& Mathf.IsZeroApprox(self.BottomRight) && Mathf.IsZeroApprox(self.BottomLeft);
|
|
|
|
|
|
|
|
public static bool IsEqualApprox(this Corners<float> self)
|
|
|
|
=> Mathf.IsEqualApprox(self.TopLeft, self.TopRight )
|
|
|
|
&& Mathf.IsEqualApprox(self.TopLeft, self.BottomRight)
|
|
|
|
&& Mathf.IsEqualApprox(self.TopLeft, self.BottomLeft );
|
|
|
|
}
|