/** * The map: this game's one grid, and what its letters mean. * * Two sites (A left, B right), a mid corridor between the spawns, and a back * corridor along the bottom that links A main, spawn and B main — the flank. * Tiles are 80 units; a player is a circle of radius 16, so a one-tile * corridor is comfortably wide and a wall is never thin enough for a ray to * step over. * * The mechanics — collision, line of sight, the walk distance a bot uses to * path — are `engine/grid.ts`, bound here to these rows. What is this * game's own is the rows themselves and the reading of their letters: a * plant site, a spawn, where the orbs sit. */ import { makeGrid, segmentHitsCircle, segmentPoint, type Point } from "./engine/grid.ts"; export { segmentHitsCircle, segmentPoint, type Point }; export const TILE = 80; // '#' wall · '.' floor · 'A'/'B' plant site · 'a' attacker spawn · 'd' defender spawn const ROWS_SRC = [ "##############################", "#........###.dddd.###........#", "#.AAAAA..###.dddd.###..BBBBB.#", "#.AAAAA..##........##..BBBBB.#", "#.AAAAA..##.#....#.##..BBBBB.#", "#.AAAAA..##........##..BBBBB.#", "#...##......#....#......##...#", "#..####....#......#....####..#", "#..####..###......###..####..#", "#..####..####....####..####..#", "#..####..####.##.####..####..#", "#........####.##.####........#", "#..####..####....####..####..#", "#..####..#####..#####..####..#", "#..####..#####..#####..####..#", "#..####..###......###..####..#", "#........##..aaaa..##........#", "#............aaaa............#", "##############################", ]; const board = makeGrid(ROWS_SRC, TILE); export const COLS = board.cols; export const ROWS = board.rows; export const WIDTH = board.width; export const HEIGHT = board.height; export const grid: string[] = ROWS_SRC; export const gridRows = (): string[] => ROWS_SRC.slice(); export type Site = "A" | "B"; export const solidTile = board.solidTile; export const tileOf = board.tileOf; export const centreOf = board.centreOf; export const solidAt = board.solidAt; export const moveCircle = board.moveCircle; export const blockedCircle = board.blockedCircle; export const wallBetween = board.wallBetween; export const rayHitWall = board.rayHitWall; export const walkDistance = board.walkDistance; export const pathStep = board.pathStep; export const clearPaths = board.clearPaths; /** The site a point plants on, or null: the spike only arms inside a site. */ export const siteAt = (x: number, y: number): Site | null => { const c = board.cellAt(x, y); return c === "A" || c === "B" ? c : null; }; export const spawnCells = (side: "atk" | "def"): Point[] => board.cellsOf(side === "atk" ? "a" : "d"); export const siteCells = (site: Site): Point[] => board.cellsOf(site); export const siteCentre = (site: Site): Point => { const cells = siteCells(site); const sum = cells.reduce((a, p) => ({ x: a.x + p.x, y: a.y + p.y }), { x: 0, y: 0 }); return { x: sum.x / cells.length, y: sum.y / cells.length }; }; /** Ult orbs: one on each half of mid, free for whoever walks over them. */ export const orbSpots = (): Point[] => [centreOf(4, 11), centreOf(25, 11)];