Ping: veins fade outward in the order found, faster; recharge arc with the drill (t75)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 17:23:25 -04:00
co-authored by Claude Opus 5.5
parent 29eee51e35
commit f4ca1cae57
3 changed files with 34 additions and 12 deletions
+3 -2
View File
@@ -8,8 +8,9 @@ open or not verified. Keep each entry short; the git history has the details.
- 10 s recharge (`PING_RECHARGE`, `pingWait`) with a drill panel chip. `pingImage` draws veins in - 10 s recharge (`PING_RECHARGE`, `pingWait`) with a drill panel chip. `pingImage` draws veins in
their lit colour (base + per-grain noise) with a lighter rim; the ring is dimmer; up to 60 glints their lit colour (base + per-grain noise) with a lighter rim; the ring is dimmer; up to 60 glints
per ping flash as the ring passes. Files: `src/ui/actions.js`, `src/ui/selection-panels.js`, per ping flash as the ring passes. Follow-up: veins fade by distance (lit 1.2 s, fade 0.8 s,
`src/render/overlays.js`. Tests: full suite with --browser passed. The ring and glints were not drawn in 6 alpha bands) and `drawPingRecharge` shows an arc around the bot with the drill. Files: `src/ui/actions.js`, `src/ui/selection-panels.js`,
`src/render/overlays.js`, `src/ui/loop.js`. Tests: full suite with --browser passed. The ring and glints were not
caught in a screenshot (they finish before the frame is taken). caught in a screenshot (they finish before the frame is taken).
## 2026-10-27: bag counted things its list didn't show (Claude Code) ## 2026-10-27: bag counted things its list didn't show (Claude Code)
+30 -10
View File
@@ -111,9 +111,10 @@ function drawFlowRates() {
} }
// The resonance ping's echo: a faint ring spreading from the bot, the veins it found showing in // 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), // 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 // a brief sparkle as the ring passes them; each vein fades a fixed time after the ring reached
// image, so each frame is one clipped drawImage plus a few dozen sparkles. // it, so the fade follows the ring outward. The veins are drawn once into a small image; each
const PING_SPEED = 160, PING_FADE = 4500, PING_GLINTS = 60, GLINT_MS = 450; // frame draws it in a few distance bands (fading ones at partial alpha) plus a few dozen sparkles.
const PING_SPEED = 160, PING_HOLD = 1200, PING_FADE = 800, PING_BANDS = 6, PING_GLINTS = 60, GLINT_MS = 450;
function pingImage(p) { function pingImage(p) {
const size = p.r * 2 + 1, c = document.createElement('canvas'); c.width = c.height = size; 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 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);
@@ -133,15 +134,23 @@ function drawPing() {
const p = GAME.ping; const p = GAME.ping;
if (!p) return; if (!p) return;
const ms = performance.now() - p.t0, front = ms / 1000 * PING_SPEED, done = p.r / PING_SPEED * 1000; const ms = performance.now() - p.t0, front = ms / 1000 * PING_SPEED, done = p.r / PING_SPEED * 1000;
if (ms > done + PING_FADE) { GAME.ping = null; return; } if (ms > done + PING_HOLD + PING_FADE) { GAME.ping = null; return; }
if (!p.img) p.img = pingImage(p); if (!p.img) p.img = pingImage(p);
const ctx = R.ctx, zp = R.zp, c = worldToScreenPx(p.x, p.y), o = worldToScreenPx(p.img.ox, p.img.oy); const ctx = R.ctx, zp = R.zp, c = worldToScreenPx(p.x, p.y), o = worldToScreenPx(p.img.ox, p.img.oy);
ctx.save(); // inside `gone` the veins have faded out; from `full` out to the ring they are fully lit
ctx.beginPath(); ctx.arc(c.x, c.y, Math.min(front, p.r + 1) * zp, 0, Math.PI * 2); ctx.clip(); const outer = Math.min(front, p.r + 1), full = (ms - PING_HOLD) / 1000 * PING_SPEED, gone = (ms - PING_HOLD - PING_FADE) / 1000 * PING_SPEED;
ctx.globalAlpha = ms < done ? 1 : Math.max(0, 1 - (ms - done) / PING_FADE); const band = (r0, r1, a) => {
ctx.imageSmoothingEnabled = false; if (r1 <= Math.max(0, r0) || a <= 0) return;
ctx.drawImage(p.img.c, o.x, o.y, p.img.c.width * zp, p.img.c.height * zp); ctx.save(); ctx.beginPath(); ctx.arc(c.x, c.y, r1 * zp, 0, Math.PI * 2);
ctx.restore(); if (r0 > 0) ctx.arc(c.x, c.y, r0 * zp, 0, Math.PI * 2, true);
ctx.clip(); ctx.globalAlpha = a; ctx.imageSmoothingEnabled = false;
ctx.drawImage(p.img.c, o.x, o.y, p.img.c.width * zp, p.img.c.height * zp); ctx.restore();
};
band(Math.max(full, 0), outer, 1);
for (let k = 0; k < PING_BANDS; k++) {
const r0 = gone + (full - gone) * k / PING_BANDS, r1 = gone + (full - gone) * (k + 1) / PING_BANDS;
band(r0, Math.min(r1, outer), (k + 0.5) / PING_BANDS);
}
ctx.save(); ctx.save();
if (front < p.r) { if (front < p.r) {
ctx.strokeStyle = `rgba(160,225,255,${0.35 * (1 - front / p.r)})`; ctx.lineWidth = Math.max(1, zp * 0.4); ctx.strokeStyle = `rgba(160,225,255,${0.35 * (1 - front / p.r)})`; ctx.lineWidth = Math.max(1, zp * 0.4);
@@ -158,6 +167,17 @@ function drawPing() {
} }
ctx.restore(); ctx.restore();
} }
// While the ping recharges, a thin arc around the bot fills up (only with the drill in hand).
function drawPingRecharge() {
if (toolId() !== 'drill' || GAME.pingTick === undefined || tick < GAME.pingTick) return;
const k = (tick - GAME.pingTick) / PING_RECHARGE;
if (k >= 1) return;
const ctx = R.ctx, c = worldToScreenPx(bot.x + bot.w / 2, bot.y + bot.h / 2), r = Math.max(bot.w, bot.h) * R.zp * 0.9;
ctx.save(); ctx.lineWidth = Math.max(2, R.zp * 0.5); ctx.lineCap = 'round';
ctx.strokeStyle = 'rgba(160,225,255,0.18)'; ctx.beginPath(); ctx.arc(c.x, c.y, r, 0, Math.PI * 2); ctx.stroke();
ctx.strokeStyle = 'rgba(160,225,255,0.7)'; ctx.beginPath(); ctx.arc(c.x, c.y, r, -Math.PI / 2, -Math.PI / 2 + k * Math.PI * 2); ctx.stroke();
ctx.restore();
}
// Outline a run of blocks: strong while it is being edited, faint when pointed at // Outline a run of blocks: strong while it is being edited, faint when pointed at
function drawRunOutline(run, strong) { function drawRunOutline(run, strong) {
if (!run || !run.length) return; if (!run || !run.length) return;
+1
View File
@@ -74,6 +74,7 @@ function frame(now) {
if (GAME.infoOverlay) { drawFlowArrows(); drawFlowRates(); } if (GAME.infoOverlay) { drawFlowArrows(); drawFlowRates(); }
drawJoinMarks(); drawJoinMarks();
drawPing(); drawPing();
drawPingRecharge();
drawFilterTags(!!GAME.infoOverlay, GAME.editRun ? GAME.editRun[0] : hb); drawFilterTags(!!GAME.infoOverlay, GAME.editRun ? GAME.editRun[0] : hb);
drawFloaters(); drawFloaters();
drawRain(); drawRain();