diff --git a/src/common/display/brick.tsx b/src/common/display/brick.tsx index 1f208f7..3d64bed 100644 --- a/src/common/display/brick.tsx +++ b/src/common/display/brick.tsx @@ -1,7 +1,6 @@ -import { render } from "preact"; -import type { ReactElement } from "preact/compat"; import { clamp, range } from "@common/utils"; import clsx from "clsx"; +import { render, type ComponentChildren } from "preact"; import styles from './assets/brick.module.css'; import "./assets/lcd.font.css"; @@ -12,10 +11,16 @@ 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 BrickDisplayImage { + public image: boolean[]; + public width: number; + public height: number; + + constructor(image: boolean[] = [], width: number = 0, height: number = 0) { + this.image = image; + this.width = width; + this.height = height; + } } export class BrickDisplay { @@ -26,7 +31,7 @@ export class BrickDisplay { #level: number = 1; public pause: boolean = false; public gameOver: boolean = false; - public helpText: string | ReactElement = ''; + public helpText: ComponentChildren = ''; init() { this.update(); @@ -37,7 +42,7 @@ export class BrickDisplay { } set score(value) { - this.#score = Math.max(0, (value | 0) % 1000000000); + this.#score = Math.max(0, (value | 0) % 1_000_000_000); } get speed() { @@ -243,11 +248,7 @@ export class BrickDisplay { } static convertImage(image: HTMLImageElement): BrickDisplayImage { - const result: BrickDisplayImage = { - image: [], - width: 0, - height: 0, - } + const result = new BrickDisplayImage(); const canvas = document.createElement('canvas'); result.width = canvas.width = image.naturalWidth; @@ -267,7 +268,7 @@ export class BrickDisplay { static extractSprite(image: BrickDisplayImage, x: number, y: number, w: number, h: number): BrickDisplayImage { if (w <= 0 || h <= 0 || x >= image.width || y >= image.height) { - return { image: [], width: 0, height: 0 }; + return new BrickDisplayImage(); } x = clamp(x | 0, 0, image.width); @@ -276,11 +277,7 @@ export class BrickDisplay { w = clamp(w | 0, 1, image.width - x); h = clamp(h | 0, 1, image.height - y); - const result: BrickDisplayImage = { - image: new Array(w * h), - width: w, - height: h, - } + const result = new BrickDisplayImage(new Array(w * h), w, h); for (let j = 0; j < h; j++) { for (let i = 0; i < w; i++) { @@ -320,11 +317,11 @@ export class BrickDisplay { static rotateSprite(image: BrickDisplayImage, angle: 0 | 90 | 180 | 270): BrickDisplayImage { if (angle === 0) return this.copySprite(image); - const newImage: BrickDisplayImage = { - image: new Array(image.width * image.height), - width: angle === 180 ? image.width : image.height, - height: angle === 180 ? image.height : image.width, - } + const newImage = new BrickDisplayImage( + new Array(image.width * image.height), + angle === 180 ? image.width : image.height, + angle === 180 ? image.height : image.width, + ); for (let j = 0; j < image.height; j++) { for (let i = 0; i < image.width; i++) { @@ -351,4 +348,4 @@ export class BrickDisplay { return newImage; } -} \ No newline at end of file +} diff --git a/src/common/rpg/components/render/brick.ts b/src/common/rpg/components/render/brick.ts new file mode 100644 index 0000000..36638f6 --- /dev/null +++ b/src/common/rpg/components/render/brick.ts @@ -0,0 +1,9 @@ +import { Component } from "@common/rpg/core/world"; +import { component } from "@common/rpg/utils/decorators"; + +@component +export class BrickSprite extends Component<{ xor: boolean, miniDisplay: boolean }> { + constructor(miniDisplay: boolean, xor: boolean) { + super({ xor, miniDisplay }); + } +} diff --git a/src/common/rpg/systems/render/brick.ts b/src/common/rpg/systems/render/brick.ts new file mode 100644 index 0000000..6630b74 --- /dev/null +++ b/src/common/rpg/systems/render/brick.ts @@ -0,0 +1,32 @@ +import { BrickDisplay, BrickDisplayImage } from "@common/display/brick"; +import { Position } from "@common/rpg/components/position"; +import { BrickSprite } from "@common/rpg/components/render/brick"; +import { Hidden, Sprite } from "@common/rpg/components/sprite"; +import { System, World } from "@common/rpg/core/world"; +import { Resources } from "@common/rpg/utils/resources"; + +export class BrickDisplaySystem extends System { + public readonly display: BrickDisplay; + + constructor(display?: BrickDisplay) { + super(); + this.display = display ?? new BrickDisplay(); + } + + override update(world: World) { + const sprites = Array.from(world.query(Sprite, Position)).sort((a, b) => a[2].state.z - b[2].state.z); + for (const [e, sprite, pos] of sprites) { + if (e.has(Hidden)) continue; + + const { xor = false, miniDisplay = false } = e.get(BrickSprite)?.state ?? {}; + + const { x, y } = pos.state; + + const data = Resources.get(BrickDisplayImage, sprite.image); + if (!data) { + throw new Error('No image data found for sprite'); + } + this.display.drawImage(data, x, y, miniDisplay, xor); + } + } +} \ No newline at end of file