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:
+3
-2
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user