The reaction lands where the ball does
- praise was firing at RELEASE, half a second before impact (measured: 32-frame flight, word on frame 0) — it now arms on the ball and pops on the first body it meets, or its first bounce - DOUBLE KILL / MASSACRE / RETURNED rise off the thing that earned them, as world-anchored pop texts — the announcement band is for milestones - the band got rows: a card can no longer print over the flash line - GAME N flash deleted (the scoreboard bar already says it), CHANGEOVER folded into the countdown panel Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9476f354c2
commit
1337048a85
7 files changed
+91
-30
No files matched your search
@@ -4,7 +4,7 @@ import { cv, ctx } from './dom.js';
|
||||
import { TUNE, UI, EATEN_BURST, EATEN_SUMMARY, EATEN_RESTART, COURT, P_BOUNDS, PLAYER_H, BOY_H, UMP_H, UMP_X, MACH_H, BOSS_ART, ZWOUND, woundStage, BOUNCE_IDLE, SPRITE_H, zombieH , WEAPONS, RACK_ROW, TOSS } from './config.js';
|
||||
import { P, O, ball, score, crowd, G, court, tour } from './state.js';
|
||||
import { predictLanding, previewServe, slotId, slotStock } from './gameplay.js';
|
||||
import { zombies, gore, piles, parts, pellets, extras, shots } from './zombies.js';
|
||||
import { zombies, gore, piles, parts, pellets, extras, shots, pops } from './zombies.js';
|
||||
|
||||
// ---------------------------------------------------------------- palette ----
|
||||
// tennis palette -> drained splatter palette; the transition lerps between them.
|
||||
@@ -1178,7 +1178,11 @@ function drawRack() {
|
||||
// CROWD, below the scoreboard block, above the ad strip. Nothing transient sits on the court
|
||||
// anymore — a card at centre court covered the very play it was announcing, non-blocking or not.
|
||||
const AD_TOP = () => Math.round(proj(0, 0, -6).y) - AD_H; // top of the courtside ad strip
|
||||
const bandTop = h => Math.max(56, AD_TOP() - h - 4); // fit an h-px band above the ads
|
||||
// The band has ROWS now: the flash line owns 68..90 (FLASH_Y, Bangers 18) and the countdown panel
|
||||
// the strip under it, so a card may never climb above 96 — a tall icon card used to reach the
|
||||
// flash line and the two printed over each other, which was the "spamming over each other" report.
|
||||
// The floor wins over the ad-strip anchor: a too-tall card overlaps the ads (decoration) instead.
|
||||
const bandTop = h => Math.max(96, AD_TOP() - h - 4);
|
||||
|
||||
function drawCard(title, sub1, sub2, color, icon) {
|
||||
// An ICON (the won-duel tin) needs its own room above the title, so the band grows for it
|
||||
@@ -1646,6 +1650,18 @@ export function render() {
|
||||
ctx.beginPath(); ctx.ellipse(p.x, p.y, p.s * 0.62 * k, p.s * 0.22 * k, 0, 0, 7); ctx.stroke();
|
||||
}
|
||||
if (G.mode !== 'eaten') drawChar(P); // ...the dying man is drawn with the crowd instead (drawDying)
|
||||
// world-anchored reaction pops (zombies.popText): DOUBLE KILL rises off the corpse, the praise
|
||||
// word lands on the impact. Drawn IN the world pass, before the un-shake, so they ride the kick
|
||||
// of the moment they belong to. Small, brief, and where you are already looking — the reactions
|
||||
// left the announcement band because up there they read as one more banner shouting over play.
|
||||
for (const p of pops) {
|
||||
const f = p.t / p.T, pr = proj(p.x, p.y, p.z);
|
||||
ctx.save();
|
||||
ctx.globalAlpha = Math.min(1, f * 3); // holds, then a quick fade at the end
|
||||
ctx.font = B(13); ctx.textAlign = 'center'; ctx.textBaseline = 'top';
|
||||
shadowText(p.text, Math.round(pr.x), Math.round(pr.y - (1 - f) * 14), p.col || '#ffffff');
|
||||
ctx.restore();
|
||||
}
|
||||
// (a PERFECT-contact spark ring lived here for one build — Oliver's verdict: too much. The
|
||||
// hitstop, the click and the praise word already carry the moment.)
|
||||
// The braced "rambo racket" is NOT drawn any more. It was a procedural racket laid over a sprite
|
||||
@@ -1762,7 +1778,8 @@ export function render() {
|
||||
// In the message band with the rest of the family, and on its OWN PANEL: bare shadowed text
|
||||
// was barely readable over the sunlit clay crowd (Oliver flagged it every court since).
|
||||
// Plain Silkscreen on purpose — a countdown is information, not an announcement.
|
||||
const txt = 'RESUMING IN ' + Math.ceil(waitT);
|
||||
// the changeover names itself here — its old separate flash was the same word, twice
|
||||
const txt = (G.changeover > 0 ? 'CHANGEOVER · ' : '') + 'RESUMING IN ' + Math.ceil(waitT);
|
||||
const y = Math.min(FLASH_Y + 26, AD_TOP() - 18);
|
||||
ctx.font = F(10);
|
||||
const tw = Math.ceil(ctx.measureText(txt).width);
|
||||
|
||||
Reference in new issue
Block a user