Pneumatic nozzle: air streaks while sucking and blowing (t25)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 15:27:18 -04:00
co-authored by Claude Opus 5.5
parent 638fd3bdb7
commit e2c9bc9c58
6 changed files with 100 additions and 5 deletions
+37
View File
@@ -121,6 +121,42 @@ function drawDrillAim(ax, ay) {
if (GAME.drillTune) { const sz = Math.max(6, zp * 1.4); ctx.fillStyle = rgbOf(GAME.drillTune); ctx.fillRect(p.x + k * 1.8, p.y - k * 1.8 - sz, sz, sz); ctx.strokeStyle = 'rgba(0,0,0,0.5)'; ctx.lineWidth = 1; ctx.strokeRect(p.x + k * 1.8 + 0.5, p.y - k * 1.8 - sz + 0.5, sz - 1, sz - 1); }
ctx.restore();
}
// Air swirls into the pickup ring or spreads from the nozzle toward the cursor.
function drawAirStreaks(ax, ay, p, r, blowing) {
if (!SET.particles || (SET.fog && fogSeen(ax, ay) < 64)) return;
const ctx = R.ctx, zp = R.zp, now = performance.now();
const cx = p.x + zp / 2, cy = p.y + zp / 2, ring = (r + 0.5) * zp;
ctx.save(); ctx.lineWidth = Math.max(1, zp * 0.2); ctx.lineCap = 'round';
if (blowing) {
const bx = bot.x + bot.w / 2, by = bot.y + bot.h / 2;
const dx = ax + 0.5 - bx, dy = ay + 0.5 - by, d = Math.hypot(dx, dy) || 1;
const o = worldToScreenPx(bx + dx / d * 3, by + dy / d * 3);
const px = cx - o.x, py = cy - o.y, len = Math.hypot(px, py) || 1;
const ux = px / len, uy = py / len;
for (let i = 0; i < 10; i++) {
const phase = (now * 0.0016 + i * 0.618) % 1;
const side = ((i * 0.37) % 1 - 0.5) * 2 * ring * phase;
const x = o.x + px * phase - uy * side, y = o.y + py * phase + ux * side;
ctx.strokeStyle = `rgba(255,210,122,${Math.sin(phase * Math.PI) * 0.55})`;
ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + ux * 1.6 * zp, y + uy * 1.6 * zp); ctx.stroke();
}
} else {
const travel = ring * 0.65 + 3 * zp;
for (let i = 0; i < 12; i++) {
const phase = (now * 0.0012 + i * 0.618) % 1;
const dist = ring + 3 * zp - travel * phase;
const angle = i * Math.PI * 2 / 12 + now * 0.0004 + phase * 1.2;
const co = Math.cos(angle), si = Math.sin(angle);
const x = cx + co * dist, y = cy + si * dist;
const vx = -travel * co - dist * (1.2 + 1 / 3) * si;
const vy = -travel * si + dist * (1.2 + 1 / 3) * co;
const scale = 1.2 * zp / Math.hypot(vx, vy);
ctx.strokeStyle = `rgba(92,186,166,${Math.sin(phase * Math.PI) * 0.5})`;
ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + vx * scale, y + vy * scale); ctx.stroke();
}
}
ctx.restore();
}
function drawPreviews() {
const ctx = R.ctx, zp = R.zp;
const t = toolId(), ax = Math.floor(GAME.aim.x), ay = Math.floor(GAME.aim.y);
@@ -161,6 +197,7 @@ function drawPreviews() {
ctx.strokeStyle = blowing ? 'rgba(255,210,122,0.22)' : 'rgba(92,186,166,0.22)'; ctx.lineWidth = Math.max(1, zp * (r + 0.5) * 0.6);
ctx.beginPath(); ctx.moveTo(c.x, c.y); ctx.lineTo(p.x + zp / 2, p.y + zp / 2); ctx.stroke();
}
if (reachOk && (blowing || GAME.lmb)) drawAirStreaks(ax, ay, p, r, blowing);
}
} else if (t === 'build') {
const P = piece(), b = aimBlock();