Fly the modals in with a swoosh instead of cutting to them
m.in (0..1 over TUNE.modalIn, 0.34 s) is the entrance clock, ticked inside update()'s modal early-return — the one thing that animates while the world is frozen. Icon and title fly in from the right trailing two ghost copies as a speed streak, the body lines cascade in from the left one after another, everything on easeOutBack so it overshoots and settles rather than decelerating politely into place. The dim washes in over the first third. A descending noise sweep on arrival (swish is parameterised now — same six lines of Web Audio, opposite reading) and a low pock as it lands. Every x is rounded: a fractional x on a nearest-neighbour scaled image re-snaps its pixel columns, which is the boiling that once read as HUD flicker. Dismissal is gated on the entrance: while m.in < 1 dismissModal swallows the gesture but doesn't clear the modal, so a tap already in the air when a popup appears can't skip it — and swallowing rather than returning false is what keeps that tap from falling through into a serve. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
79fb43f60d
commit
4cf54603f3
6 files changed
+69
-15
No files matched your search
+15
-3
@@ -2,7 +2,7 @@
|
||||
|
||||
import { TUNE, UI, APPLAUSE_T, EATEN_BURST, COURT, CONTACT, VOLLEY, ZONE_DEPTH, HOME, P_BOUNDS, TOSS, SHOT_DEPTH, POSE_TIME, BOUNCE_IDLE } from './config.js';
|
||||
import { P, O, ball, score, crowd, G, other, deuceSide, setupPoint, sendBoy } from './state.js';
|
||||
import { audio, pock, swish, applause, voice, sfx, aww, zombieMusic, zombieDuck } from './audio.js';
|
||||
import { audio, pock, swish, whoosh, applause, voice, sfx, aww, zombieMusic, zombieDuck } from './audio.js';
|
||||
import { axisX, axisZ, takeFlickAim, touchPX } from './input.js';
|
||||
import { W } from './render.js'; // runtime-only cycle (render imports gameplay), same pattern as input/zombies
|
||||
import { updateZombies, spawnZombie, spawnRacketZombie, zombies, gore, explode, meleeSwipe, extras, parts, pellets, playerGore } from './zombies.js';
|
||||
@@ -226,9 +226,16 @@ function scoreKey() {
|
||||
// on a timer, so a popup can be READ. showModal never clobbers an open one (a kill cascade can
|
||||
// try to open two in the same frame) and only ever touches G — never the zombie array it may
|
||||
// be called from mid-iteration.
|
||||
export function showModal(m) { if (!G.modal) G.modal = m; }
|
||||
export function showModal(m) {
|
||||
if (G.modal) return;
|
||||
m.in = 0; G.modal = m; whoosh(); // 0..1 entrance clock, ticked in update() behind the freeze
|
||||
}
|
||||
export function dismissModal() {
|
||||
const m = G.modal; if (!m) return false;
|
||||
// Still flying in? Eat the gesture but DON'T dismiss. A modal you haven't read yet must not be
|
||||
// skippable by the same panic tap that was mid-air when it appeared — and returning false here
|
||||
// would let that tap fall through into a serve, which is the swallowed-gesture bug all over again.
|
||||
if (m.in < 1) return true;
|
||||
G.modal = null; if (m.onDismiss) m.onDismiss();
|
||||
return true; // the caller must swallow the gesture: dismissing must never also serve
|
||||
}
|
||||
@@ -798,7 +805,12 @@ export function update(dt) {
|
||||
if (G.boomFlash > 0) G.boomFlash -= dt;
|
||||
// A modal freezes the WORLD, not the clock: no zombie steps, no ball flight, no spawn timers,
|
||||
// so nobody gets eaten while reading a popup. idleT keeps running for the blink.
|
||||
if (G.modal) { G.idleT += dt; return; }
|
||||
if (G.modal) {
|
||||
G.idleT += dt;
|
||||
const m = G.modal; // the entrance is the ONE thing that animates behind the freeze
|
||||
if (m.in < 1 && (m.in = Math.min(1, m.in + dt / TUNE.modalIn)) >= 1) pock(0.4, 620); // it lands with a thud
|
||||
return;
|
||||
}
|
||||
crowd.cheer = Math.max(0, crowd.cheer - dt);
|
||||
G.idleT += dt;
|
||||
// "Time." belongs to the changeover alone. It used to ride the generic 'over' expiry, so any
|
||||
|
||||
Reference in new issue
Block a user