tuxman/client/js/logic.js

333 lines
8.3 KiB
JavaScript
Raw Normal View History

2023-06-14 01:18:01 +00:00
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
}
2023-06-15 03:22:26 +00:00
export const ItemType = {
DOT: 1
}
2023-06-14 01:18:01 +00:00
/**
* @typedef {[number, number]} Vec2
*
2023-06-15 03:22:26 +00:00
* @typedef {{[key: number]: Key}} InputMap
2023-06-14 01:18:01 +00:00
*
* @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
*
2023-06-15 03:22:26 +00:00
* @typedef {{type: ItemType, pos: Vec2}} Item
*
2023-06-14 01:18:01 +00:00
* @typedef {{
* started: boolean,
* input: InputMap,
* players: Players,
2023-06-15 03:22:26 +00:00
* map: Map,
2023-06-15 03:56:28 +00:00
* items_removed: Item[]
2023-06-14 01:18:01 +00:00
* }} GameState
*/
/** @type {GameState} */
export const initState = {
started: false,
input: {},
players: [],
2023-06-15 03:56:28 +00:00
map: {}
2023-06-14 01:18:01 +00:00
}
2023-06-15 03:22:26 +00:00
let last = Date.now()
let fps_div = document.getElementById("fps")
2023-06-15 03:56:28 +00:00
let frameCount = 0
2023-06-15 03:22:26 +00:00
2023-06-14 01:18:01 +00:00
export function advance(
pastData = initState,
input = { players: {} },
frame
) {
let data = processInput(pastData, input, frame);
2023-06-15 03:22:26 +00:00
2023-06-15 03:56:28 +00:00
if (frameCount == 60) {
frameCount = 0
2023-06-15 03:22:26 +00:00
let now = Date.now()
let fps = (now-last)/1000*60
fps_div.innerHTML = fps.toFixed(2);
last = now
}
2023-06-15 03:56:28 +00:00
frameCount++
2023-06-14 01:18:01 +00:00
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"
2023-06-14 02:22:42 +00:00
let width = 21
let height = 21
2023-06-14 01:18:01 +00:00
let m_data = [
2023-06-14 02:22:42 +00:00
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,
2023-06-15 03:22:26 +00:00
1,0,0,0,0,0,1,0,1,2,2,2,1,0,1,0,0,0,0,0,1,
1,0,1,1,1,0,1,0,1,2,2,2,1,0,1,0,1,1,1,0,1,
1,0,0,0,0,0,1,0,1,2,2,2,1,0,1,0,0,0,0,0,1,
1,1,1,0,1,0,1,0,1,1,2,1,1,0,1,0,1,0,1,1,1,
2023-06-14 02:22:42 +00:00
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
2023-06-14 01:18:01 +00:00
]
2023-06-14 02:22:42 +00:00
2023-06-14 01:18:01 +00:00
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)
2023-06-15 03:22:26 +00:00
return Math.abs(rpos[0] - pos[0]) < .05 && Math.abs(rpos[1] - pos[1]) < .05
2023-06-14 01:18:01 +00:00
}
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
}
}
2023-06-15 03:22:26 +00:00
export const get_item_key = (x, y, w) => {
let nx = Math.round(x * 2)
let ny = Math.round(y * 2)
let key = ny * w * 2 + nx
return key
}
const ceil_half = (n) => {
return Math.ceil(n*2)/2
}
const floor_half = (n) => {
return Math.floor(n*2)/2
}
2023-06-14 01:18:01 +00:00
/**
* @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
2023-06-15 03:22:26 +00:00
let tile_in_front_with_turn = get_tile_with_rot(data.map, current_pos, input_dir)
if (tile_in_front_with_turn == 1 || tile_in_front_with_turn == 2) {
2023-06-14 01:18:01 +00:00
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)
2023-06-15 03:22:26 +00:00
let tile_in_front = get_tile_with_rot(data.map, current_pos, move_dir)
if (tile_in_front != 1 && tile_in_front != 2) {
2023-06-14 01:18:01 +00:00
data.players[id].pos = move_pos
} else {
data.players[id].pos = round_pos(current_pos)
}
2023-06-15 03:22:26 +00:00
// eat items
let pos = data.players[id].pos
for (let x = ceil_half(pos[0]-.5); x <= floor_half(pos[0]+.5); x += .5) {
for (let y = ceil_half(pos[1]-.5); y <= floor_half(pos[1]+.5); y += .5) {
let item_key = get_item_key(x, y, data.map.width)
2023-06-15 03:56:28 +00:00
delete data.map.items[item_key]
2023-06-15 03:22:26 +00:00
}
}
2023-06-14 01:18:01 +00:00
}
}