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 = (ctx: CanvasRenderingContext2D, tile: T) => void; export type NullRenderer = (ctx: CanvasRenderingContext2D) => void; type Renderers = { [K in Tile['type']]?: Renderer> } 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 = (image: HTMLImageElement): Renderer => (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), };