1
0
Fork 0
tsgames/src/game/renderer.ts

110 lines
3.9 KiB
TypeScript

import { type Tile, TileType, getPortDirections, PortDirection, LIMITS, type Resource } from "./world";
import { ALL_DIRECTIONS, Direction, makeImage, movePoint } from "./utils";
import emptySrc from '../assets/img/empty.png';
import extractorSrc from '../assets/img/extractor.png';
import notSrc from '../assets/img/not.png';
import andSrc from '../assets/img/and.png';
import orSrc from '../assets/img/or.png';
export type Renderer<T extends Tile> = (ctx: CanvasRenderingContext2D, tile: T) => void;
export type NullRenderer = (ctx: CanvasRenderingContext2D) => void;
type Renderers = {
[K in Tile['type']]?: Renderer<Extract<Tile, { type: K }>>
}
const emptyImage = makeImage(emptySrc);
const extractorImage = makeImage(extractorSrc);
const notImage = makeImage(notSrc);
const andImage = makeImage(andSrc);
const orImage = makeImage(orSrc);
export const renderResource = (ctx: CanvasRenderingContext2D, tile: Tile, resource?: Resource) => {
const resources: [Direction, Resource | undefined][] = resource
? [[Direction.NONE, resource]]
: [...ALL_DIRECTIONS, Direction.NONE].map((d) => [d, tile.inv[d]]);
const oldStyle = ctx.fillStyle;
const px = 1 / 32;
let wasOtherDrawn = false;
for (const [direction, res] of resources) {
if (direction === Direction.NONE && wasOtherDrawn) continue;
if (res) {
const str = res.toString(2);
const amount = (tile.animationTimer ?? 0) / LIMITS[tile.type].cooldown;
const [x, y] = movePoint([0.5, 0.65], direction, amount);
ctx.fillStyle = 'white';
ctx.fillText(str, x - px, y - px);
ctx.fillText(str, x - px, y + px);
ctx.fillText(str, x + px, y - px);
ctx.fillText(str, x + px, y + px);
ctx.fillStyle = oldStyle;
ctx.fillText(str, x, y);
wasOtherDrawn = true;
}
}
}
export const renderPorts = (ctx: CanvasRenderingContext2D, tile: Tile) => {
if (tile.type === TileType.DESTINATION || tile.type === TileType.SOURCE) {
return;
}
for (const direction of getPortDirections(tile.ports)) {
const portDirection = tile.ports[direction]?.direction;
if (portDirection === PortDirection.INPUT) {
ctx.fillStyle = 'lightgreen';
} else if (portDirection === PortDirection.OUTPUT) {
ctx.fillStyle = 'red';
}
if (direction === Direction.NORTH) {
ctx.fillRect(0.2, 0, 0.6, 0.2);
} else if (direction === Direction.SOUTH) {
ctx.fillRect(0.2, 0.8, 0.6, 0.2);
} else if (direction === Direction.WEST) {
ctx.fillRect(0, 0.2, 0.2, 0.6);
} else if (direction === Direction.EAST) {
ctx.fillRect(0.8, 0.2, 0.2, 0.6);
}
}
}
export const defaultRenderer = (ctx: CanvasRenderingContext2D) => {
ctx.drawImage(emptyImage, 0, 0, 1, 1);
}
const imageRenderer = <T extends Tile>(image: HTMLImageElement): Renderer<T> => (ctx, tile) => {
ctx.drawImage(image, 0, 0, 1, 1);
}
export const renderers: Renderers = {
[TileType.SOURCE]: (ctx, tile) => {
ctx.fillStyle = '#bbffff7f';
ctx.fillRect(0, 0, 1, 1);
ctx.fillStyle = 'black';
renderResource(ctx, tile, tile.resource);
},
[TileType.DESTINATION]: (ctx, tile) => {
if (tile.center) {
ctx.fillStyle = '#bbffbb';
ctx.fillRect(-2, -2, 5, 5);
ctx.fillStyle = 'black';
ctx.fillText('Deploy', 0.5, 0.65);
}
},
[TileType.EXTRACTOR]: (ctx, tile) => {
renderers[TileType.SOURCE]?.(ctx, tile.source);
ctx.drawImage(extractorImage, 0, 0, 1, 1);
},
[TileType.CONVEYOR]: (ctx, tile) => {
ctx.fillStyle = 'lightgray';
ctx.fillRect(0.2, 0.2, 0.6, 0.6);
},
[TileType.NOT]: imageRenderer(notImage),
[TileType.AND]: imageRenderer(andImage),
[TileType.OR]: imageRenderer(orImage),
};