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

forked from benjivers/val

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

Keyboard shortcuts