diff options
author | metamuffin <metamuffin@disroot.org> | 2024-06-04 17:14:39 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2024-06-04 17:14:39 +0200 |
commit | f39f9840a8d14f35de0d38c9570ae27bacb20119 (patch) | |
tree | 81d80c18e5cc480cb3b34b988d7ebf9ed268c0ec | |
parent | 34967cd3b6530656ef0bf31810f9fd6dfb853765 (diff) | |
download | gpn-tron-rust-f39f9840a8d14f35de0d38c9570ae27bacb20119.tar gpn-tron-rust-f39f9840a8d14f35de0d38c9570ae27bacb20119.tar.bz2 gpn-tron-rust-f39f9840a8d14f35de0d38c9570ae27bacb20119.tar.zst |
animated spectator
-rw-r--r-- | src/spectate/main.ts | 51 | ||||
-rw-r--r-- | src/spectate/server.rs | 2 |
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 { |