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:
@@ -4,6 +4,14 @@ What agents have done, newest first. Add an entry for every piece of work: the d
|
||||
note or request it was for, what changed, the files touched, the tests run, and anything left
|
||||
open or not verified. Keep each entry short; the git history has the details.
|
||||
|
||||
## 2026-10-27: t75 ping recharge, natural ore colour, glints (Claude Code)
|
||||
|
||||
- 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
|
||||
per ping flash as the ring passes. Files: `src/ui/actions.js`, `src/ui/selection-panels.js`,
|
||||
`src/render/overlays.js`. Tests: full suite with --browser passed. The ring and glints were not
|
||||
caught in a screenshot (they finish before the frame is taken).
|
||||
|
||||
## 2026-10-27: bag counted things its list didn't show (Claude Code)
|
||||
|
||||
- Owner's exported save: bag total 1,522 but the list showed nothing for 1,000 basalt (from the
|
||||
|
||||
+25
-12
@@ -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) {
|
||||
|
||||
+5
-3
@@ -202,11 +202,13 @@ function secondaryPress(shift) {
|
||||
}
|
||||
|
||||
// Right click with the drill: a resonance ping. The echo is drawn by drawPing (overlays.js).
|
||||
const PING_RECHARGE = 180;
|
||||
const PING_RECHARGE = 600;
|
||||
// Seconds until the ping is ready again (0: ready).
|
||||
function pingWait() { return GAME.pingTick !== undefined && tick >= GAME.pingTick ? Math.max(0, Math.ceil((PING_RECHARGE - (tick - GAME.pingTick)) / 60)) : 0; }
|
||||
function firePing() {
|
||||
if (GAME.ping && tick >= GAME.ping.tick && tick - GAME.ping.tick < PING_RECHARGE) { setStatus('The ping is still ringing; try again in a moment.'); playSfx('error'); return; }
|
||||
if (pingWait()) { setStatus(`The ping is recharging: ${pingWait()} s.`); playSfx('error'); return; }
|
||||
const p = resonancePing(bot.x + bot.w / 2, bot.y + bot.h / 2);
|
||||
GAME.ping = Object.assign(p, { tick, t0: performance.now() });
|
||||
GAME.ping = Object.assign(p, { t0: performance.now() }); GAME.pingTick = tick;
|
||||
playSfx('pulse', 2);
|
||||
const names = p.found.map(m => MD[m].name.toLowerCase());
|
||||
setStatus(names.length ? `Ping: ${names.slice(0, 3).join(', ')}${names.length > 3 ? ' and more' : ''} nearby.` : `Ping: no veins within ${Math.round(p.r / BS)} blocks.`, 'info');
|
||||
|
||||
@@ -19,7 +19,7 @@ function renderSelCard() {
|
||||
const b = BIT_TIERS[player.bitTier];
|
||||
const pw = PULSE_W[GAME.drillW], tn = GAME.drillTune;
|
||||
h = `<div class="row cycle"><kbd>Q</kbd><span class="name grow">Resonance drill</span><span class="chip hot">${pw.name} pulse</span><kbd>E</kbd></div>
|
||||
<div class="row" style="flex-wrap:wrap;gap:4px"><span class="chip">${esc(b.name.toLowerCase())}</span><span class="chip">hardness ≤ ${b.hard}</span><span class="chip">reach ${drillReach(GAME.drillW)}</span>${tn ? `<span class="chip ok"><span class="sw" style="display:inline-block;vertical-align:-2px;width:9px;height:9px;margin-right:4px;background:${rgbOf(tn)}"></span>tuned: ${esc(MD[tn].name.toLowerCase())} · R</span>` : '<span class="chip">untuned · R tunes</span>'}</div>
|
||||
<div class="row" style="flex-wrap:wrap;gap:4px"><span class="chip">${esc(b.name.toLowerCase())}</span><span class="chip">hardness ≤ ${b.hard}</span><span class="chip">reach ${drillReach(GAME.drillW)}</span>${pingWait() ? `<span class="chip">ping ${pingWait()}s</span>` : '<span class="chip ok">ping ready</span>'}${tn ? `<span class="chip ok"><span class="sw" style="display:inline-block;vertical-align:-2px;width:9px;height:9px;margin-right:4px;background:${rgbOf(tn)}"></span>tuned: ${esc(MD[tn].name.toLowerCase())} · R</span>` : '<span class="chip">untuned · R tunes</span>'}</div>
|
||||
<div class="sub">Hold to send pulses at the cursor: rock they shake hard enough shatters and falls. <kbd>Q</kbd>/<kbd>E</kbd> width: narrow bores a long tunnel fast, wide clears a big face up close. <kbd>R</kbd> over a material tunes to it, so only that shatters. Right click pings: veins nearby light up, even unexplored. Built blocks are never harmed.</div>`;
|
||||
} else if (t === 'build') {
|
||||
// the build tool in general; the piece itself is described in the menu's hover popup
|
||||
|
||||
Reference in New Issue
Block a user