84 lines
2.6 KiB
TypeScript
84 lines
2.6 KiB
TypeScript
import { defineComponent } from "../../src/component";
|
||
import type { World } from "../../src/index";
|
||
import {
|
||
randomPiece,
|
||
collides,
|
||
lockPiece,
|
||
clearLines,
|
||
scoreForLines,
|
||
BOARD_W,
|
||
} from "./game";
|
||
|
||
// ── Component definitions ────────────────────────────
|
||
|
||
/** The playfield grid (20 rows × 10 cols). 0 = empty, non-zero = color index. */
|
||
export const Board = defineComponent("board", {
|
||
grid: Array.from({ length: 20 }, () => new Uint8Array(10)) as Uint8Array[],
|
||
});
|
||
|
||
// ── Active piece ─────────────────────────────────────
|
||
export const Piece = defineComponent("piece", {
|
||
shape: [] as number[][],
|
||
color: 1,
|
||
x: 3,
|
||
y: 0,
|
||
});
|
||
|
||
// ── Score / state ────────────────────────────────────
|
||
export const Score = defineComponent("score", {
|
||
points: 0,
|
||
lines: 0,
|
||
level: 1,
|
||
});
|
||
|
||
export const GameOver = defineComponent("gameOver", {});
|
||
export const Paused = defineComponent("paused", {});
|
||
|
||
// ── Timing ───────────────────────────────────────────
|
||
export const TickTimer = defineComponent("tickTimer", {
|
||
accumulator: 0,
|
||
interval: 800, // ms between gravity ticks
|
||
});
|
||
|
||
// ── Piece helpers ────────────────────────────────────
|
||
|
||
export function createPieceHelpers(world: World) {
|
||
return {
|
||
spawnPiece(): void {
|
||
const p = randomPiece();
|
||
world.addSingleton(Piece, {
|
||
shape: p.shape,
|
||
color: p.color,
|
||
x: Math.floor((BOARD_W - p.shape[0].length) / 2),
|
||
y: 0,
|
||
});
|
||
},
|
||
|
||
lockAndSpawn(): void {
|
||
const piece = world.getSingleton(Piece);
|
||
const board = world.getSingleton(Board);
|
||
|
||
lockPiece(board.grid, piece.shape, piece.color, piece.x, piece.y);
|
||
world.removeSingleton(Piece);
|
||
|
||
const cleared = clearLines(board.grid);
|
||
if (cleared > 0) {
|
||
const score = world.getSingleton(Score);
|
||
score.lines += cleared;
|
||
score.points += scoreForLines(cleared, score.level);
|
||
score.level = Math.floor(score.lines / 10) + 1;
|
||
const timer = world.getSingleton(TickTimer);
|
||
timer.interval = Math.max(100, 800 - (score.level - 1) * 70);
|
||
}
|
||
|
||
this.spawnPiece();
|
||
|
||
const newPiece = world.getSingleton(Piece);
|
||
if (collides(board.grid, newPiece.shape, newPiece.x, newPiece.y)) {
|
||
world.removeSingleton(Piece);
|
||
world.addSingleton(GameOver);
|
||
}
|
||
},
|
||
};
|
||
}
|