diff options
Diffstat (limited to 'client/js/gfx')
-rw-r--r-- | client/js/gfx/graphics.js | 56 | ||||
-rw-r--r-- | client/js/gfx/map.js | 261 | ||||
-rw-r--r-- | client/js/gfx/sprite.js | 60 |
3 files changed, 377 insertions, 0 deletions
diff --git a/client/js/gfx/graphics.js b/client/js/gfx/graphics.js new file mode 100644 index 0000000..e73927e --- /dev/null +++ b/client/js/gfx/graphics.js @@ -0,0 +1,56 @@ +import { Sprite } from './sprite.js' +import { Rotation } from '../logic.js' + + +export const startGraphicsUpdater = () => { + + let sprites = [] + + /** + * @type {(data: import("../logic.js").GameState) => void} + */ + return (data) => { + + if (!data.started) return + + let players = Object.keys(data.players).filter(k => data.players[k] !== undefined) + + if (sprites.length !== players.length) { + + for (const sprite of sprites) { + if (sprite !== undefined) { + sprite.destroy() + } + } + + sprites = Array(players) + sprites.fill(undefined) + + for (let id of players) { + let sprite = new Sprite("/static/tux.png", data.map) + sprite.show() + sprite.resize(1.5,1.5) + sprites[id] = sprite + } + } + + for (let id of players) { + let pos = data.players[id].pos + sprites[id].move(pos[0], pos[1]) + switch (data.players[id].move_rot) { + case Rotation.NORTH: + sprites[id].rotate(270) + break + case Rotation.EAST: + sprites[id].rotate(0) + break + case Rotation.SOUTH: + sprites[id].rotate(90) + break + case Rotation.WEST: + sprites[id].rotate(180) + break + } + } + } +} diff --git a/client/js/gfx/map.js b/client/js/gfx/map.js new file mode 100644 index 0000000..0ba7f9c --- /dev/null +++ b/client/js/gfx/map.js @@ -0,0 +1,261 @@ +const gen_style = (map, style) => { + const css = ` + * { + --scale: 100; + --aspect: ${map.width/map.height}; + --scaleX: calc(var(--scale) * 1vw); + --scaleY: calc(var(--scale) * 1vh); + } + + #container { + 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; + } + + #container img { + display: inline-block; + width: ${100/map.width}%; + height: ${100/map.height}%; + image-rendering: pixelated; + } + + @media (max-aspect-ratio: ${map.width}/${map.height}) { + #container { + 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 Direction = { + EMPTY: 0, + WALL_HZ: 1, + WALL_VT: 2, + TURN_Q1: 3, + TURN_Q2: 4, + TURN_Q3: 5, + TURN_Q4: 6, + TEE_NORTH: 7, + TEE_EAST: 8, + TEE_SOUTH: 9, + TEE_WEST: 10, + CROSS: 11, + DOT: 12, + WALL_END_NORTH: 13, + WALL_END_SOUTH: 14, + WALL_END_EAST: 15, + WALL_END_WEST: 16 +} + +const place_tile = (container, type) => { + + const img = document.createElement("img") + + let image_src, class_name; + switch(type) { + case Direction.EMPTY: + image_src = "/static/empty.png" + class_name = "" + break + case Direction.WALL_HZ: + image_src = "/static/wall.png" + class_name = "" + break + case Direction.WALL_VT: + image_src = "/static/wall.png" + class_name = "rotate90" + break + case Direction.TURN_Q1: + image_src = "/static/turn.png" + class_name = "" + break + case Direction.TURN_Q2: + image_src = "/static/turn.png" + class_name = "rotate270" + break + case Direction.TURN_Q3: + image_src = "/static/turn.png" + class_name = "rotate180" + break + case Direction.TURN_Q4: + image_src = "/static/turn.png" + class_name = "rotate90" + break + case Direction.TEE_NORTH: + image_src = "/static/tee.png" + class_name = "rotate180" + break + case Direction.TEE_EAST: + image_src = "/static/tee.png" + class_name = "rotate270" + break + case Direction.TEE_SOUTH: + image_src = "/static/tee.png" + class_name = "" + break + case Direction.TEE_WEST: + image_src = "/static/tee.png" + class_name = "rotate90" + break + case Direction.CROSS: + image_src = "/static/cross.png" + class_name = "" + break + case Direction.DOT: + image_src = "/static/dot.png" + class_name = "" + break + case Direction.WALL_END_NORTH: + image_src = "/static/wall_end.png" + class_name = "" + break; + case Direction.WALL_END_EAST: + image_src = "/static/wall_end.png" + class_name = "rotate90" + break; + case Direction.WALL_END_SOUTH: + image_src = "/static/wall_end.png" + class_name = "rotate180" + break; + case Direction.WALL_END_WEST: + image_src = "/static/wall_end.png" + class_name = "rotate270" + break; + } + + img.setAttribute("class", class_name) + img.setAttribute("src", image_src) + + container.appendChild(img) +} + +const get_point = (width, height, data, x, y) => { + if (x < 0 || x >= width || y < 0 || y >= height) { + return 0 + } else { + return data[y * width + x] + } +} + +const gen_walls = (width, height, data) => { + + let walls = Array(width * height) + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + + let north = get_point(width, height, data, x, y-1) + let south = get_point(width, height, data, x, y+1) + let east = get_point(width, height, data, x+1, y) + let west = get_point(width, height, data, x-1, y) + let current = get_point(width, height, data, x, y) + + let point = Direction.EMPTY + + if (!current) { + walls[y * width + x] = point + continue + } + + if (north && south && east && west) { + point = Direction.CROSS + } else if (east && west && north) { + point = Direction.TEE_NORTH + } else if (east && west && south) { + point = Direction.TEE_SOUTH + } else if (north && south && east) { + point = Direction.TEE_EAST + } else if (north && south && west) { + point = Direction.TEE_WEST + } else if (east && west) { + point = Direction.WALL_HZ + } else if (north && south) { + point = Direction.WALL_VT + } else if (west && south) { + point = Direction.TURN_Q1 + } else if (south && east) { + point = Direction.TURN_Q2 + } else if (east && north) { + point = Direction.TURN_Q3 + } else if (north && west) { + point = Direction.TURN_Q4 + } else if (north) { + point = Direction.WALL_END_NORTH + } else if (east) { + point = Direction.WALL_END_EAST + } else if (south) { + point = Direction.WALL_END_SOUTH + } else if (west) { + point = Direction.WALL_END_WEST + } else { + point = Direction.DOT + } + + walls[y * width + x] = point + + } + } + + return walls +} + +const gen_map = (map, container) => { + for (let y = 0; y < map.height; y++) { + for (let x = 0; x < map.width; x++) { + place_tile(container, map.walls[y * map.width + x]) + } + } + +} + +export class Map { + + constructor(width, height, data) { + + let last = document.getElementById("container") + if (last) last.remove() + + this.width = width + this.height = height + this.data = data + this.walls = gen_walls(width, height, data) + + } + + show() { + this.hide() + + let container = document.getElementById("container") + if (!container) { + container = document.createElement("div") + container.id = "container" + document.body.appendChild(container) + } + + gen_map(this, container) + + let style = document.getElementById("style") + if (!style) { + style = document.createElement("style") + style.id = "style" + document.body.appendChild(style) + } + + gen_style(this, style) + } + + hide() { + let container = document.getElementById("container") + if (container) container.remove() + let style = document.getElementById("style") + if (style) style.remove() + } +} diff --git a/client/js/gfx/sprite.js b/client/js/gfx/sprite.js new file mode 100644 index 0000000..1ed8136 --- /dev/null +++ b/client/js/gfx/sprite.js @@ -0,0 +1,60 @@ +export class Sprite { + + constructor(image_src, map) { + this.element = document.createElement("img") + this.element.src = image_src + this.element.className = "sprite" + document.getElementById("container").appendChild(this.element) + + this.map = map + this.x = 0 + this.y = 0 + this.w = 1 + this.h = 1 + this.d = 0 + this.hide() + } + + #update_pos() { + let width = 100 / this.map.width * this.w + let height = 100 / this.map.height * this.h + let left = 100 / this.map.width * (this.x + (1 - this.w) / 2) + let top = 100 / this.map.height * (this.y + (1 - this.h) / 2) + + this.element.style.width = `${width}%` + this.element.style.height = `${height}%` + this.element.style.left = `${left}%` + this.element.style.top = `${top}%` + this.element.style.transform = `rotate(${this.d}deg)` + } + + move(x, y) { + this.x = x + this.y = y + this.#update_pos() + } + + resize(w, h) { + this.w = w + this.h = h + this.#update_pos() + } + + rotate(d) { + this.d = d + this.#update_pos() + } + + hide() { + this.element.style.display = "none" + } + + show() { + this.element.style.display = "initial" + } + + destroy() { + this.element.remove() + } + +} |