110 lines
3.9 KiB
TypeScript
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),
|
|
}; |