Ping: 10 s recharge with a panel chip, natural ore colours with a rim, glints (t75)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 17:12:09 -04:00
co-authored by Claude Opus 5.5
parent a671dcb59a
commit 29eee51e35
4 changed files with 39 additions and 16 deletions
+25 -12
View File
@@ -109,21 +109,25 @@ function drawFlowRates() {
}
ctx.restore();
}
// The resonance ping's echo: a ring spreading from the bot, the veins it found lighting up inside
// it, then fading once it has gone out. The veins are drawn once into a small image, so each
// frame is one clipped drawImage however many there are. Drawn over fog, so unexplored rock shows.
const PING_SPEED = 160, PING_FADE = 4500;
// The resonance ping's echo: a faint ring spreading from the bot, the veins it found showing in
// their own lit colour inside it (with a slightly lighter rim so dark seams read against fog),
// a brief sparkle as the ring passes them, then a fade. The veins are drawn once into a small
// image, so each frame is one clipped drawImage plus a few dozen sparkles.
const PING_SPEED = 160, PING_FADE = 4500, PING_GLINTS = 60, GLINT_MS = 450;
function pingImage(p) {
const size = p.r * 2 + 1, c = document.createElement('canvas'); c.width = c.height = size;
const g = c.getContext('2d'), img = g.createImageData(size, size), d = img.data, ox = Math.floor(p.x - p.r), oy = Math.floor(p.y - p.r);
const lit = (i, k, lift) => { const m = mat[i], v = (noiseB[i] - 128) * CVAR[m] / 128 + lift; d[k] = COLR[m] + v; d[k + 1] = COLG[m] + v; d[k + 2] = COLB[m] + v; d[k + 3] = 255; };
for (const i of p.cells) { const x = i % W - ox, y = ((i / W) | 0) - oy; if (x >= 0 && y >= 0 && x < size && y < size) lit(i, (y * size + x) * 4, 0); }
for (const i of p.cells) {
const x = i % W - ox, y = ((i / W) | 0) - oy;
if (x < 0 || y < 0 || x >= size || y >= size) continue;
const k = (y * size + x) * 4, m = mat[i];
d[k] = Math.min(255, COLR[m] + 90); d[k + 1] = Math.min(255, COLG[m] + 90); d[k + 2] = Math.min(255, COLB[m] + 90); d[k + 3] = 220;
const x = i % W - ox, y = ((i / W) | 0) - oy, k = (y * size + x) * 4;
if (x <= 0 || y <= 0 || x >= size - 1 || y >= size - 1) continue;
if (!d[k - 1] || !d[k + 7] || !d[k - size * 4 + 3] || !d[k + size * 4 + 3]) lit(i, k, 45); // the rim
}
g.putImageData(img, 0, 0);
return { c, ox, oy };
const step = Math.max(1, Math.ceil(p.cells.length / PING_GLINTS));
const glints = p.cells.filter((_, k) => k % step === 0).map(i => ({ x: i % W + 0.5, y: ((i / W) | 0) + 0.5, d: Math.hypot(i % W - p.x, ((i / W) | 0) - p.y), m: mat[i] }));
return { c, ox, oy, glints };
}
function drawPing() {
const p = GAME.ping;
@@ -138,12 +142,21 @@ function drawPing() {
ctx.imageSmoothingEnabled = false;
ctx.drawImage(p.img.c, o.x, o.y, p.img.c.width * zp, p.img.c.height * zp);
ctx.restore();
ctx.save();
if (front < p.r) {
ctx.save();
ctx.strokeStyle = `rgba(160,225,255,${0.85 * (1 - front / p.r)})`; ctx.lineWidth = Math.max(2, zp * 0.8);
ctx.strokeStyle = `rgba(160,225,255,${0.35 * (1 - front / p.r)})`; ctx.lineWidth = Math.max(1, zp * 0.4);
ctx.beginPath(); ctx.arc(c.x, c.y, front * zp, 0, Math.PI * 2); ctx.stroke();
ctx.restore();
}
// a four-point glint where the ring has just passed, growing then shrinking
for (const gl of p.img.glints) {
const t = (ms - gl.d / PING_SPEED * 1000) / GLINT_MS;
if (t < 0 || t > 1) continue;
const s = worldToScreenPx(gl.x, gl.y), k = Math.sin(t * Math.PI) * zp * 2.2, m = gl.m;
ctx.strokeStyle = `rgba(${Math.min(255, COLR[m] + 140)},${Math.min(255, COLG[m] + 140)},${Math.min(255, COLB[m] + 140)},${0.9 * Math.sin(t * Math.PI)})`;
ctx.lineWidth = Math.max(1, zp * 0.35);
ctx.beginPath(); ctx.moveTo(s.x - k, s.y); ctx.lineTo(s.x + k, s.y); ctx.moveTo(s.x, s.y - k); ctx.lineTo(s.x, s.y + k); ctx.stroke();
}
ctx.restore();
}
// Outline a run of blocks: strong while it is being edited, faint when pointed at
function drawRunOutline(run, strong) {