diff options
Diffstat (limited to 'client/src/renderer.ts')
-rw-r--r-- | client/src/renderer.ts | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/client/src/renderer.ts b/client/src/renderer.ts new file mode 100644 index 0000000..c7bbbc2 --- /dev/null +++ b/client/src/renderer.ts @@ -0,0 +1,320 @@ +import { getMap } from "./map.js"; +import { Items, Players, Rotation, ItemType, Map, Wall, GameState } from "./types.js"; + +const ATLAS_TILE_WIDTH = 32 + +const update_style = (width: number, height: number) => { + + let style = document.getElementById("style") + + const css = ` + * { + --scale: 100; + --aspect: ${width/height}; + --scaleX: calc(var(--scale) * 1vw); + --scaleY: calc(var(--scale) * 1vh); + } + + #canvas { + width: calc(var(--scaleY) * var(--aspect)); + height: var(--scaleY); + margin-top: calc((100vh - var(--scaleY))/2); + margin-left: calc(50vw - var(--scaleY)*var(--aspect)/2); + position: relative; + vertical-align: top; + line-height: 0; + } + + @media (max-aspect-ratio: ${width}/${height}) { + #canvas { + width: var(--scaleX); + height: calc(var(--scaleX) / var(--aspect)); + margin-left: calc((100vw - var(--scaleX))/2); + margin-top: calc(50vh - var(--scaleX)/var(--aspect)/2); + } + }`; + + style.innerHTML = css +} + +const draw_sprite = ( + ctx: CanvasRenderingContext2D, + x: number, + y: number, + width: number, + atlas: CanvasImageSource, + atlas_index: [number, number], + atlas_tile_width: number, + rotation: Rotation +) => { + ctx.save() + ctx.translate( + (x + 0.5) * ATLAS_TILE_WIDTH, + (y + 0.5) * ATLAS_TILE_WIDTH + ) + ctx.rotate(rotation * Math.PI / 180) + ctx.drawImage( + atlas, + atlas_index[0] * atlas_tile_width, + atlas_index[1] * atlas_tile_width, + atlas_tile_width, + atlas_tile_width, + -width * ATLAS_TILE_WIDTH / 2, + -width * ATLAS_TILE_WIDTH / 2, + width * ATLAS_TILE_WIDTH, + width * ATLAS_TILE_WIDTH + ) + ctx.restore() +} + +const draw_players = ( + ctx: CanvasRenderingContext2D, + atlas: CanvasImageSource, + players: Players, + frame: number +) => { + + let atlas_frames: [number, number][] = [ + [0, 2], + [1, 2], + [2, 2], + [0, 3], + [1, 3], + [0, 3], + [2, 2], + [1, 2], + ] + + for (let id in players) { + + let player = players[id] + if (!player) continue + + let atlas_index = atlas_frames[0] + if (player.moving) { + atlas_index = atlas_frames[Math.floor(frame / 2) % atlas_frames.length] + } + + let rotation: number + switch (player.moveRotation) { + case Rotation.NORTH: + rotation = 270 + break + case Rotation.SOUTH: + rotation = 90 + break + case Rotation.WEST: + rotation = 180 + break + case Rotation.EAST: + default: + rotation = 0 + break + } + + draw_sprite ( + ctx, + player.pos.x, + player.pos.y, + 1, + atlas, + atlas_index, + ATLAS_TILE_WIDTH, + rotation + ) + } +} + +const draw_items = ( + ctx: CanvasRenderingContext2D, + atlas: CanvasImageSource, + items: Items +) => { + + for (let item_key in items) { + + let item = items[item_key] + if (!item) continue + + let width: number, atlas_index: [number, number] + switch (item.type) { + case ItemType.DOT: + width = .2, + atlas_index = [2, 3] + break + default: + continue + } + + draw_sprite ( + ctx, + item.pos.x, + item.pos.y, + width, + atlas, + atlas_index, + ATLAS_TILE_WIDTH, + 0 + ) + + } + +} + +const draw_map_canvas = ( + ctx: CanvasRenderingContext2D, + atlas: CanvasImageSource, + map: Map +) => { + + for (let y = 0; y < map.height; y++) { + for (let x = 0; x < map.width; x++) { + + let wall_type = map.walls[y * map.width + x] + + let atlas_index: [number, number], rotation: number; + switch(wall_type) { + case Wall.EMPTY: + continue + case Wall.WALL_HZ: + atlas_index = [1, 1] + rotation = 0 + break + case Wall.WALL_VT: + atlas_index = [1, 1] + rotation = 90 + break + case Wall.TURN_Q1: + atlas_index = [2, 0] + rotation = 0 + break + case Wall.TURN_Q2: + atlas_index = [2, 0] + rotation = 270 + break + case Wall.TURN_Q3: + atlas_index = [2, 0] + rotation = 180 + break + case Wall.TURN_Q4: + atlas_index = [2, 0] + rotation = 90 + break + case Wall.TEE_NORTH: + atlas_index = [1, 0] + rotation = 180 + break + case Wall.TEE_EAST: + atlas_index = [1, 0] + rotation = 270 + break + case Wall.TEE_SOUTH: + atlas_index = [1, 0] + rotation = 0 + break + case Wall.TEE_WEST: + atlas_index = [1, 0] + rotation = 90 + break + case Wall.CROSS: + atlas_index = [0, 0] + rotation = 0 + break + case Wall.DOT: + atlas_index = [2, 1] + rotation = 0 + break + case Wall.WALL_END_NORTH: + atlas_index = [0, 1] + rotation = 0 + break; + case Wall.WALL_END_EAST: + atlas_index = [0, 1] + rotation = 90 + break; + case Wall.WALL_END_SOUTH: + atlas_index = [0, 1] + rotation = 180 + break; + case Wall.WALL_END_WEST: + atlas_index = [0, 1] + rotation = 270 + break; + } + + draw_sprite ( + ctx, + x, + y, + 1, + atlas, + atlas_index, + ATLAS_TILE_WIDTH, + rotation + ) + } + } + +} + +let map_canvas = document.createElement("canvas") +const draw_map = ( + ctx: CanvasRenderingContext2D, + atlas: CanvasImageSource, + map: Map, + last: number | undefined +) => { + + if (map.id !== last) { + map_canvas.width = map.width * ATLAS_TILE_WIDTH + map_canvas.height = map.height * ATLAS_TILE_WIDTH + + let map_ctx = map_canvas.getContext("2d") + draw_map_canvas(map_ctx, atlas, map) + } + + ctx.drawImage ( + map_canvas, + 0, + 0 + ) + +} + +let last_map_drawn: number | undefined +export const startGraphicsUpdater = () => { + + let canvas = document.getElementById("canvas") as HTMLCanvasElement + let atlas = document.getElementById("atlas") as HTMLImageElement + + /** + * @param {import("./logic").GameState} data + */ + return ( + data: GameState, + frame: number + ) => { + + let map = getMap(data.mapId) + + if (!map) return + + if (map.id !== last_map_drawn) { + canvas.style.display = "" + canvas.width = map.width * ATLAS_TILE_WIDTH + canvas.height = map.height * ATLAS_TILE_WIDTH + } + + let ctx = canvas.getContext("2d") + ctx.clearRect(0, 0, canvas.width, canvas.height) + + draw_map(ctx, atlas, map, last_map_drawn) + draw_items(ctx, atlas, data.items) + draw_players(ctx, atlas, data.players, frame) + update_style(map.width, map.height) + + last_map_drawn = map.id + + } + +} |