Exact touchdown, the boy finishes his shift, healing announces itself, 4K heads
Five of Oliver's six (the sixth is a play-mode discussion): - THE DOUBLE FAULTS WERE FRAME QUANTIZATION: the bounce registered a full integration step after true contact — ~0.75 m of travel at serve pace — so serves aimed half a metre safe were judged long. Classic enforces the service box, the match only the court, hence 'zombie tennis does not'. The bounce now rewinds to y=0 before anything reads the position. Measured in-pane: 30/40 faults on good taps before, 0/40 after (mistimed and smudged taps also 0/40; aimed hard serves keep their scatter risk). Also: the strike flick dead zone is 44 px so a smudged tap stays a tap, and the aim base clamps 0.18 m inside the lines — scatter, not the aim, is what faults. - The mop beat holds the walk-on until the boy actually LEAVES the court (ordered off at beat end, 6 s cap). - A match game win that restores blood shows the duel's WOUNDS TREATED popup; full-blood wins stay silent. - The opponent walk-on card holds 4.0 s (was 2.4). - Card heads are cut from the 4K sheets at 144 px (assets/_heads.py, z_<fam>head.png, HEAD_CROP stays as fallback) — the old 65 px crops were the 'very low res' heads. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c847bcbfa1
commit
c6218a6284
17 files changed
+178
-23
No files matched your search
+17
-7
@@ -221,7 +221,7 @@ function farB(z) {
|
||||
// Asset cache-buster — bump with main.js?v=N (see the img.src note below). EXPORTED because the
|
||||
// menu screens hang <img> tags on the same files, and two copies of this number is one copy too
|
||||
// many: the one that does not get bumped is the one that serves a cached 404.
|
||||
export const SPRV = 111;
|
||||
export const SPRV = 112;
|
||||
// The boot bar in main.js watches this. Nothing here CHANGES when things load — every sprite was
|
||||
// already requested at import — it just makes the wait visible and holds the menu back until the
|
||||
// last sheet has landed. See the onerror note below for the other half of "sometimes missing".
|
||||
@@ -273,6 +273,9 @@ for (const n of ['p_idle1','p_idle2','p_bounce','p_bounce2','p_toss','p_smash','
|
||||
// filenames typed out here (each of which is a chance to typo one and have a body
|
||||
// vanish for a frame — the oldest bug in this file).
|
||||
...ZFAMS.flatMap(famFrames),
|
||||
// the card's 4K-cut head icons (assets/_heads.py). 'r' has no fam sheet — his
|
||||
// card keeps the HEAD_CROP fallback, so no file and no 404 for him.
|
||||
...ZFAMS.filter(k => k !== 'r').map(k => 'z_' + k + 'head'),
|
||||
'w_ball','w_bomb','w_triple','w_fire','w_gun', // weapon-rack icons
|
||||
'w_beacon','w_laser','w_rocket','w_flamer', // ...and the four that come out of a barrel
|
||||
'z_barrel1','z_barrel2', // the weapon barrel, two frames so it ROLLS
|
||||
@@ -1711,10 +1714,16 @@ function cardBody(c, dx) {
|
||||
// and boss names of any length, and Bangers 22 ran a long one off both edges of a phone. Measure
|
||||
// once and shrink to the band; 22 stays 22 for everything that already fit.
|
||||
// THE OPPONENT CARD CARRIES ITS HEAD (Oliver: the punchline plus a picture of his head). The
|
||||
// icon is a straight crop out of the family's own healthy frame — config.HEAD_CROP, measured,
|
||||
// never separate art — drawn left of the text block, which shifts right to keep the whole
|
||||
// group centred as one. A family with no crop row degrades to the plain text card.
|
||||
const hd = c.head && HEAD_CROP[c.head] && SPR['z_' + c.head + '1'] ? HEAD_CROP[c.head] : null;
|
||||
// icon prefers the 4K-cut z_<fam>head.png (assets/_heads.py — the installed 224 px frames gave
|
||||
// ~65 px heads drawn at 144 device px, which is the "very low res" he reported) and degrades
|
||||
// to the HEAD_CROP crop of the healthy frame, then to the plain text card. Never separate art:
|
||||
// both sources are the body already on court.
|
||||
const headImg = c.head && SPR['z_' + c.head + 'head'];
|
||||
const hd = headImg ? { img: headImg, w: headImg.width, h: headImg.height }
|
||||
: c.head && HEAD_CROP[c.head] && SPR['z_' + c.head + '1']
|
||||
? { img: SPR['z_' + c.head + '1'], box: HEAD_CROP[c.head],
|
||||
w: HEAD_CROP[c.head].w, h: HEAD_CROP[c.head].h }
|
||||
: null;
|
||||
const ih = hd ? CARD_H - 14 : 0, iw = hd ? Math.round(hd.w * ih / hd.h) : 0;
|
||||
const room = W - 24 - (hd ? iw + 8 : 0);
|
||||
ctx.font = B(22);
|
||||
@@ -1731,8 +1740,9 @@ function cardBody(c, dx) {
|
||||
let blockW = 0;
|
||||
for (const [t, f] of lines) { ctx.font = f; blockW = Math.max(blockW, ctx.measureText(t).width); }
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
ctx.drawImage(SPR['z_' + c.head + '1'], hd.x, hd.y, hd.w, hd.h,
|
||||
Math.round(cx - blockW / 2 - 8 - iw), Math.round(CARD_MID - ih / 2), iw, ih);
|
||||
const hx = Math.round(cx - blockW / 2 - 8 - iw), hy = Math.round(CARD_MID - ih / 2);
|
||||
if (hd.box) ctx.drawImage(hd.img, hd.box.x, hd.box.y, hd.box.w, hd.box.h, hx, hy, iw, ih);
|
||||
else ctx.drawImage(hd.img, hx, hy, iw, ih);
|
||||
}
|
||||
for (const [t, f, rel, col] of lines) { ctx.font = f; shadowText(t, cx, y + rel, col); }
|
||||
ctx.textAlign = 'center'; ctx.restore(); // the band's neighbours (flash, countdown) assume centred
|
||||
|
||||
Reference in new issue
Block a user