import { render } from "preact"; import type { ReactElement } from "preact/compat"; import { clamp, range } from "@common/utils"; import clsx from "clsx"; import styles from './assets/brick.module.css'; import "./assets/lcd.font.css"; const FIELD_WIDTH = 10; const FIELD_HEIGHT = 20; const MINI_FIELD_WIDTH = 4; const MINI_FIELD_HEIGHT = 4; export interface BrickDisplayImage { image: boolean[]; width: number; height: number; } export class BrickDisplay { #field: boolean[] = new Array(FIELD_HEIGHT * FIELD_WIDTH); #miniField: boolean[] = new Array(MINI_FIELD_HEIGHT * MINI_FIELD_WIDTH); #score: number = 0; #speed: number = 1; #level: number = 1; public pause: boolean = false; public gameOver: boolean = false; public helpText: string | ReactElement = ''; init() { this.update(); } get score() { return this.#score; } set score(value) { this.#score = Math.max(0, (value | 0) % 1000000000); } get speed() { return this.#speed; } set speed(value) { this.#speed = clamp(value | 0, 1, 15); } get level() { return this.#level; } set level(value) { this.#level = clamp(value | 0, 1, 15); } get width() { return FIELD_WIDTH; } get height() { return FIELD_HEIGHT; } update() { render(this.#display, document.body); } setPixel(x: number, y: number, value: boolean, miniDisplay = false) { const w = miniDisplay ? MINI_FIELD_WIDTH : FIELD_WIDTH; const h = miniDisplay ? MINI_FIELD_HEIGHT : FIELD_HEIGHT; x = x | 0; y = y | 0; if (x < 0 || x >= w || y < 0 || y >= h) return; const field = miniDisplay ? this.#miniField : this.#field; field[y * w + x] = value; } getPixel(x: number, y: number, miniDisplay = false): boolean { const w = miniDisplay ? MINI_FIELD_WIDTH : FIELD_WIDTH; const h = miniDisplay ? MINI_FIELD_HEIGHT : FIELD_HEIGHT; x = x | 0; y = y | 0; if (x < 0 || x >= w || y < 0 || y >= h) return false; const field = miniDisplay ? this.#miniField : this.#field; return field[y * w + x]; } togglePixel(x: number, y: number, miniDisplay = false) { const w = miniDisplay ? MINI_FIELD_WIDTH : FIELD_WIDTH; const h = miniDisplay ? MINI_FIELD_HEIGHT : FIELD_HEIGHT; x = x | 0; y = y | 0; if (x < 0 || x >= w || y < 0 || y >= h) return; const field = miniDisplay ? this.#miniField : this.#field; field[y * w + x] = !field[y * w + x]; } clear(miniDisplay = false) { const length = miniDisplay ? this.#miniField.length : this.#field.length; const field = miniDisplay ? this.#miniField : this.#field; for (let i = 0; i < length; i++) { field[i] = false; } } drawVLine(x: number, y1: number, y2: number, value = true, miniDisplay = false) { const w = miniDisplay ? MINI_FIELD_WIDTH : FIELD_WIDTH; const h = miniDisplay ? MINI_FIELD_HEIGHT : FIELD_HEIGHT; if (x < 0 || x >= w) return; x = x | 0; y1 = y1 | 0; y2 = y2 | 0; if (y2 < y1) { const t = y2; y2 = y1; y1 = t; } if (y2 < 0 || y1 >= h) return; for (let y = Math.max(y1, 0); y <= Math.min(y2, h); y++) { this.setPixel(x, y, value, miniDisplay); } } drawHLine(x1: number, x2: number, y: number, value = true, miniDisplay = false) { const w = miniDisplay ? MINI_FIELD_WIDTH : FIELD_WIDTH; const h = miniDisplay ? MINI_FIELD_HEIGHT : FIELD_HEIGHT; if (y < 0 || y >= h) return; x1 = x1 | 0; x2 = x2 | 0; y = y | 0; if (x2 < x1) { const t = x2; x2 = x1; x1 = t; } if (x2 < 0 || x1 >= w) return; for (let x = Math.max(x1, 0); x <= Math.min(x2, w); x++) { this.setPixel(x, y, value, miniDisplay); } } drawRect(x1: number, y1: number, x2: number, y2: number, value = true, miniDisplay = false) { this.drawHLine(x1, x2, y1, value, miniDisplay); this.drawHLine(x1, x2, y2, value, miniDisplay); this.drawVLine(x1, y1, y2, value, miniDisplay); this.drawVLine(x2, y1, y2, value, miniDisplay); } fillRect(x1: number, y1: number, x2: number, y2: number, value = true, miniDisplay = false) { y1 = y1 | 0; y2 = y2 | 0; if (y2 < y1) { const t = y2; y2 = y1; y1 = t; } for (let y = Math.max(0, y1); y <= y2; y++) { this.drawHLine(x1, x2, y, value, miniDisplay); } } drawImage(image: BrickDisplayImage, x: number, y: number, miniDisplay = false, xor = false) { for (let j = 0; j < image.height; j++) { for (let i = 0; i < image.width; i++) { const px = image.image[j * image.width + i]; if (px) { if (xor) { this.togglePixel(x + i, y + j, miniDisplay); } else { this.setPixel(x + i, y + j, px, miniDisplay); } } } } } get #display() { return (