import { Map } from "./gfx/map.js"; // enum export const Key = { NOTHING: undefined, UP: 1, DOWN: 2, LEFT: 3, RIGHT: 4, } // enum export const Rotation = { NOTHING: undefined, NORTH: 1, EAST: 2, SOUTH: 3, WEST: 4 } /** * @typedef {[number, number]} Vec2 * * @typedef {{[key: number]: Key} InputMap * * @typedef {{pos: Vec2, move_rot: Rotation, input_rot: Rotation, name?: string}} Player * @typedef {{start: boolean, key: Key, name?: string}} PlayerInput * @typedef {{players: {[key: number]: PlayerInput}, added?: number[], removed?: number[] }} Input * * @typedef {{[key: number]: Player}} Players * * @typedef {{width: number, height: number, data: number[]}} Map * * @typedef {{ * started: boolean, * input: InputMap, * players: Players, * map: Map * }} GameState */ /** @type {GameState} */ export const initState = { started: false, input: {}, players: [], map: {} } export function advance( pastData = initState, input = { players: {} }, frame ) { let data = processInput(pastData, input, frame); return data; } /** * @param {GameState} pastData * @param {Input} input * @param {number} frame */ function processInput(pastData, input) { /** @type {GameState} */ let data = structuredClone(pastData) let startPressed = false; for(const added of input.added || []) { if (data.started || Object.keys(data.players).length >= 4) continue; console.log("added", added); data.input[added] ||= { pos: [1, 1], input_rot: Rotation.EAST, mov_rot: Rotation.EAST }; if(!(added in data.players)) { data.players[added] = structuredClone(data.input[added]) } } for(const id in input.players) { if(!input.players[id]) { continue; } if(id in data.players && input.players[id].name !== undefined) { let name = input.players[id].name; name = name.substring(0, 16); data.players[id] = { ...data.players[id], name, }; } startPressed ||= input.players[id].start; data.input[id] = input.players[id].dir } const player_display = document.getElementById("players") for (const id in data.players) { if (data.players[id] === null) continue let name = data.players[id].name if (name === undefined) continue let element_id = 'span' + id let element = player_display.children[element_id] if (element === null || element === undefined) { let span = document.createElement("span") span.innerHTML = `[${id}] ${name}` span.id = element_id player_display.appendChild(span) } } if (startPressed && !data.started) { init_map(data) data.started ||= startPressed; } if (data.started) { update_players(data) } for(const removed of input.removed || []) { console.log("removed", removed); delete data.input[removed]; delete data.players[removed]; let element_id = 'span' + removed let element = document.getElementById(element_id) if (element !== null && element !== undefined) element.remove() } return data } const init_map = (data) => { document.getElementById("lobby").style.display = "none" // let width = 13 // let height = 5 // let m_data = [ // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, // 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, // 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, // 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 // ] let width = 21 let height = 21 let m_data = [ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1, 1,0,1,1,1,0,1,1,1,0,1,0,1,1,1,0,1,1,1,0,1, 1,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1, 1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1, 1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1, 1,0,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,0,1, 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, 1,1,1,0,1,0,1,0,1,1,1,1,1,0,1,0,1,0,1,1,1, 1,0,0,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,1, 1,0,1,1,1,0,1,0,1,0,0,0,1,0,1,0,1,1,1,0,1, 1,0,0,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,1, 1,1,1,0,1,0,1,0,1,1,0,1,1,0,1,0,1,0,1,1,1, 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, 1,0,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,0,1, 1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1, 1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1, 1,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1, 1,0,1,1,1,0,1,1,1,0,1,0,1,1,1,0,1,1,1,0,1, 1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ] data.map = new Map(width, height, m_data) data.map.show() } const MOVE_SPEED = .1 const round_pos = (pos) => { return [Math.round(pos[0]), Math.round(pos[1])] } const is_stable_pos = (pos) => { let rpos = round_pos(pos) return Math.abs(rpos[0] - pos[0]) < MOVE_SPEED && Math.abs(rpos[1] - pos[1]) < MOVE_SPEED } const get_tile = (map, pos, ox, oy) => { let x = Math.round(pos[0] + ox) let y = Math.round(pos[1] + oy) if (x < 0 || x >= map.width || y < 0 || y >= map.height) return 1 return map.data[y * map.width + x] } const get_tile_with_rot = (map, pos, rot) => { let collider = 1 switch(rot) { case Rotation.NORTH: collider = get_tile(map, pos, 0, -.51) break case Rotation.SOUTH: collider = get_tile(map, pos, 0, .51) break case Rotation.WEST: collider = get_tile(map, pos, -.51, 0) break case Rotation.EAST: collider = get_tile(map, pos, .51, 0) break } return collider } const get_rot = (dir) => { switch (dir) { case Key.UP: return Rotation.NORTH case Key.DOWN: return Rotation.SOUTH case Key.LEFT: return Rotation.WEST case Key.RIGHT: return Rotation.EAST case Key.NOTHING: return Rotation.NOTHING } } const increment_pos = (pos, rot, speed) => { switch (rot) { case Rotation.NORTH: pos[1] -= speed break case Rotation.SOUTH: pos[1] += speed break case Rotation.WEST: pos[0] -= speed break case Rotation.EAST: pos[0] += speed break } } /** * @param {GameState} data */ const update_players = (data) => { for(const id in data.input) { // move players if(!(id in data.players)) { console.log("what. player undefined???", id); continue; } let input_key = data.input[id] let input_dir = get_rot(input_key) let move_dir = data.players[id].move_rot let current_pos = data.players[id].pos if (get_tile_with_rot(data.map, current_pos, input_dir) == 1) { input_dir = Rotation.NOTHING } let turning = input_dir != Key.NOTHING && input_dir != move_dir data.players[id].input_rot = input_dir if (turning && is_stable_pos(current_pos)) { current_pos = round_pos(current_pos) data.players[id].move_rot = input_dir move_dir = input_dir } let move_pos = structuredClone(current_pos) increment_pos(move_pos, move_dir, MOVE_SPEED) if (get_tile_with_rot(data.map, current_pos, move_dir) != 1) { data.players[id].pos = move_pos } else { data.players[id].pos = round_pos(current_pos) } } }