quill/val Public Public: anyone can read this; nobody can change it.

forked from benjivers/val

workbenchthis machine
Files c8ea83f25aa0be359368817293ebc3f7f1d27dd4
97 lines · 3.8 KB · ~1.1k tok Raw History
1
/**
2
 * Watching.
3
 *
4
 * A connection the server gave no seat — one that carried no session the page
5
 * minted, or a second client from an address that already holds a seat — is
6
 * sent a snapshot with no `me` and no players: the score, the round, the
7
 * clock and the kill feed, and nothing at all that says where anybody is.
8
 * That is the whole point of it, so this view draws no map and no bodies. It
9
 * says what the match is doing and why this connection is only watching.
10
 */
11
12
import { roundOutcome } from "./prompt.js";
13
14
/** Is this a snapshot for a connection that has no seat? */
15
export const watching = (snap) => Boolean(snap) && (snap.spectator === true || !snap.me);
16
17
// Why the server handed this connection no seat. It says which, because the
18
// two have different answers: open the tab that is playing, or let the page
19
// keep its cookie.
20
const WHY = {
21
  address: "Somebody at this address is already playing — that tab holds the seat, and this one watches.",
22
  cookie: "This browser did not send the session cookie the page sets, so there is no seat to give it.",
23
};
24
const WHY_UNSAID = "This connection has no seat, so it is watching the match.";
25
26
const PHASE = { buy: "buy phase", live: "live", end: "round over" };
27
28
/** What the watching view says, in order. Plain strings, nothing drawn. */
29
export function watchLines(snap) {
30
  const score = snap.score ?? { red: 0, blue: 0 };
31
  // A round that has just ended says who took it and why, the same as a seat
32
  // is told — it is the one thing a watcher most wants to know.
33
  const outcome = snap.phase === "end" ? roundOutcome(snap) : null;
34
  const turned = outcome ? ` — ${outcome.winner} take it, ${outcome.why}` : "";
35
  return {
36
    title: "watching",
37
    why: WHY[snap.why] ?? WHY_UNSAID,
38
    phase: `round ${snap.round ?? 1} · ${PHASE[snap.phase] ?? "match over"}${turned} · ${snap.attackers ?? "—"} attacking${snap.result ? " · " + snap.result : ""}`,
39
    score: `red ${score.red ?? 0} — ${score.blue ?? 0} blue`,
40
    clock: `${Math.ceil(Math.max(0, Number(snap.clock) || 0))}s`,
41
    note: "No map and no bodies: a watching connection is never told where anybody is.",
42
  };
43
}
44
45
/** Break a line at the last space that still fits, measured by the canvas. */
46
export function wrap(ctx, text, maxWidth) {
47
  const lines = [];
48
  let line = "";
49
  for (const word of String(text).split(" ")) {
50
    const tried = line ? `${line} ${word}` : word;
51
    if (line && ctx.measureText(tried).width > maxWidth) {
52
      lines.push(line);
53
      line = word;
54
    } else line = tried;
55
  }
56
  if (line) lines.push(line);
57
  return lines;
58
}
59
60
/**
61
 * The view itself: the score, the round, the clock, and the sentence that
62
 * says why this connection is watching. Centred on a flat panel, because
63
 * there is no world here to stand it on.
64
 */
65
export function drawWatching(ctx, snap, width, height) {
66
  const lines = watchLines(snap);
67
  const mid = width / 2;
68
  const unit = Math.min(width / 900, height / 570);
69
  ctx.fillStyle = "#0b0d11";
70
  ctx.fillRect(0, 0, width, height);
71
  ctx.textAlign = "center";
72
73
  ctx.fillStyle = "#8b93a5";
74
  ctx.font = `${Math.round(22 * unit)}px system-ui, sans-serif`;
75
  ctx.fillText(lines.title, mid, height * 0.3);
76
77
  ctx.fillStyle = "#e7e9ee";
78
  ctx.font = `${Math.round(54 * unit)}px system-ui, sans-serif`;
79
  ctx.fillText(lines.score, mid, height * 0.42);
80
81
  ctx.font = `${Math.round(30 * unit)}px system-ui, sans-serif`;
82
  ctx.fillText(lines.clock, mid, height * 0.52);
83
84
  ctx.fillStyle = "#b9c0cf";
85
  ctx.font = `${Math.round(19 * unit)}px system-ui, sans-serif`;
86
  ctx.fillText(lines.phase, mid, height * 0.6);
87
88
  ctx.fillStyle = "#8b93a5";
89
  ctx.font = `${Math.round(17 * unit)}px system-ui, sans-serif`;
90
  let y = height * 0.7;
91
  for (const line of [...wrap(ctx, lines.why, width * 0.8), ...wrap(ctx, lines.note, width * 0.8)]) {
92
    ctx.fillText(line, mid, y);
93
    y += 24 * unit;
94
  }
95
  ctx.textAlign = "left";
96
  return lines;
97
}
↑↓ navigate ↵ open esc close
↑↓ navigate ↵ open esc close

Keyboard shortcuts