diff --git a/client/css/main.css b/client/css/main.css
index bfb7426..d1d97e8 100644
--- a/client/css/main.css
+++ b/client/css/main.css
@@ -39,7 +39,7 @@ body {
.sprite {
position: absolute;
- transition: left .1s, top .1s;
+ /* transition: left .1s, top .1s; */
z-index: 2;
}
@@ -72,3 +72,13 @@ input {
margin-bottom: .215rem;
}
+#fps {
+ position: absolute;
+ left: 0;
+ top: 0;
+ z-index: 99;
+ font-size: 20;
+ font-style: monospace;
+ background-color: black;
+ color: white;
+}
diff --git a/client/img/dot.png b/client/img/dot.png
new file mode 100644
index 0000000..b911cea
Binary files /dev/null and b/client/img/dot.png differ
diff --git a/client/img/pac.png b/client/img/pac.png
new file mode 100644
index 0000000..7dc0989
Binary files /dev/null and b/client/img/pac.png differ
diff --git a/client/img/wall_cross.png b/client/img/wall_cross.png
new file mode 100644
index 0000000..9553645
Binary files /dev/null and b/client/img/wall_cross.png differ
diff --git a/client/img/wall_dot.png b/client/img/wall_dot.png
new file mode 100644
index 0000000..2e7fa15
Binary files /dev/null and b/client/img/wall_dot.png differ
diff --git a/client/static/empty.png b/client/img/wall_empty.png
similarity index 100%
rename from client/static/empty.png
rename to client/img/wall_empty.png
diff --git a/client/img/wall_end.png b/client/img/wall_end.png
new file mode 100644
index 0000000..752f73b
Binary files /dev/null and b/client/img/wall_end.png differ
diff --git a/client/img/wall_straight.png b/client/img/wall_straight.png
new file mode 100644
index 0000000..2dc5448
Binary files /dev/null and b/client/img/wall_straight.png differ
diff --git a/client/img/wall_tee.png b/client/img/wall_tee.png
new file mode 100644
index 0000000..31742bd
Binary files /dev/null and b/client/img/wall_tee.png differ
diff --git a/client/img/wall_turn.png b/client/img/wall_turn.png
new file mode 100644
index 0000000..dbe5837
Binary files /dev/null and b/client/img/wall_turn.png differ
diff --git a/client/index.html b/client/index.html
index 0740f5f..50ae8ef 100644
--- a/client/index.html
+++ b/client/index.html
@@ -5,6 +5,7 @@
+
diff --git a/client/js/gfx/graphics.js b/client/js/gfx/graphics.js
index e73927e..31565a8 100644
--- a/client/js/gfx/graphics.js
+++ b/client/js/gfx/graphics.js
@@ -1,56 +1,105 @@
import { Sprite } from './sprite.js'
-import { Rotation } from '../logic.js'
+import { ItemType, Rotation } from '../logic.js'
+const draw_players = (data, players, sprites) => {
+ 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
+ }
+ }
+}
+
+const update_player_sprites = (data, players, sprites) => {
+ for (const sprite of sprites) {
+ if (sprite !== undefined) {
+ sprite.destroy()
+ }
+ }
+
+ let new_sprites = Array(players)
+ new_sprites.fill(undefined)
+
+ for (let id of players) {
+ let sprite = new Sprite("/img/pac.png", data.map)
+ sprite.layer(3)
+ sprite.resize(1,1)
+ sprite.show()
+ new_sprites[id] = sprite
+ }
+
+ return new_sprites
+}
+
+const create_map_dot = (data, x, y) => {
+ let dot = new Sprite("/img/dot.png", data.map)
+ dot.move(x, y)
+ dot.resize(.2,.2)
+ dot.show()
+ return dot
+}
+
+const update_item_sprites = (data) => {
+
+ let item_sprites = {}
+
+ for (let item_key in data.map.items) {
+ let item = data.map.items[item_key]
+ switch (item.type) {
+ case ItemType.DOT:
+ item_sprites[item_key] = create_map_dot(data, item.pos[0], item.pos[1])
+ break;
+ }
+ }
+
+ return item_sprites
+}
+
export const startGraphicsUpdater = () => {
- let sprites = []
+ let player_sprites = []
+ let item_sprites = {}
/**
* @type {(data: import("../logic.js").GameState) => void}
*/
return (data) => {
- if (!data.started) return
+ if (!data.map || !data.map.visible) 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
- }
+ if (player_sprites.length !== players.length) {
+ player_sprites = update_player_sprites(data, players, player_sprites)
+ console.log("updating player sprites")
}
- 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
- }
+ for (let item_key of data.items_eaten) {
+ if (!(item_sprites[item_key])) continue
+ item_sprites[item_key].destroy()
+ delete item_sprites[item_key]
+ delete data.items_eaten[item_key]
}
+
+ if (Object.keys(item_sprites).length !== Object.keys(data.map.items).length) {
+ item_sprites = update_item_sprites(data)
+ console.log("updating item sprites")
+ }
+
+ draw_players(data, players, player_sprites)
+
}
}
diff --git a/client/js/gfx/map.js b/client/js/gfx/map.js
index 0ba7f9c..e629b17 100644
--- a/client/js/gfx/map.js
+++ b/client/js/gfx/map.js
@@ -1,3 +1,5 @@
+import { ItemType, get_item_key } from "../logic.js";
+
const gen_style = (map, style) => {
const css = `
* {
@@ -63,71 +65,71 @@ const place_tile = (container, type) => {
let image_src, class_name;
switch(type) {
case Direction.EMPTY:
- image_src = "/static/empty.png"
+ image_src = "/img/wall_empty.png"
class_name = ""
break
case Direction.WALL_HZ:
- image_src = "/static/wall.png"
+ image_src = "/img/wall_straight.png"
class_name = ""
break
case Direction.WALL_VT:
- image_src = "/static/wall.png"
+ image_src = "/img/wall_straight.png"
class_name = "rotate90"
break
case Direction.TURN_Q1:
- image_src = "/static/turn.png"
+ image_src = "/img/wall_turn.png"
class_name = ""
break
case Direction.TURN_Q2:
- image_src = "/static/turn.png"
+ image_src = "/img/wall_turn.png"
class_name = "rotate270"
break
case Direction.TURN_Q3:
- image_src = "/static/turn.png"
+ image_src = "/img/wall_turn.png"
class_name = "rotate180"
break
case Direction.TURN_Q4:
- image_src = "/static/turn.png"
+ image_src = "/img/wall_turn.png"
class_name = "rotate90"
break
case Direction.TEE_NORTH:
- image_src = "/static/tee.png"
+ image_src = "/img/wall_tee.png"
class_name = "rotate180"
break
case Direction.TEE_EAST:
- image_src = "/static/tee.png"
+ image_src = "/img/wall_tee.png"
class_name = "rotate270"
break
case Direction.TEE_SOUTH:
- image_src = "/static/tee.png"
+ image_src = "/img/wall_tee.png"
class_name = ""
break
case Direction.TEE_WEST:
- image_src = "/static/tee.png"
+ image_src = "/img/wall_tee.png"
class_name = "rotate90"
break
case Direction.CROSS:
- image_src = "/static/cross.png"
+ image_src = "/img/wall_cross.png"
class_name = ""
break
case Direction.DOT:
- image_src = "/static/dot.png"
+ image_src = "/img/wall_dot.png"
class_name = ""
break
case Direction.WALL_END_NORTH:
- image_src = "/static/wall_end.png"
+ image_src = "/img/wall_end.png"
class_name = ""
break;
case Direction.WALL_END_EAST:
- image_src = "/static/wall_end.png"
+ image_src = "/img/wall_end.png"
class_name = "rotate90"
break;
case Direction.WALL_END_SOUTH:
- image_src = "/static/wall_end.png"
+ image_src = "/img/wall_end.png"
class_name = "rotate180"
break;
case Direction.WALL_END_WEST:
- image_src = "/static/wall_end.png"
+ image_src = "/img/wall_end.png"
class_name = "rotate270"
break;
}
@@ -152,11 +154,11 @@ const gen_walls = (width, height, data) => {
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 north = get_point(width, height, data, x, y-1) == 1
+ let south = get_point(width, height, data, x, y+1) == 1
+ let east = get_point(width, height, data, x+1, y) == 1
+ let west = get_point(width, height, data, x-1, y) == 1
+ let current = get_point(width, height, data, x, y) == 1
let point = Direction.EMPTY
@@ -213,11 +215,45 @@ const gen_map = (map, container) => {
place_tile(container, map.walls[y * map.width + x])
}
}
+}
+const gen_items = (map) => {
+
+ let width = map.width
+ let height = map.height
+
+ let items = {}
+
+ for (let y = 0; y < height; y++) {
+ for (let x = 0; x < width; x++) {
+ let tile = get_point(width, height, map.data, x, y)
+ if (tile != 0) continue
+
+ let item_key = get_item_key(x, y, width)
+ items[item_key] = {type: ItemType.DOT, pos: [x, y]}
+
+ let tile_south = get_point(width, height, map.data, x, y + 1)
+ if (tile_south == 0) {
+ item_key = get_item_key(x, y + .5, width)
+ items[item_key] = {type: ItemType.DOT, pos: [x, y + .5]}
+ }
+
+ let tile_east = get_point(width, height, map.data, x + 1, y)
+ if (tile_east == 0) {
+ item_key = get_item_key(x + .5, y, width)
+ items[item_key] = {type: ItemType.DOT, pos: [x + .5, y]}
+ }
+ }
+ }
+
+ return items
}
export class Map {
+ static data
+ static walls
+
constructor(width, height, data) {
let last = document.getElementById("container")
@@ -227,6 +263,8 @@ export class Map {
this.height = height
this.data = data
this.walls = gen_walls(width, height, data)
+ this.items = gen_items(this)
+ this.visible = false
}
@@ -250,6 +288,8 @@ export class Map {
}
gen_style(this, style)
+
+ this.visible = true
}
hide() {
@@ -257,5 +297,7 @@ export class Map {
if (container) container.remove()
let style = document.getElementById("style")
if (style) style.remove()
+
+ this.visible = false
}
}
diff --git a/client/js/gfx/sprite.js b/client/js/gfx/sprite.js
index 1ed8136..b793d89 100644
--- a/client/js/gfx/sprite.js
+++ b/client/js/gfx/sprite.js
@@ -11,6 +11,7 @@ export class Sprite {
this.y = 0
this.w = 1
this.h = 1
+ this.z = 1
this.d = 0
this.hide()
}
@@ -26,6 +27,7 @@ export class Sprite {
this.element.style.left = `${left}%`
this.element.style.top = `${top}%`
this.element.style.transform = `rotate(${this.d}deg)`
+ this.element.style.zIndex = `${this.z}`
}
move(x, y) {
@@ -40,6 +42,11 @@ export class Sprite {
this.#update_pos()
}
+ layer(z) {
+ this.z = z
+ this.#update_pos()
+ }
+
rotate(d) {
this.d = d
this.#update_pos()
diff --git a/client/js/input.js b/client/js/input.js
index abe64bb..3928e91 100644
--- a/client/js/input.js
+++ b/client/js/input.js
@@ -31,7 +31,14 @@ export function startInputListener() {
if (ev.code === "KeyB") {
debug_enabled = !debug_enabled
if (debug_enabled) {
- debug_style.innerHTML = "* {box-shadow: 0 0 3px red inset;}"
+ debug_style.innerHTML = ` \
+ #container img { \
+ box-shadow: 0 0 1px red inset; \
+ } \
+ #container .sprite { \
+ box-shadow: 0 0 1px white inset; \
+ } \
+ `
} else {
debug_style.innerHTML = ""
}
diff --git a/client/js/logic.js b/client/js/logic.js
index 405366c..e869c56 100644
--- a/client/js/logic.js
+++ b/client/js/logic.js
@@ -18,10 +18,14 @@ export const Rotation = {
WEST: 4
}
+export const ItemType = {
+ DOT: 1
+}
+
/**
* @typedef {[number, number]} Vec2
*
- * @typedef {{[key: number]: Key} InputMap
+ * @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
@@ -31,11 +35,14 @@ export const Rotation = {
*
* @typedef {{width: number, height: number, data: number[]}} Map
*
+ * @typedef {{type: ItemType, pos: Vec2}} Item
+ *
* @typedef {{
* started: boolean,
* input: InputMap,
* players: Players,
- * map: Map
+ * map: Map,
+ * items_eaten: Item[]
* }} GameState
*/
@@ -44,15 +51,26 @@ export const initState = {
started: false,
input: {},
players: [],
- map: {}
+ map: {},
+ items_eaten: []
}
+let last = Date.now()
+let fps_div = document.getElementById("fps")
+
export function advance(
pastData = initState,
input = { players: {} },
frame
) {
let data = processInput(pastData, input, frame);
+
+ if (frame % 60 == 0) {
+ let now = Date.now()
+ let fps = (now-last)/1000*60
+ fps_div.innerHTML = fps.toFixed(2);
+ last = now
+ }
return data;
}
@@ -147,16 +165,6 @@ 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 = [
@@ -169,10 +177,10 @@ const init_map = (data) => {
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,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,
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,
@@ -195,7 +203,7 @@ const round_pos = (pos) => {
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
+ return Math.abs(rpos[0] - pos[0]) < .05 && Math.abs(rpos[1] - pos[1]) < .05
}
const get_tile = (map, pos, ox, oy) => {
@@ -251,6 +259,21 @@ const increment_pos = (pos, rot, speed) => {
}
}
+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
+}
+
/**
* @param {GameState} data
*/
@@ -267,7 +290,8 @@ const update_players = (data) => {
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) {
+ 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) {
input_dir = Rotation.NOTHING
}
@@ -284,12 +308,26 @@ const update_players = (data) => {
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) {
+ let tile_in_front = get_tile_with_rot(data.map, current_pos, move_dir)
+ if (tile_in_front != 1 && tile_in_front != 2) {
data.players[id].pos = move_pos
} else {
data.players[id].pos = round_pos(current_pos)
}
+ // 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)
+ let item = data.map.items[item_key]
+ if (item) {
+ data.items_eaten.push(item_key)
+ delete data.map.items[item_key]
+ }
+ }
+ }
+
}
}
diff --git a/client/static/cross.png b/client/static/cross.png
deleted file mode 100644
index c8d1054..0000000
Binary files a/client/static/cross.png and /dev/null differ
diff --git a/client/static/dot.png b/client/static/dot.png
deleted file mode 100644
index bab810b..0000000
Binary files a/client/static/dot.png and /dev/null differ
diff --git a/client/static/tee.png b/client/static/tee.png
deleted file mode 100644
index ca144be..0000000
Binary files a/client/static/tee.png and /dev/null differ
diff --git a/client/static/turn.png b/client/static/turn.png
deleted file mode 100644
index c951f17..0000000
Binary files a/client/static/turn.png and /dev/null differ
diff --git a/client/static/tux.png b/client/static/tux.png
deleted file mode 100644
index a595145..0000000
Binary files a/client/static/tux.png and /dev/null differ
diff --git a/client/static/wall.png b/client/static/wall.png
deleted file mode 100644
index 19d4168..0000000
Binary files a/client/static/wall.png and /dev/null differ
diff --git a/client/static/wall_end.png b/client/static/wall_end.png
deleted file mode 100644
index 4f09e19..0000000
Binary files a/client/static/wall_end.png and /dev/null differ