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

forked from benjivers/val

workbenchthis machine
Files 00e29188271b60cb71e377df120e603f5649cfdb
1
/**
2
 * The board from above — the drawing this game started as.
3
 *
4
 * It is three things now: the minimap in the corner, the full-screen map Tab
5
 * holds open, and the view a phone plays in, where a raycaster with touch
6
 * look would be miserable. One function, drawn into whatever box it is given.
7
 */
8
9
import { AGENT_COLOUR } from "./scene.js";
10
11
const TILE_COLOUR = {
12
  "#": "#20242d",
13
  A: "#2c2a22",
14
  B: "#2c2a22",
15
  a: "#2a2024",
16
  d: "#1e2630",
17
  ".": "#171a21",
18
};
19
20
/**
21
 * Draw the whole map into `box` (canvas pixels). `look(player)` gives the
22
 * smoothed position; `opts.labels` draws site letters and names, which only
23
 * fit on the big version.
24
 */
25
export function drawTopdown(ctx, map, snap, look, box, opts = {}) {
26
  const cols = map.rows[0].length;
27
  const s = box.w / (cols * map.tile);
28
  const labels = opts.labels !== false;
29
  ctx.save();
30
  ctx.beginPath();
31
  ctx.rect(box.x, box.y, box.w, box.h);
32
  ctx.clip();
33
  ctx.translate(box.x, box.y);
34
35
  const t = map.tile * s;
36
  for (let row = 0; row < map.rows.length; row++)
37
    for (let col = 0; col < map.rows[row].length; col++) {
38
      const cell = map.rows[row][col];
39
      ctx.fillStyle = TILE_COLOUR[cell] ?? "#171a21";
40
      ctx.fillRect(col * t, row * t, t + 0.6, t + 0.6);
41
      if (labels && (cell === "A" || cell === "B")) {
42
        ctx.fillStyle = "#6b5f3a";
43
        ctx.font = `${Math.round(t * 0.5)}px system-ui, sans-serif`;
44
        ctx.fillText(cell, col * t + t * 0.3, row * t + t * 0.68);
45
      }
46
    }
47
48
  if (!snap) {
49
    ctx.restore();
50
    return;
51
  }
52
53
  for (const orb of snap.orbs) {
54
    ctx.fillStyle = "#c9a0ff";
55
    ctx.beginPath();
56
    ctx.arc(orb.x * s, orb.y * s, Math.max(2, 9 * s), 0, 7);
57
    ctx.fill();
58
  }
59
  for (const zone of snap.zones) if (zone.k !== "smoke") drawZone(ctx, zone, s);
60
61
  if (snap.spike.x !== null) {
62
    const x = snap.spike.x * s;
63
    const y = snap.spike.y * s;
64
    const r = Math.max(2.5, 7 * s);
65
    ctx.fillStyle = snap.spike.planted ? (Math.floor(Date.now() / 250) % 2 ? "#ff5b4a" : "#8a2a22") : "#c8b45c";
66
    ctx.fillRect(x - r, y - r, r * 2, r * 2);
67
    if (snap.spike.planted && snap.spike.defuse > 0) {
68
      ctx.fillStyle = "#4fd6a0";
69
      ctx.fillRect(x - 20 * s, y + 12 * s, 40 * s * (snap.spike.defuse / 7), 3);
70
    }
71
  }
72
73
  for (const shot of snap.shots) {
74
    ctx.strokeStyle = shot.hit ? "rgba(255,120,110,0.85)" : "rgba(255,240,180,0.5)";
75
    ctx.lineWidth = Math.max(1, 2 * s);
76
    ctx.beginPath();
77
    ctx.moveTo(shot.x1 * s, shot.y1 * s);
78
    ctx.lineTo(shot.x2 * s, shot.y2 * s);
79
    ctx.stroke();
80
  }
81
82
  for (const player of snap.players) drawPlayer(ctx, player, look(player), s, snap.me, labels);
83
  // Smoke last: on this view it hides what is under it, as it should.
84
  for (const zone of snap.zones) if (zone.k === "smoke" && zone.f <= 0) drawZone(ctx, zone, s);
85
  ctx.restore();
86
}
87
88
function drawZone(ctx, z, s) {
89
  const x = z.x * s;
90
  const y = z.y * s;
91
  const r = z.r * s;
92
  ctx.lineWidth = Math.max(1, s);
93
  if (z.k === "smoke") {
94
    ctx.fillStyle = "rgba(190,195,205,0.72)";
95
    disc(ctx, x, y, r, true);
96
  } else if (z.k === "molly") {
97
    ctx.fillStyle = "rgba(230,110,50,0.45)";
98
    ctx.strokeStyle = "#f08a4b";
99
    disc(ctx, x, y, r, true, true);
100
  } else if (z.k === "wall") {
101
    ctx.fillStyle = "rgba(120,200,255,0.55)";
102
    disc(ctx, x, y, r, true);
103
  } else if (z.k === "flash") {
104
    ctx.fillStyle = z.f > 0 ? "#ffe9a8" : "rgba(255,255,255,0.9)";
105
    disc(ctx, x, y, z.f > 0 ? 5 * s : 30 * s, true);
106
  } else if (z.k === "dart") {
107
    ctx.strokeStyle = "rgba(90,220,170,0.7)";
108
    disc(ctx, x, y, r, false, true);
109
  } else if (z.k === "trip") {
110
    ctx.strokeStyle = "rgba(230,200,90,0.8)";
111
    disc(ctx, x, y, r, false, true);
112
  } else if (z.k === "heal") {
113
    ctx.fillStyle = "rgba(120,230,140,0.5)";
114
    disc(ctx, x, y, 14 * s, true);
115
  }
116
}
117
118
function disc(ctx, x, y, r, fill, stroke) {
119
  ctx.beginPath();
120
  ctx.arc(x, y, r, 0, 7);
121
  if (fill) ctx.fill();
122
  if (stroke) ctx.stroke();
123
}
124
125
/** Everybody is a dot; you are an arrow, so you can tell at a glance where you face. */
126
function drawPlayer(ctx, p, pos, s, me, labels) {
127
  const x = pos.x * s;
128
  const y = pos.y * s;
129
  const r = Math.max(2.5, 16 * s);
130
  const mine = p.id === me.id;
131
  const body = !p.alive ? "#555a63" : p.mate ? (mine ? "#eaeaea" : "#4fb3d9") : "#e35d5b";
132
133
  if (mine) {
134
    ctx.save();
135
    ctx.translate(x, y);
136
    ctx.rotate(pos.angle);
137
    ctx.fillStyle = body;
138
    ctx.beginPath();
139
    ctx.moveTo(r * 1.5, 0);
140
    ctx.lineTo(-r, r * 0.9);
141
    ctx.lineTo(-r * 0.4, 0);
142
    ctx.lineTo(-r, -r * 0.9);
143
    ctx.closePath();
144
    ctx.fill();
145
    ctx.restore();
146
    return;
147
  }
148
149
  ctx.fillStyle = body;
150
  ctx.beginPath();
151
  ctx.arc(x, y, r, 0, 7);
152
  ctx.fill();
153
  ctx.strokeStyle = AGENT_COLOUR[p.agent] || "#888";
154
  ctx.lineWidth = Math.max(1, 3 * s);
155
  ctx.stroke();
156
  if (p.alive) {
157
    ctx.strokeStyle = body;
158
    ctx.beginPath();
159
    ctx.moveTo(x, y);
160
    ctx.lineTo(x + Math.cos(pos.angle) * r * 2, y + Math.sin(pos.angle) * r * 2);
161
    ctx.stroke();
162
  }
163
  if (p.spike) {
164
    ctx.fillStyle = "#f0d060";
165
    ctx.fillRect(x - r * 0.4, y - r * 1.8, r * 0.8, r * 0.8);
166
  }
167
  if (p.blind) {
168
    ctx.fillStyle = "rgba(255,255,255,0.8)";
169
    disc(ctx, x, y - r * 1.6, r * 0.35, true);
170
  }
171
  if (labels) {
172
    ctx.fillStyle = "rgba(235,235,235,0.75)";
173
    ctx.font = `${Math.round(11 * Math.max(1, s * 2))}px system-ui, sans-serif`;
174
    ctx.textAlign = "center";
175
    ctx.fillText(p.name, x, y - r * 1.9);
176
    ctx.textAlign = "left";
177
  }
178
  if (p.alive && p.hp < 100) {
179
    ctx.fillStyle = "#0008";
180
    ctx.fillRect(x - r, y + r * 1.2, r * 2, 3);
181
    ctx.fillStyle = "#6fcf6f";
182
    ctx.fillRect(x - r, y + r * 1.2, r * 2 * (p.hp / 100), 3);
183
  }
184
}
↑↓ navigate ↵ open esc close
↑↓ navigate ↵ open esc close

Keyboard shortcuts