diff --git a/HANDOFF.md b/HANDOFF.md
index 616b713..cdf6c48 100644
--- a/HANDOFF.md
+++ b/HANDOFF.md
@@ -320,6 +320,44 @@ zombie (now an occasional opponent, `TUNE.racketEvery`, not the every-game fixtu
frame entirely. `xLim = Math.max(xVis, COURT.W / 2)` — the camera limit, floored at the sideline so
every legal ball stays catchable.
+## Session log: the second pass on the barrel weapons (2026-08-03)
+
+**The auto-fire jog is gone, and it was one guard.** `gameplay` skipped the pose update in `mgSteer`
+mode (`if (!mgSteer && ...) P.pose = moving ? 'run' : 'idle'`), so the pose stuck on 'idle' — and
+`render` covered for it by faking a run cycle off `G.idleT`, which animated whether he was moving or
+not. That fake is what Oliver saw: *"this hectic run animation... looked odd all the time."* The pose
+follows MOVEMENT in every mode now and the fake is deleted, so auto-fire (and the horde, and the
+melee racket) uses the ordinary run animation and standing still is standing still. The comment that
+justified the fake — "the idle sprite has a racket at his hip AND drawMgRacket puts one in his hands,
+two rackets" — was solving a smaller problem than it caused; at sprite scale the overlap does not read.
+
+**The laser, second pass.** Four things, all of them "it must read as light he fired":
+- **It draws AFTER the net and every body.** It was drawn before the far-side pass, so the net and
+ the crowd painted over it — which is precisely why it read as lying on the floor and being *under*
+ the net. Light in the air goes last.
+- **`beamLen` 26 -> 34.** At 26 it stopped a metre past the far baseline and read as being CUT OFF in
+ mid-court. It now carries into the courtside ads, which is where light should stop.
+- **`beamW0` 0.35 -> 0.06** — a POINT at the strings. At 0.35 it left the racket as a 0.7 m bar
+ hanging in mid-air rather than as something emitted.
+- **It DELETES ordinary bodies**, hp irrelevant (`killZombie`, not a wound) — a plane of light that
+ left a roller man standing made it a worse ball, not a weapon. A BOSS is the exception and takes
+ `dmgBeam` 8: "they also must suffer severely" but they survive.
+- **The look is four nested wedges composited `lighter`** — violet bloom, the weapon's blue, cyan,
+ white core — each fading along its length. One quad with a gradient was a coloured shape; layers
+ sharing an axis and brightening toward the centre is what reads as sci-fi.
+
+**Rocket:** speed 17 -> 38 and a real JET plume. The plume is laid down tight ON the flight line
+(`jet`, spread along this frame's step, near-zero velocity, additive hot-white -> orange -> grey)
+instead of scattered sparks with their own velocities, which looked like a firework.
+
+**Beacon:** a finer trail (`trail` 1 at `trailR` 0.035, one-pixel motes) and a much brighter ball
+(`glow` 3.2, a two-ring additive bloom and a fat white core), so the BALL is the bright thing and the
+trail only says where it has been.
+
+**The bug in the middle of all that:** `firePellet` stamps a whitelist of fields onto the round, and
+`jet`/`trailR` were added to the WEAPONS rows without being added to that list — so the rocket flew
+with no plume at all and nothing errored. **Any new pellet field has to be added in both places.**
+
## TEST MODE — triple-tap the scoreboard
A sandbox for trying new content without playing twenty games to reach it. Three taps on the
diff --git a/config.js b/config.js
index c908605..07d2aa0 100644
--- a/config.js
+++ b/config.js
@@ -240,11 +240,15 @@ export const TUNE = {
testLevel: 20, // the roster level pickVariant pretends to be at, so every body type can turn up
testBarrelEvery: 14,// s between barrels, so the pickup itself is testable too
// ---- THE LASER's wedge: aim and release like a serve, and a plane of light widens down the lane
- beamLen: 26, // m it reaches — the whole court, so a lane you aim is a lane you clear
- beamW0: 0.35, // m half-width at the racket...
- beamW1: 4.2, // ...and at the far end. It is a CONE: the spread is the point of it
+ beamLen: 34, // m it reaches. Was 26, which stopped at the far baseline and read as the beam
+ // being CUT OFF in mid-court; the player stands at z~24, so this carries it past
+ // the baseline and up into the courtside ads, which is where light should stop.
+ beamW0: 0.06, // m half-width at the racket — a POINT. At 0.35 it left the racket as a 0.7 m
+ // bar hanging in the air rather than as something emitted from the strings.
+ beamW1: 5.0, // ...and at the far end. It is a CONE: the spread is the point of it
beamT: 0.42, // s the wedge stays drawn
- dmgBeam: 3, // hp the beam takes off anything it catches (dmgBomb is 3, and it reads the same)
+ dmgBeam: 8, // what it takes off a BOSS. An ordinary body is not damaged by the beam, it is
+ // DELETED by it (see fireBeam); bosses "must suffer severely" but survive.
// ---- the weapon barrel: the only way to get the four weapons that are not on the duel ladder
barrelEvery: 3, // games between barrels being rolled on (like the ball machine's machEvery)
barrelRoll: 2.6, // m/s it trundles across the far court — fast enough to have to be led
@@ -460,7 +464,7 @@ export const WEAPONS = {
// A LOBBED tennis ball twice the weight of yours, thrown on its own beat, changing colour every
// shot and dragging its colour behind it. The showiest thing in the game and the one you meet first.
beacon: { label: 'BEACON', icon: 'w_beacon', tin: 1, kind: 'burst', len: 11, rate: 1.6,
- r: 1.2, dmg: 2, hp: 4, arc: 1, hue: 1, trail: 3, glow: 1.6, speed: 19,
+ r: 1.2, dmg: 2, hp: 4, arc: 1, hue: 1, trail: 1, trailR: 0.035, glow: 3.2, speed: 19,
desc: ['IT THROWS ITSELF', 'TWICE THE BALL, EVERY COLOUR'] },
// "make this one way more pronounced": twice the rounds a second, a wider and longer cone, fat
// glowing rounds that throw their own embers, and every one of them lights what it touches.
@@ -478,9 +482,9 @@ export const WEAPONS = {
desc: ['AIM AND RELEASE', 'A WIDENING WALL OF LIGHT'] },
// Lobbed, self-throwing, and it detonates like a bomb where it lands. Deliberately a LATE pickup
// (see pickBarrelWeapon) — Oliver: "best later in the game when there is really a lot going on".
- rocket: { label: 'ROCKET', icon: 'w_rocket', tin: 1, kind: 'burst', len: 9, rate: 0.85,
- r: 0.75, dmg: 3, hp: 9, arc: 1, boom: true, trail: 2, speed: 17,
- col: '#ffd050', glow: 1.4,
+ rocket: { label: 'ROCKET', icon: 'w_rocket', tin: 1, kind: 'burst', len: 9, rate: 1.1,
+ r: 0.75, dmg: 3, hp: 9, arc: 1, boom: true, jet: 3, speed: 38,
+ col: '#ffd050', glow: 1.8,
desc: ['IT LAUNCHES ITSELF', 'AND EVERY ONE IS A BOMB'] },
};
// What a pink barrel can be carrying — the four above, which you cannot win any other way. NOT a
diff --git a/gameplay.js b/gameplay.js
index 0b4d893..e924b19 100644
--- a/gameplay.js
+++ b/gameplay.js
@@ -1204,7 +1204,11 @@ export function update(dt) {
P.z = Math.max(P_BOUNDS.zMin, Math.min(P_BOUNDS.zMax, P.z));
const moving = mx || mz || assist !== 0;
P.runT += moving ? dt : 0;
- if (!mgSteer && P.pose !== 'swing' && P.pose !== 'serve') P.pose = moving ? 'run' : 'idle';
+ // Auto-fire used to be excluded here, which left the pose stuck on 'idle' — and render then
+ // faked a run cycle off G.idleT so he was not a statue. That fake is what read as "this hectic
+ // run animation" whether he was moving or not. The pose follows MOVEMENT in every mode now, so
+ // auto-fire uses the ordinary run animation and standing still is standing still.
+ if (P.pose !== 'swing' && P.pose !== 'serve') P.pose = moving ? 'run' : 'idle';
if (mx) P.facing = mx > 0 ? 1 : -1;
else if (Math.abs(assist) > 0.25) P.facing = assist > 0 ? 1 : -1; // hysteresis: small corrections don't flip facing
}
diff --git a/index.html b/index.html
index aff33c0..6e25eee 100644
--- a/index.html
+++ b/index.html
@@ -179,6 +179,6 @@
-
+