The card lies flat, and a popup owns its screen
- icon LEFT of the text: every card is 62px at a fixed top, fully over the crowd — the stacked layout hung its lower third over live court, and the art draws bigger this way, not smaller - nothing draws or ticks under a modal: a card raised in the same beat used to burn out dimmed beneath it — it holds and plays after Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1337048a85
commit
911035af00
4 files changed
+39
-26
No files matched your search
@@ -442,6 +442,15 @@ the countdown panel ('CHANGEOVER · RESUMING IN n'). He kept the burst-arm weapo
|
||||
praise words. **A bottom/above-rack message slot was proposed and REJECTED**: that is thumb
|
||||
territory — covered by the hand mid-pull and outside where the eyes are.
|
||||
|
||||
**Fourth batch, same day — the card went HORIZONTAL, and nothing draws under a modal.** The
|
||||
stacked icon card ran 102 px tall and its lower third hung over live court; the icon sits LEFT of
|
||||
the text now (boxed to 58×52 so a wide tin cannot reach the type — and it draws BIGGER than the
|
||||
stacked layout gave it), so every card is a flat `CARD_H` 62 px at fixed `CARD_TOP` 96, fully over
|
||||
the crowd. `bandTop()` is gone with it. And a modal owns the whole screen now: the flash and the
|
||||
card are neither DRAWN under its dim nor TICKED behind the freeze (the card tick moved below the
|
||||
modal early-return, next to the flash's) — a card raised in the same beat as a popup used to burn
|
||||
its whole life dimmed-out underneath it, and now it holds and plays after the dismissal.
|
||||
|
||||
**Second batch, same day — THE MESSAGE BAND.** Oliver: transient messages need "a definite space
|
||||
… over the crowd". `AD_TOP()`/`bandTop(h)` in render.js pin the whole family there — below the
|
||||
scoreboard block, above the ad strip, never on the court:
|
||||
|
||||
+8
-6
@@ -1326,12 +1326,6 @@ export function update(dt) {
|
||||
updateZombies(dt);
|
||||
return;
|
||||
}
|
||||
// A card can wait before it shows. The chapter card used to land on the same frame as the boss
|
||||
// died, so the biggest gore burst in the game happened underneath it and nobody ever saw it.
|
||||
if (G.card) {
|
||||
if (G.card.wait > 0) G.card.wait -= dt;
|
||||
else { G.card.t -= dt; if (G.card.t <= 0) G.card = null; }
|
||||
}
|
||||
if (G.shake && (G.shake.t -= dt) <= 0) G.shake = null;
|
||||
if (G.netRipple && (G.netRipple.t -= dt) <= 0) G.netRipple = null; // the tape settles
|
||||
if (G.zoom && (G.zoom.t += dt) >= G.zoom.T) G.zoom = null; // scripted punch-in (render applies it)
|
||||
@@ -1351,6 +1345,14 @@ export function update(dt) {
|
||||
// late in a run — the weapon modal is first-sight-only, so once you have seen a tin the label
|
||||
// flash IS the announcement, and the trainer's popup was eating it every time you were wounded.
|
||||
if (G.flash) { G.flash.t -= dt; if (G.flash.t <= 0) G.flash = null; }
|
||||
// A card can wait before it shows. The chapter card used to land on the same frame as the boss
|
||||
// died, so the biggest gore burst in the game happened underneath it and nobody ever saw it.
|
||||
// BELOW the modal return, like the flash: a card raised in the same beat as a popup used to
|
||||
// burn its whole life dimmed-out underneath it — now it holds and plays AFTER the dismissal.
|
||||
if (G.card) {
|
||||
if (G.card.wait > 0) G.card.wait -= dt;
|
||||
else { G.card.t -= dt; if (G.card.t <= 0) G.card = null; }
|
||||
}
|
||||
for (let i = pops.length - 1; i >= 0; i--) if ((pops[i].t -= dt) <= 0) pops.splice(i, 1); // reaction pops decay
|
||||
crowd.cheer = Math.max(0, crowd.cheer - dt);
|
||||
G.idleT += dt;
|
||||
|
||||
+1
-1
@@ -359,6 +359,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="main.js?v=131"></script>
|
||||
<script type="module" src="main.js?v=132"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1178,30 +1178,29 @@ 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
|
||||
// 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);
|
||||
// The band has ROWS: the flash line owns 68..90 (FLASH_Y, Bangers 18), the countdown panel the
|
||||
// strip under it, and the card a FIXED slot below both. The card is ONE compact height now, icon
|
||||
// or not — the icon sits LEFT of the text instead of stacking above it. Stacked, an icon card ran
|
||||
// 102 px tall and its lower third hung over live court; side-by-side it is 62 px flat, sits fully
|
||||
// over the crowd, and the art actually draws BIGGER than the stacked layout gave it.
|
||||
const CARD_TOP = 96, CARD_H = 62;
|
||||
|
||||
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
|
||||
// rather than the art crowding the type.
|
||||
const img = icon && SPR[icon], ih = img ? 34 : 0;
|
||||
const y0 = bandTop(68 + ih);
|
||||
ctx.fillStyle = 'rgba(10,6,8,0.72)'; ctx.fillRect(0, y0, W, 68 + ih);
|
||||
ctx.textAlign = 'center';
|
||||
if (img) { // the tin itself, at its own aspect, sat on the title
|
||||
const w = Math.round(img.width * (ih - 6) / img.height);
|
||||
const img = icon && SPR[icon];
|
||||
ctx.fillStyle = 'rgba(10,6,8,0.72)'; ctx.fillRect(0, CARD_TOP, W, CARD_H);
|
||||
if (img) { // left of the text block, vertically centred, boxed so a wide tin can't reach the type
|
||||
const k = Math.min((CARD_H - 10) / img.height, 58 / img.width);
|
||||
const iw = Math.round(img.width * k), ih = Math.round(img.height * k);
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
ctx.drawImage(img, Math.round(W / 2 - w / 2), y0 + 3, w, ih - 6);
|
||||
ctx.drawImage(img, 8, Math.round(CARD_TOP + (CARD_H - ih) / 2), iw, ih);
|
||||
}
|
||||
// the title still SHOUTS (Bangers) but at 22, not 30 — up here it shares the screen with the
|
||||
// scoreboard and the ads, and 30 read as oversized the moment it left centre court
|
||||
ctx.font = B(22); shadowText(title, W / 2, y0 + ih + 8, color);
|
||||
ctx.textAlign = 'center';
|
||||
ctx.font = B(22); shadowText(title, W / 2, CARD_TOP + 4, color);
|
||||
ctx.font = F(8);
|
||||
if (sub1) shadowText(sub1, W / 2, y0 + ih + 36, UI.cream);
|
||||
if (sub2) shadowText(sub2, W / 2, y0 + ih + 50, UI.cream);
|
||||
if (sub1) shadowText(sub1, W / 2, CARD_TOP + 34, UI.cream);
|
||||
if (sub2) shadowText(sub2, W / 2, CARD_TOP + 48, UI.cream);
|
||||
}
|
||||
|
||||
// A GHOST HAND, fingertip at (x, y) — the tutorial finger every gesture hint shares. A drawn
|
||||
@@ -1825,7 +1824,10 @@ export function render() {
|
||||
// The free-floating register: no panel, one fixed slot, shouted. PERFECT, GAME N, DOUBLE KILL,
|
||||
// RETURNED, CHANGEOVER, weapon labels — everything that used to be plain white 12px text sitting
|
||||
// directly on the crowd. It keeps its 1px jitter; the shadow is what makes it legible anywhere.
|
||||
if (G.flash) {
|
||||
// ...and neither the flash nor the card draws UNDER a modal: the popup owns the screen, and a
|
||||
// band message half-visible through its dim was clutter saying nothing. Their clocks are frozen
|
||||
// behind the freeze too (see update), so both play out in full once the popup is dismissed.
|
||||
if (G.flash && !G.modal) {
|
||||
// 18, not 24: at the card's near-size a transient one-liner read as a milestone announcement,
|
||||
// and half of what lands in this slot (PERFECT, RETURNED, DOUBLE KILL) is only ever a footnote
|
||||
// to something you already watched happen. The register is right; the volume was not.
|
||||
@@ -1833,7 +1835,7 @@ export function render() {
|
||||
shadowText(G.flash.text, W / 2, FLASH_Y + Math.sin(G.flash.t * 20) * 1, '#ffffff');
|
||||
}
|
||||
// the SET milestone card (the duel's own announcement is a real modal now, not a timed card)
|
||||
if (G.card && G.card.t > 0 && !(G.card.wait > 0)) // `wait` holds it OFF the screen (a boss dying under it)
|
||||
if (G.card && !G.modal && G.card.t > 0 && !(G.card.wait > 0)) // `wait` holds it OFF the screen (a boss dying under it)
|
||||
drawCard(G.card.title, G.card.sub1, G.card.sub2, G.card.color, G.card.icon);
|
||||
if (TUNE.missHud && G.lastMiss && G.idleT - G.lastMiss.at < 3.5) { // last miss reason, live
|
||||
ctx.font = F(8); ctx.fillStyle = '#ff9060';
|
||||
|
||||
Reference in new issue
Block a user