using Content.Shared.Arcade; using System.Linq; namespace Content.Server.Arcade.BlockGame; public sealed partial class BlockGame { /// /// The set of types of game pieces that exist. /// Used as templates when creating pieces for the game. /// private readonly BlockGamePieceType[] _allBlockGamePieces; /// /// The set of types of game pieces that exist. /// Used to generate the templates used when creating pieces for the game. /// private enum BlockGamePieceType { I, L, LInverted, S, SInverted, T, O } /// /// The set of possible rotations for the game pieces. /// private enum BlockGamePieceRotation { North, East, South, West } /// /// A static extension for the rotations that allows rotating through the possible rotations. /// private static BlockGamePieceRotation Next(BlockGamePieceRotation rotation, bool inverted) { return rotation switch { BlockGamePieceRotation.North => inverted ? BlockGamePieceRotation.West : BlockGamePieceRotation.East, BlockGamePieceRotation.East => inverted ? BlockGamePieceRotation.North : BlockGamePieceRotation.South, BlockGamePieceRotation.South => inverted ? BlockGamePieceRotation.East : BlockGamePieceRotation.West, BlockGamePieceRotation.West => inverted ? BlockGamePieceRotation.South : BlockGamePieceRotation.North, _ => throw new ArgumentOutOfRangeException(nameof(rotation), rotation, null) }; } /// /// A static extension for the rotations that allows rotating through the possible rotations. /// private struct BlockGamePiece { /// /// Where all of the blocks that make up this piece are located relative to the origin of the piece. /// public Vector2i[] Offsets; /// /// The color of all of the blocks that make up this piece. /// private BlockGameBlock.BlockGameBlockColor _gameBlockColor; /// /// Whether or not the block should be able to rotate about its origin. /// public bool CanSpin; /// /// Generates a list of the positions of each block comprising this game piece in worldspace. /// /// The position of the game piece in worldspace. /// The rotation of the game piece in worldspace. public readonly Vector2i[] Positions(Vector2i center, BlockGamePieceRotation rotation) { return RotatedOffsets(rotation).Select(v => center + v).ToArray(); } /// /// Gets the relative position of each block comprising this piece given a rotation. /// /// The rotation to be applied to the local position of the blocks in this piece. private readonly Vector2i[] RotatedOffsets(BlockGamePieceRotation rotation) { var rotatedOffsets = (Vector2i[]) Offsets.Clone(); //until i find a better algo var amount = rotation switch { BlockGamePieceRotation.North => 0, BlockGamePieceRotation.East => 1, BlockGamePieceRotation.South => 2, BlockGamePieceRotation.West => 3, _ => 0 }; for (var i = 0; i < amount; i++) { for (var j = 0; j < rotatedOffsets.Length; j++) { rotatedOffsets[j] = rotatedOffsets[j].Rotate90DegreesAsOffset(); } } return rotatedOffsets; } /// /// Gets a list of all of the blocks comprising this piece in worldspace. /// /// The position of the game piece in worldspace. /// The rotation of the game piece in worldspace. public readonly BlockGameBlock[] Blocks(Vector2i center, BlockGamePieceRotation rotation) { var positions = Positions(center, rotation); var result = new BlockGameBlock[positions.Length]; var i = 0; foreach (var position in positions) { result[i++] = position.ToBlockGameBlock(_gameBlockColor); } return result; } /// /// Gets a list of all of the blocks comprising this piece in worldspace. /// Used to generate the held piece/next piece preview images. /// public readonly BlockGameBlock[] BlocksForPreview() { var xOffset = 0; var yOffset = 0; foreach (var offset in Offsets) { if (offset.X < xOffset) xOffset = offset.X; if (offset.Y < yOffset) yOffset = offset.Y; } return Blocks(new Vector2i(-xOffset, -yOffset), BlockGamePieceRotation.North); } /// /// Generates a game piece for a given type of game piece. /// See for the available options. /// /// The type of game piece to generate. public static BlockGamePiece GetPiece(BlockGamePieceType type) { //switch statement, hardcoded offsets return type switch { BlockGamePieceType.I => new BlockGamePiece { Offsets = new[] { new Vector2i(0, -1), new Vector2i(0, 0), new Vector2i(0, 1), new Vector2i(0, 2), }, _gameBlockColor = BlockGameBlock.BlockGameBlockColor.LightBlue, CanSpin = true }, BlockGamePieceType.L => new BlockGamePiece { Offsets = new[] { new Vector2i(0, -1), new Vector2i(0, 0), new Vector2i(0, 1), new Vector2i(1, 1), }, _gameBlockColor = BlockGameBlock.BlockGameBlockColor.Orange, CanSpin = true }, BlockGamePieceType.LInverted => new BlockGamePiece { Offsets = new[] { new Vector2i(0, -1), new Vector2i(0, 0), new Vector2i(-1, 1), new Vector2i(0, 1), }, _gameBlockColor = BlockGameBlock.BlockGameBlockColor.Blue, CanSpin = true }, BlockGamePieceType.S => new BlockGamePiece { Offsets = new[] { new Vector2i(0, -1), new Vector2i(1, -1), new Vector2i(-1, 0), new Vector2i(0, 0), }, _gameBlockColor = BlockGameBlock.BlockGameBlockColor.Green, CanSpin = true }, BlockGamePieceType.SInverted => new BlockGamePiece { Offsets = new[] { new Vector2i(-1, -1), new Vector2i(0, -1), new Vector2i(0, 0), new Vector2i(1, 0), }, _gameBlockColor = BlockGameBlock.BlockGameBlockColor.Red, CanSpin = true }, BlockGamePieceType.T => new BlockGamePiece { Offsets = new[] { new Vector2i(0, -1), new Vector2i(-1, 0), new Vector2i(0, 0), new Vector2i(1, 0), }, _gameBlockColor = BlockGameBlock.BlockGameBlockColor.Purple, CanSpin = true }, BlockGamePieceType.O => new BlockGamePiece { Offsets = new[] { new Vector2i(0, -1), new Vector2i(1, -1), new Vector2i(0, 0), new Vector2i(1, 0), }, _gameBlockColor = BlockGameBlock.BlockGameBlockColor.Yellow, CanSpin = false }, _ => new BlockGamePiece { Offsets = new[] { new Vector2i(0, 0) } }, }; } } }