namespace ActionBlocks.Game.Core; /// /// Shape data for all pieces. Coordinates are relative to the piece's bounding box, y grows downwards. /// Rotation states are derived by rotating the spawn shape inside its box, which keeps the mino /// order stable across rotations (needed to track which mino carries an item). /// public static class PieceDefinitions { public const int RotationCount = 4; private static readonly Dictionary SpawnShapes = new() { [PieceType.I] = (4, [new(0, 1), new(1, 1), new(2, 1), new(3, 1)]), [PieceType.J] = (3, [new(0, 0), new(0, 1), new(1, 1), new(2, 1)]), [PieceType.L] = (3, [new(2, 0), new(0, 1), new(1, 1), new(2, 1)]), [PieceType.O] = (2, [new(0, 0), new(1, 0), new(0, 1), new(1, 1)]), [PieceType.S] = (3, [new(1, 0), new(2, 0), new(0, 1), new(1, 1)]), [PieceType.Z] = (3, [new(0, 0), new(1, 0), new(1, 1), new(2, 1)]), [PieceType.T] = (3, [new(1, 0), new(0, 1), new(1, 1), new(2, 1)]), [PieceType.Dot] = (1, [new(0, 0)]), [PieceType.SmallL] = (2, [new(0, 0), new(0, 1), new(1, 1)]), [PieceType.U] = (3, [new(0, 0), new(2, 0), new(0, 1), new(1, 1), new(2, 1)]), [PieceType.Plus] = (3, [new(1, 0), new(0, 1), new(1, 1), new(2, 1), new(1, 2)]), [PieceType.W] = (3, [new(0, 0), new(0, 1), new(1, 1), new(1, 2), new(2, 2)]), }; private static readonly Dictionary Rotations = BuildRotations(); public static IReadOnlyList All { get; } = Enum.GetValues(); /// The seven four-cell shapes dealt by the randomizer. public static IReadOnlyList Standard { get; } = All.Where(t => t <= PieceType.T).ToArray(); /// Oddball shapes that only appear while a player is transformed. public static IReadOnlyList Odd { get; } = All.Where(t => t > PieceType.T).ToArray(); public static int BoxSize(PieceType type) => SpawnShapes[type].BoxSize; /// Mino offsets of a piece in the given rotation state (0 = spawn, 1 = R, 2 = 180, 3 = L). public static IReadOnlyList Minos(PieceType type, int rotation) => Rotations[type][Mod(rotation)]; public static int Mod(int rotation) => ((rotation % RotationCount) + RotationCount) % RotationCount; private static Dictionary BuildRotations() { var result = new Dictionary(); foreach (var (type, (size, spawn)) in SpawnShapes) { var states = new Point[RotationCount][]; states[0] = spawn; for (var r = 1; r < RotationCount; r++) { // Clockwise rotation inside an n x n box: (x, y) -> (n - 1 - y, x) states[r] = states[r - 1].Select(p => new Point(size - 1 - p.Y, p.X)).ToArray(); } result[type] = states; } return result; } }