Fire that looks like fire, a rack you can leave, and a boss you can read
The band work from f352612 rides along; everything else is this round's list.
- proj returns a NEGATIVE scale behind the camera, so four ellipse call sites
could be handed negative radii — ctx.ellipse throws on those, and the throw
killed the rest of the frame (no HUD, no rack, no band). All four now skip
points behind the camera.
- a burst tin is no longer a commitment: tapping another slot aborts it, spends
it and hands back the serve loop. Lives in selectWeapon, so the rack tap and
the 1-0 keys both get it. A horde still locks the rack — that is the one place
your finger is already spoken for. Spending splices the rack, so the tap
resolves to a weapon id and re-finds it rather than trusting the index.
- beacon 1.6 -> 2.4 rounds/s: a lob cannon you could not leave AND could not
feel working was two problems.
- FIRE. Flamer throws fat embers instead of a one-pixel thread; burning bodies
get nine tongues over a heat bloom plus smoke; the fire ball gets a halo and a
real tail; an ash death gets twice the motes at twice the life over a ground
bloom and a column. The flamer had no sfx case at all and was using the gun's
dry tick — it has its own roar now.
- chapter bosses carry an HP bar over their head. The no-HP-bar rule stands for
everything else and the comment says why a boss is the exception.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f352612de3
commit
655b86d27f
7 files changed
+184
-31
No files matched your search
+33
-3
@@ -565,7 +565,27 @@ const slotOf = id => { // -1 if you are not carrying it
|
||||
};
|
||||
export function selectWeapon(i) {
|
||||
if (G.interlude) return; // the sudden-death duel is played under tennis rules — plain balls only
|
||||
// A HORDE is the one place the rack stays locked: your finger is steering the man, the racket
|
||||
// fires itself, and there is no gesture left to spend on a tin. Everything else can be switched
|
||||
// out of, including mid-burst — see below.
|
||||
if (G.horde) return;
|
||||
if (slotStock(i) <= 0) return; // dud: nothing in that slot (slot 0 is only ever empty if a spent
|
||||
// A BURST IS NOT A COMMITMENT. Committing to a burst tin used to lock the rack for its whole
|
||||
// window — eleven seconds of beacon lobs while a late-game crowd walks into you is a death you
|
||||
// can watch coming and do nothing about, which is exactly what Oliver hit. Tapping another slot
|
||||
// ABORTS it: the tin is spent (you did fire it) and the serve loop comes straight back, the same
|
||||
// handover updateZombies does when the window ends on its own. Both input paths land here — the
|
||||
// rack tap and the 1-0 keys — so the abort lives here rather than in either of them.
|
||||
if (G.mgT > 0) {
|
||||
// ...but spending a tin can EMPTY it, and spendSlot splices an empty entry out, which shifts
|
||||
// every slot above it down one. A tap is at a slot; what you meant was the weapon you could see
|
||||
// in it. So resolve to the id first and look it up again afterwards — otherwise aborting a
|
||||
// beacon in slot 1 to reach the rocket in slot 4 hands you whatever slid into slot 4.
|
||||
const want = slotId(i);
|
||||
G.mgT = 0; endBurst(); setupPoint();
|
||||
const j = slotOf(want);
|
||||
i = j >= 0 ? j : 0; // the tin we just spent WAS the one tapped: fall back to BALL
|
||||
}
|
||||
G.weapon = i; // temp has just been cleared, and then it is BALL again)
|
||||
sfx('weapon-click2.mp3', 0.45); // a real mechanical click, quiet — switching is audible, not loud
|
||||
}
|
||||
@@ -1527,9 +1547,19 @@ export function update(dt) {
|
||||
|
||||
// --- ball physics
|
||||
if (ball.active) {
|
||||
if (ball.flame) parts.push({ // fire trail: one ember per frame, brief and rising
|
||||
x: ball.x, y: ball.y, z: ball.z, t: 0.22, fire: true,
|
||||
vx: (Math.random() * 2 - 1) * 0.6, vy: 0.8, vz: (Math.random() * 2 - 1) * 0.6 });
|
||||
// fire trail. One brief ember a frame read as a spark, not as a ball that is ON FIRE — this is
|
||||
// the weapon whose whole pitch is "kills on any touch and sets the others alight", so it now
|
||||
// sheds THREE embers a frame with real spread, they live long enough to hang as a tail behind
|
||||
// the arc, and every third leaves a puff of smoke drifting off the top of it.
|
||||
if (ball.flame) {
|
||||
for (let k = 0; k < 3; k++) parts.push({
|
||||
x: ball.x + (Math.random() * 2 - 1) * 0.14, y: ball.y + (Math.random() * 2 - 1) * 0.14,
|
||||
z: ball.z + (Math.random() * 2 - 1) * 0.14, t: 0.34 + Math.random() * 0.26, fire: true,
|
||||
vx: (Math.random() * 2 - 1) * 0.9, vy: 0.9 + Math.random() * 0.7, vz: (Math.random() * 2 - 1) * 0.9 });
|
||||
if (Math.random() < 0.34) parts.push({ x: ball.x, y: ball.y + 0.15, z: ball.z,
|
||||
vx: (Math.random() * 2 - 1) * 0.3, vy: 0.7, vz: (Math.random() * 2 - 1) * 0.3,
|
||||
t: 0.5 + Math.random() * 0.4, smoke: true });
|
||||
}
|
||||
ball.vy -= (ball.g || TUNE.gravity) * dt;
|
||||
const oldZ = ball.z;
|
||||
ball.x += ball.vx * dt; ball.y += ball.vy * dt; ball.z += ball.vz * dt;
|
||||
|
||||
Reference in new issue
Block a user