The nozzle blows no further than it sucks (t34)

With the cursor outside the reach circle, blown grains aim at the circle's edge in the cursor's
direction instead of flying all the way to the cursor, and the blow preview stops there too.
New test/t_nozzle.js and a play.py preview check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 16:35:38 -04:00
co-authored by Claude Opus 5.5
parent 64ff8310f3
commit 5ce44f842e
6 changed files with 71 additions and 6 deletions
+6 -2
View File
@@ -31,7 +31,7 @@ function toolVacuum(cx, cy, r, pick) {
});
return { got, m: first, full, status: full ? 'Bag full; blow material out or cast a bigger tank.' : '' };
}
// Blow: grains leave the bot's nozzle and fly to the cursor, where they drop.
// Blow: grains leave the bot's nozzle and arc toward the cursor, within reach.
// Liquids need the Flask. Bars are thrown, one at a time.
function nozzle(tx, ty) {
const cx = bot.x + bot.w / 2, cy = bot.y + bot.h / 2;
@@ -65,6 +65,10 @@ function toolBlow(cx, cy, r, m, mix) {
if (!e) return { placed: 0, status: 'No room to throw a bar from here.' };
bagTake(m, 1); return { placed: 1, status: '' };
}
if (!inReach(cx, cy)) {
cx = nz.ox + nz.dx * nz.dist - 0.5;
cy = nz.oy + nz.dy * nz.dist - 0.5;
}
if (flyers.length > 3000) return { placed: 0, status: '' };
// nozzle 0 is a trickle for careful work, the widest a torrent
const rate = 1 + r + ((r * r) >> 1);
@@ -73,7 +77,7 @@ function toolBlow(cx, cy, r, m, mix) {
for (let k = 0; k < n; k++) {
const gm = pickM ? pickM() : m;
if (!gm) break;
// aim each grain at a spot in the brush around the cursor, on an arc that passes
// aim each grain at a spot in the brush around the target, on an arc that passes
// through it; past that point gravity carries it on until it hits something
const a = rnd() * Math.PI * 2, rr = Math.sqrt(rnd()) * (r + 0.5);
const tx = cx + 0.5 + Math.cos(a) * rr, ty = cy + 0.5 + Math.sin(a) * rr;
+3 -1
View File
@@ -80,7 +80,9 @@ function drawPreviews() {
const r = t === 'ignite' ? 1 : GAME.brush[GAME.tool];
const reachOk = inReach(ax, ay);
const blowing = t === 'vacuum' && GAME.rmb && !GAME.rmbConsumed;
ctx.strokeStyle = !reachOk ? 'rgba(239,106,85,0.8)' : t === 'drill' ? 'rgba(239,230,216,0.8)' : blowing ? 'rgba(255,210,122,0.9)' : t === 'vacuum' ? 'rgba(92,186,166,0.95)' : 'rgba(255,154,60,0.9)';
const nz = blowing && !reachOk ? nozzle(ax + 0.5, ay + 0.5) : null;
const p = nz ? worldToScreenPx(nz.ox + nz.dx * nz.dist - 0.5, nz.oy + nz.dy * nz.dist - 0.5) : worldToScreenPx(ax, ay);
ctx.strokeStyle = !reachOk && !nz ? 'rgba(239,106,85,0.8)' : t === 'drill' ? 'rgba(239,230,216,0.8)' : blowing ? 'rgba(255,210,122,0.9)' : t === 'vacuum' ? 'rgba(92,186,166,0.95)' : 'rgba(255,154,60,0.9)';
ctx.lineWidth = Math.max(1, zp * 0.25);
if (t === 'vacuum') ctx.setLineDash([zp, zp * 0.6]);
ctx.beginPath(); ctx.arc(p.x + zp / 2, p.y + zp / 2, (r + 0.5) * zp, 0, Math.PI * 2); ctx.stroke(); ctx.setLineDash([]);