122 lines
4.5 KiB
TypeScript
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),
|
|
}; |