1
0
Fork 0
tsgames/src/games/binario/renderer.ts

122 lines
4.5 KiB
TypeScript

import { type Tile, TileType, getPortDirections, PortDirection, type Resource, getTileOutput, ResourceType } from "./world";
import { ALL_DIRECTIONS, Direction, movePoint } from "./utils";
import emptyImage from './assets/img/empty.png';
import extractorImage from './assets/img/extractor.png';
import notImage from './assets/img/not.png';
import andImage from './assets/img/and.png';
import orImage from './assets/img/or.png';
export interface ViewConfig {
tileSize: number;
}
export type Renderer<T extends Tile> = (ctx: CanvasRenderingContext2D, view: ViewConfig, tile: T) => void;
export type NullRenderer = (ctx: CanvasRenderingContext2D, view: ViewConfig) => void;
type Renderers = {
[K in Tile['type']]?: Renderer<Extract<Tile, { type: K }>>
}
export const renderResource = (ctx: CanvasRenderingContext2D, view: ViewConfig, tile: Tile) => {
let resources: [Direction, Resource | undefined][] | undefined;
if (tile.type === TileType.SOURCE) {
resources = [[Direction.NONE, tile.resource]]
} else if (tile.type === TileType.EXTRACTOR) {
resources = [[Direction.NONE, tile.source.resource]]
} else if ((tile.timer ?? 0) <= 0 && tile.type !== TileType.DESTINATION) { // when resource was precessed by machine
const [output] = getTileOutput(tile);
if (output && (tile.timer ?? 0) <= 0) {
resources = [[Direction.NONE, output]];
}
}
if (!resources) {
resources = [...ALL_DIRECTIONS, Direction.NONE].map((d) => [d, tile.inv[d]]);
}
const fontScale = Math.max(Math.pow(32 / view.tileSize, 1.5), 1);
const px = 1 / 32;
let wasOtherDrawn = false;
for (const [direction, res] of resources) {
if (direction === Direction.NONE && wasOtherDrawn) continue;
if (res?.type === ResourceType.NUMBER) {
const str = res.value.toString(2);
const [timer, timerMax] = tile.inputAnimations?.get(direction) ?? [0, 1];
const amount = timer / timerMax;
const [x, y] = movePoint([0.5, 0.52], direction, amount);
ctx.strokeStyle = 'white';
ctx.lineWidth = px * 2;
ctx.miterLimit = 2;
if (tile.type === TileType.SOURCE) {
ctx.font = `${0.4 * fontScale}px "IBM VGA 8x16"`;
}
ctx.strokeText(str, x, y);
ctx.fillText(str, x, y);
wasOtherDrawn = true;
}
}
}
export const renderPorts = (ctx: CanvasRenderingContext2D, _view: ViewConfig, 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, view, tile) => {
ctx.fillStyle = '#bbffff7f';
ctx.fillRect(0, 0, 1, 1);
ctx.fillStyle = 'black';
renderResource(ctx, view, tile);
},
[TileType.DESTINATION]: (ctx, _view, 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, view, tile) => {
renderers[TileType.SOURCE]?.(ctx, view, tile.source);
ctx.drawImage(extractorImage, 0, 0, 1, 1);
},
[TileType.CONVEYOR]: (ctx, _view, _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),
};