aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-06-04 17:14:39 +0200
committermetamuffin <metamuffin@disroot.org>2024-06-04 17:14:39 +0200
commitf39f9840a8d14f35de0d38c9570ae27bacb20119 (patch)
tree81d80c18e5cc480cb3b34b988d7ebf9ed268c0ec
parent34967cd3b6530656ef0bf31810f9fd6dfb853765 (diff)
downloadgpn-tron-rust-f39f9840a8d14f35de0d38c9570ae27bacb20119.tar
gpn-tron-rust-f39f9840a8d14f35de0d38c9570ae27bacb20119.tar.bz2
gpn-tron-rust-f39f9840a8d14f35de0d38c9570ae27bacb20119.tar.zst
animated spectator
-rw-r--r--src/spectate/main.ts51
-rw-r--r--src/spectate/server.rs2
2 files changed, 42 insertions, 11 deletions
diff --git a/src/spectate/main.ts b/src/spectate/main.ts
index 4d508b6..422a2c6 100644
--- a/src/spectate/main.ts
+++ b/src/spectate/main.ts
@@ -6,10 +6,15 @@ type Packet = "tick"
| { pos: { id: number, x: number, y: number } }
| { game: { width: number, height: number } }
| { player: { id: number, name: string } }
+ | { die: number[] }
class Snake {
parts: { x: number, y: number, dx: number, dy: number }[] = []
- constructor(public name: string) { }
+ color: string
+ dead = false
+ constructor(public name: string) {
+ this.color = name_color(name)
+ }
add_part(x: number, y: number) {
if (!this.parts.length) return this.parts.push({ x, y, dx: 0, dy: 0 })
const last = this.parts[this.parts.length - 1]
@@ -17,12 +22,12 @@ class Snake {
if (x > last.x + 1 || x < last.x - 1) dx *= -1, dx /= Math.abs(dx)
if (y > last.y + 1 || y < last.y - 1) dy *= -1, dy /= Math.abs(dy)
this.parts.push({ x, y, dx, dy })
- console.log(this.parts);
-
}
}
+
let size = 0
-let snakes = new Map<number, Snake>()
+let tick_anim = 0;
+const snakes = new Map<number, Snake>()
let canvas: HTMLCanvasElement
let ctx: CanvasRenderingContext2D
@@ -59,27 +64,49 @@ function redraw() {
for (let i = 0; i < snake.parts.length; i++) {
const p = snake.parts[i];
ctx.moveTo(p.x - p.dx, p.y - p.dy)
- ctx.lineTo(p.x, p.y)
+ if (i == snake.parts.length - 1)
+ ctx.lineTo(tick_lerp(p.x - p.dx, p.x), tick_lerp(p.y - p.dy, p.y))
+ else ctx.lineTo(p.x, p.y)
}
ctx.lineCap = "round"
ctx.lineJoin = "round"
- ctx.lineWidth = 0.6;
- ctx.strokeStyle = "red"
+ if (snake.dead) ctx.lineWidth = tick_lerp(0.6, 0)
+ else ctx.lineWidth = 0.6;
+ ctx.strokeStyle = snake.color
ctx.stroke()
}
ctx.restore()
}
ctx.restore()
-
+ tick_anim += 0.1
+ tick_anim = Math.min(1, tick_anim)
requestAnimationFrame(redraw)
}
+function tick_lerp(f: number, t: number) {
+ return (1 - tick_anim) * f + tick_anim * t
+}
+
+function name_color(name: string): string {
+ let hash = 0;
+ for (let i = 0; i < name.length; i++) {
+ hash = ((hash << 5) - hash) + name.charCodeAt(i);
+ hash |= 0;
+ }
+ return `hsl(${hash % 360}deg 100% 50%)`;
+}
+
ws.onerror = console.error
ws.onmessage = message => {
const p = JSON.parse(message.data) as Packet
console.log(p);
if (p == "tick") {
-
+ tick_anim = 0
+ const d = []
+ for (const [k, s] of snakes) {
+ if (s.dead) d.push(k)
+ }
+ d.forEach(k => snakes.delete(k))
} else if ("game" in p) {
snakes.clear()
size = p.game.width
@@ -87,7 +114,11 @@ ws.onmessage = message => {
snakes.set(p.player.id, new Snake(p.player.name))
} else if ("pos" in p) {
snakes.get(p.pos.id)?.add_part(p.pos.x, p.pos.y)
+ } else if ("die" in p) {
+ for (const d of p.die) {
+ const s = snakes.get(d)
+ if (s) s.dead = true
+ }
}
-
}
diff --git a/src/spectate/server.rs b/src/spectate/server.rs
index e3e9f80..d9d2ef8 100644
--- a/src/spectate/server.rs
+++ b/src/spectate/server.rs
@@ -79,6 +79,7 @@ async fn broadcaster(sstate: Arc<SpectateState>, state: Arc<State>) {
{
let g = state.game.read().await;
+ events.push(Packet::Tick);
if new_game {
sstate.past_events.write().await.clear();
events.push(Packet::Game {
@@ -103,7 +104,6 @@ async fn broadcaster(sstate: Arc<SpectateState>, state: Arc<State>) {
if !g.dead.is_empty() {
events.push(Packet::Die(g.dead.clone()));
}
- events.push(Packet::Tick);
}
sstate.past_events.write().await.extend(events.clone());
for ev in events {