Thumb-scale draw meter, looser aim magnet, a bomb worth the tin
- The draw meter is a fixed bar across the bottom, ~90% of the width and 10px tall, with the kill notch on it. It was a 26x4 stub floating on the apron by the player's feet, which is nothing under a thumb at arm's length. The serve hint is suppressed while pulling since they share the slot. pullFull 95 -> 130: at 95 every pull maxed out by accident, so the meter never meant anything. - The aim magnet took the nearest body ALONG the lane, so a close shambler a little off-axis stole shots clearly aimed past it. It now picks the best ALIGNED body, with TUNE.aimTie of slack in which a nearer one still wins — a straight-ahead lane still eats the nearest body first, which is the settled feel. aimTie: 1 restores the old behaviour exactly, for A/B. - Bomb radius 2.5 -> 4.2 (2 kills -> 5 in a scattered ten-wave), a 120-particle eruption, a ring of thrown stains and a full-size pile. The blinding white frame is now a brief warm bloom — it was hiding the very gore it announced. - Rack tiles 22 -> 30 tall, and the touch band is padded above and runs to the screen edge below. At 22 logical px they were ~30 CSS px on a phone, well under the ~44 a thumb expects; now ~62. 26-game soak clean with heavy bomb use, arrays stay bounded. Serve returns 136/150 = 90.7% +/-2.4%, 30 human-tapped points = 50 hits / 0 whiffs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
55ddf5932e
commit
21b9dd53fa
7 files changed
+90
-35
No files matched your search
+13
-3
@@ -34,13 +34,23 @@ export function flickTarget(ch, aim) { // exported: render draws the live reticl
|
||||
const q = pullQuality(aim.mag), f = pullFrac(aim.mag);
|
||||
if (aim.mag < TUNE.pullMin) return { q, f }; // barely drawn: no lane yet, so no target
|
||||
const ax = aim.fx, az = aim.fy; // unit vector; input already inverted the pull. screen up = -z
|
||||
let zt = null, near = 1e9;
|
||||
let zt = null, near = 1e9, bestErr = 1e9;
|
||||
for (const zb of zombies) {
|
||||
const dx = zb.x - ch.x, dz = zb.z - ch.z;
|
||||
const along = dx * ax + dz * az; // how far down the lane it stands
|
||||
if (along <= 0) continue; // behind the shot
|
||||
if (Math.abs(dx * az - dz * ax) > TUNE.zSnap + TUNE.aimCone * along) continue; // outside the cone
|
||||
if (along < near) { near = along; zt = zb; } // the FIRST one in the lane eats it
|
||||
const cone = TUNE.zSnap + TUNE.aimCone * along;
|
||||
const err = Math.abs(dx * az - dz * ax) / cone; // 0 = dead on the ray, 1 = the cone's edge
|
||||
if (err > 1) continue; // outside the cone entirely
|
||||
// Pick the body you are actually POINTING AT, not merely the nearest one that happens to fall
|
||||
// inside the cone. "First in the lane wins" let a close shambler off to one side steal a shot
|
||||
// clearly aimed past it — the magnet Oliver felt. Range only breaks ties between equally
|
||||
// well-aimed targets (TUNE.aimTie of slack), so a deliberate far shot still beats a sloppy
|
||||
// near one while a genuine straight-ahead lane still eats the nearest body first.
|
||||
const tie = TUNE.aimTie;
|
||||
if (err < bestErr - tie || (Math.abs(err - bestErr) <= tie && along < near)) {
|
||||
bestErr = Math.min(bestErr, err); near = along; zt = zb;
|
||||
}
|
||||
}
|
||||
const R = zt ? near : TUNE.aimReach; // empty lane: the ball just flies the full length of it
|
||||
const tx = Math.max(-6.5, Math.min(6.5, ch.x + ax * R));
|
||||
|
||||
Reference in new issue
Block a user