import { defineComponent } from "../../src/component"; import type { World } from "../../src/index"; import type { CommandQueue } from "../../src/commands/index"; import { Board, Piece, Score, GameOver, Paused, TickTimer, createPieceHelpers, } from "./components"; import { collides, lockPiece, clearLines, scoreForLines, tryRotate, } from "./game"; // ── Command definitions ────────────────────────────── /** Move the active piece left. */ export const MoveLeft = defineComponent("moveLeft", {}); /** Move the active piece right. */ export const MoveRight = defineComponent("moveRight", {}); /** Rotate the active piece clockwise. */ export const Rotate = defineComponent("rotate", {}); /** Soft drop — move piece down one row immediately. */ export const SoftDrop = defineComponent("softDrop", {}); /** Hard drop — slam piece to the bottom instantly. */ export const HardDrop = defineComponent("hardDrop", {}); /** Pause / unpause the game. */ export const TogglePause = defineComponent("togglePause", {}); /** Restart after game over. */ export const Restart = defineComponent("restart", {}); // ── Command handlers ───────────────────────────────── export function registerCommands( world: World, commands: CommandQueue, pieces: ReturnType, ): void { const hasActivePiece = () => world.hasSingleton(Piece); const isBlocked = () => world.hasSingleton(GameOver) || world.hasSingleton(Paused); commands.handle(MoveLeft, () => { if (!hasActivePiece() || isBlocked()) return; const piece = world.getSingleton(Piece); const board = world.getSingleton(Board); if (!collides(board.grid, piece.shape, piece.x - 1, piece.y)) { piece.x--; } }); commands.handle(MoveRight, () => { if (!hasActivePiece() || isBlocked()) return; const piece = world.getSingleton(Piece); const board = world.getSingleton(Board); if (!collides(board.grid, piece.shape, piece.x + 1, piece.y)) { piece.x++; } }); commands.handle(Rotate, () => { if (!hasActivePiece() || isBlocked()) return; const piece = world.getSingleton(Piece); const board = world.getSingleton(Board); const result = tryRotate(board.grid, piece.shape, piece.x, piece.y); if (result) { piece.shape = result.shape; piece.x = result.x; } }); commands.handle(SoftDrop, () => { if (!hasActivePiece() || isBlocked()) return; const piece = world.getSingleton(Piece); const board = world.getSingleton(Board); if (!collides(board.grid, piece.shape, piece.x, piece.y + 1)) { piece.y++; } }); commands.handle(HardDrop, () => { if (!hasActivePiece() || isBlocked()) return; const piece = world.getSingleton(Piece); const board = world.getSingleton(Board); while (!collides(board.grid, piece.shape, piece.x, piece.y + 1)) { piece.y++; } pieces.lockAndSpawn(); }); commands.handle(TogglePause, () => { if (world.hasSingleton(GameOver)) return; if (world.hasSingleton(Paused)) { world.removeSingleton(Paused); } else { world.addSingleton(Paused); } }); commands.handle(Restart, () => { if (!world.hasSingleton(GameOver)) return; const board = world.getSingleton(Board); for (let r = 0; r < board.grid.length; r++) { board.grid[r].fill(0); } world.setSingleton(Score, { points: 0, lines: 0, level: 1 }); world.setSingleton(TickTimer, { accumulator: 0, interval: 800 }); world.removeSingleton(GameOver); if (world.hasSingleton(Piece)) world.removeSingleton(Piece); pieces.spawnPiece(); }); }