Splatter feel pass: baseline turret, tennis duel, rapid serves, bleeding wounds
- Stand at the baseline: in splatter the player is pinned to the baseline (a turret, side-to-side only) — the duel is exempt so it keeps free depth. - Zombie 1:1 duel now plays like real tennis: no free-aim flick, no drag reticle; lane aim + the toss-then-hit serve (gate on !G.interlude in doHit/hitServe/keyPressed/canAimServe). - Rapid serves: the two-stage reticle serve now fires mid-rally too — a ball still in flight is banished to a ballistic clone so the next serve launches at once. Serve as fast as you can drag-release. - Wounded skins for multi-hit foes: brute (arm off) and racket boss (arm off, then headless) swap by remaining hp, with a graceful fallback to the healthy sprite. The headless boss is drawn a head shorter so the shoulders line up. - Dynamic gore: the blood is NOT painted in — open wounds gush live particles every frame (arm stumps + a neck geyser on the headless boss), absurdly, and bounded so it self-regulates. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
7936d83f0f
commit
df4eaf6e39
10 files changed
+53
-21
No files matched your search
+19
@@ -155,6 +155,23 @@ export function explode(x, z) {
|
||||
if (G.idleT - G.lastOrderT > 3) { voice('order', 0.35); G.lastOrderT = G.idleT; } // the umpire loses composure
|
||||
}
|
||||
|
||||
// wounded zombies gush blood — continuously and absurdly — from every open wound. Anchors are
|
||||
// approximate body-height offsets (drawStatic never mirrors, so a fixed side stays put); the
|
||||
// parts loop's gravity arcs each spurt into a spray. Bounded so the geyser self-regulates.
|
||||
function bleed(zb) {
|
||||
if (parts.length > 520) return; // absurd, but not unbounded
|
||||
let wounds;
|
||||
if (zb.variant === 'g' && zb.hp < TUNE.bruteHp) wounds = [[-0.3, 1.95, 3.2]]; // brute: torn-off arm
|
||||
else if (zb.variant === 'r' && zb.hp <= 1) wounds = [[0.05, 1.5, 6.5], [0.4, 1.15, 2.6]]; // headless: neck geyser + torn arm
|
||||
else if (zb.variant === 'r' && zb.hp === 2) wounds = [[0.4, 1.2, 3]]; // racket: torn-off arm
|
||||
else return;
|
||||
for (const w of wounds)
|
||||
for (let k = 0; k < 4; k++) parts.push({
|
||||
x: zb.x + w[0] + (Math.random() * 2 - 1) * 0.12, y: w[1] + (Math.random() * 2 - 1) * 0.1, z: zb.z + (Math.random() * 2 - 1) * 0.12,
|
||||
vx: (Math.random() * 2 - 1) * 1.9, vy: w[2] * (0.5 + Math.random() * 0.7), vz: (Math.random() * 2 - 1) * 1.4,
|
||||
t: 0.5 + Math.random() * 0.6, dark: Math.random() < 0.45 });
|
||||
}
|
||||
|
||||
export function updateZombies(dt) {
|
||||
moanT -= dt;
|
||||
if (moanT <= 0) { moan(); moanT = 2.5 + Math.random() * 4; }
|
||||
@@ -183,6 +200,8 @@ export function updateZombies(dt) {
|
||||
if (!zb) continue; // a kill can cascade into sweepCourt (game won mid-loop) and gut the array under us
|
||||
zb.walkT += dt;
|
||||
zb.z += zb.v * dt;
|
||||
bleed(zb); // open wounds bleed every frame they're standing
|
||||
|
||||
if (zb.shieldT > 0) zb.shieldT -= dt; // staggered chair comes back up when this runs out
|
||||
if (zb.variant === 'r') { /* the racket boss holds station — its AI owns its x */ }
|
||||
else if (G.mode === 'splatter' && zb.z > COURT.netZ) { // on YOUR half they close in — each on its own lane, not a column
|
||||
|
||||
Reference in new issue
Block a user