diff --git a/PROJECT.md b/PROJECT.md index e602674..598f350 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -1832,6 +1832,25 @@ pipeline's knowledge: faces RIGHT by convention — the gameplay.js note at the swing aim). The new rows are all drawn facing right, which quietly fixed it. +## THE TWELVE-RUNG LADDER — four new families, and the pair-reference lesson (2026-08-13) + +Oliver: at least 12 opponents. His picks: **DIRECTOR, CHAMPION, PHYSIO, MASCOT** — all four already +existed as chapter bosses, so each family anchors on its own settled boss frame (`z_dir1`, +`z_champ1`, `z_physio1`, `z_mascot1`): the rung and the boss are the same person, which is the +roster's narrative. Rolled through `_famroll.py` (rows added), cut with `_zsheet.py`, HEAD_CROP +measured for the four. The ladder is now 12 rungs (mascot at 5, physio at 8, director 11, +champion 12) and **`matchFoe` is DECOUPLED from `bossArt`'s cycling** — his call, champion caps +rung 12 while chapters keep their 10-rotation. The lists share characters, not arithmetic. + +**THE PAIR REFERENCE, and when words lose.** The champion came back club-pro-slim through FOUR +rolls — two prompt escalations ("shoulders TWICE as wide", "if he is as slim as image 1 it is +WRONG") and a general build-override clause in `_famroll.prompt()` all lost to the pose sheet's +figures. `char_ref` now takes a `pair`: the character pasted NEXT TO a club-pro frame on one +ground line, so the size difference is shown, not described. Even that only half-worked, so the +champion's mass is STATURE instead: `OPPONENTS` rows carry an optional `h` (world height, read by +`zombieH` so the serve aims at the body actually drawn) — champion 2.1 m, the Bulk 2.0 m. When a +build matters, budget rolls for it, and remember the fallback is a data row, not a fifth roll. + ## THE ZOMBIE FAMILY PIPELINE — one sheet, one character, 31 sprites (2026-08-12) **This replaces the nine-batch flow above for a whole opponent family.** `_regen.py` still exists and diff --git a/assets/_famroll.py b/assets/_famroll.py index 9ec14f7..f8207ee 100644 --- a/assets/_famroll.py +++ b/assets/_famroll.py @@ -37,6 +37,23 @@ FAMS = { 'ru': ('z_f1', 'a wiry FAST ZOMBIE RUNNER in a sleeveless tennis top, lean and forward-leaning'), 'gk': ('z_boss1', 'a heavy ZOMBIE GROUNDSKEEPER in stained work overalls and a shapeless hat'), 'lj': ('z_judge1', 'a ZOMBIE LINE JUDGE in a dark blazer and pale trousers, upright and severe'), + # the 2026-08-13 four (Oliver picked them): each anchors on its own settled BOSS frame, so the + # match opponent and the chapter boss are the same person — which is the roster's whole narrative + 'dr': ('z_dir1', 'a ZOMBIE TOURNAMENT DIRECTOR in a navy club blazer with a gold crest, a striped ' + 'tie, glasses, a laminated ID badge on a lanyard, and tan trousers — stiff and officious'), + 'ch': ('z_champ1', 'an ENORMOUS HULKING ZOMBIE CHAMPION, a mountain of muscle: shoulders TWICE as ' + 'wide as the figures in image 1, arms thicker than their legs, a massive barrel ' + 'chest straining a stained vintage white tennis kit with a gold-trimmed collar, ' + 'a bandage wrapped around his head, sweatbands. Image 2 shows him STANDING NEXT ' + 'TO one of image 1\'s men at true scale: THAT is the size difference every ' + 'figure keeps, in every pose. If he is as slim as his neighbour, it is WRONG', + 'z_r1'), + 'ph': ('z_physio1', 'a ZOMBIE PHYSIO in a white polo shirt with a red cross on the chest, glasses, ' + 'bright blue medical gloves, and dark green trousers'), + 'ma': ('z_mascot1', 'a ZOMBIE MASCOT wearing a GIANT FUZZY YELLOW TENNIS BALL costume: his gaunt ' + 'grey-green head pokes out of a hole in the ball, thin ragged green arms and ' + 'legs stick out of it, one white glove, worn sneakers. The huge yellow ball IS ' + 'his torso in every pose'), } @@ -48,14 +65,28 @@ def key(): sys.exit('no OPENROUTER_API_KEY') -def char_ref(k, spr): +def char_ref(k, spr, pair=None): """IMAGE 2: the character, at 4x NEAREST on magenta. Raw sprite pixels, no round trip — the same - rule the pose sheet now follows, and for the same reason.""" + rule the pose sheet now follows, and for the same reason. + + `pair` puts a SECOND sprite beside it at true relative scale. A picture beats a paragraph (the + project's own lesson, both ways): three rolls of prompt language could not make the champion + keep his bulk against the pose sheet's slim figures — showing him STANDING NEXT TO the club pro + is the claim the words kept failing to make.""" im = Image.open(os.path.join(HERE, spr + '.png')).convert('RGBA') big = im.resize((im.width * 4, im.height * 4), Image.NEAREST) pad = round(big.height * 0.08) - out = Image.new('RGB', (big.width + 2 * pad, big.height + 2 * pad), (255, 0, 255)) - out.paste(big, (pad, pad), big) + tiles = [big] + if pair: + pim = Image.open(os.path.join(HERE, pair + '.png')).convert('RGBA') + tiles.append(pim.resize((pim.width * 4, pim.height * 4), Image.NEAREST)) + w = sum(t.width for t in tiles) + pad * (len(tiles) + 1) + h = max(t.height for t in tiles) + 2 * pad + out = Image.new('RGB', (w, h), (255, 0, 255)) + x = pad + for t in tiles: + out.paste(t, (x, h - pad - t.height), t) # shared ground line: the size difference IS the message + x += t.width + pad p = os.path.join(SCRATCH, f'char_{k}.png') os.makedirs(SCRATCH, exist_ok=True) out.save(p) @@ -74,6 +105,11 @@ def prompt(who): 'damage states in the same places. A figure with no head keeps no head; a figure missing an arm ' 'keeps that arm missing with the same stump and blood. Only the body wearing the poses changes. ' 'Do not carry over the previous character\'s shirt, shorts, shoes, hair or headband.\n\n' + # "the same sizes" above is about LAYOUT, and the model reads it as licence to keep image 1's + # slim build — the champion came back club-pro-thin twice. The build belongs to the CHARACTER. + 'THE CHARACTER\'S OWN BUILD OVERRIDES IMAGE 1\'S: if image 2 shows a wider, heavier or fatter ' + 'body than the figures of image 1, every redrawn figure keeps that wider, heavier body in ' + 'every pose. Only the POSE comes from image 1 — never the physique.\n\n' # EVERY UNDAMAGED FIGURE HAS A HEAD, said out loud. The clause above ("a figure with no head # keeps no head") is there for the wound rows and the old normalisation pass took it as licence # to remove one from the play row — which is how six families came back with a decapitated @@ -96,8 +132,9 @@ def u(p): def roll(k): - spr, who = FAMS[k] - ch = char_ref(k, spr) + spr, who = FAMS[k][:2] + pair = FAMS[k][2] if len(FAMS[k]) > 2 else None + ch = char_ref(k, spr, pair) r = requests.post('https://openrouter.ai/api/v1/images', headers={'Authorization': f'Bearer {key()}', 'Content-Type': 'application/json'}, # 4K and 9:16: the endpoint DEFAULTS to 1K and snaps the aspect to an enum, so both are diff --git a/assets/z_ch1.png b/assets/z_ch1.png new file mode 100644 index 0000000..cfa9577 Binary files /dev/null and b/assets/z_ch1.png differ diff --git a/assets/z_ch2.png b/assets/z_ch2.png new file mode 100644 index 0000000..b1b75d4 Binary files /dev/null and b/assets/z_ch2.png differ diff --git a/assets/z_ch3.png b/assets/z_ch3.png new file mode 100644 index 0000000..b1c82fc Binary files /dev/null and b/assets/z_ch3.png differ diff --git a/assets/z_ch4.png b/assets/z_ch4.png new file mode 100644 index 0000000..3b013af Binary files /dev/null and b/assets/z_ch4.png differ diff --git a/assets/z_chbounce.png b/assets/z_chbounce.png new file mode 100644 index 0000000..1076760 Binary files /dev/null and b/assets/z_chbounce.png differ diff --git a/assets/z_chfall1.png b/assets/z_chfall1.png new file mode 100644 index 0000000..9617a0e Binary files /dev/null and b/assets/z_chfall1.png differ diff --git a/assets/z_chfall2.png b/assets/z_chfall2.png new file mode 100644 index 0000000..f5eefc5 Binary files /dev/null and b/assets/z_chfall2.png differ diff --git a/assets/z_chfall3.png b/assets/z_chfall3.png new file mode 100644 index 0000000..1380571 Binary files /dev/null and b/assets/z_chfall3.png differ diff --git a/assets/z_chrise.png b/assets/z_chrise.png new file mode 100644 index 0000000..a069544 Binary files /dev/null and b/assets/z_chrise.png differ diff --git a/assets/z_chserve.png b/assets/z_chserve.png new file mode 100644 index 0000000..ac38bbe Binary files /dev/null and b/assets/z_chserve.png differ diff --git a/assets/z_chtoss.png b/assets/z_chtoss.png new file mode 100644 index 0000000..e3a3d44 Binary files /dev/null and b/assets/z_chtoss.png differ diff --git a/assets/z_chw1.png b/assets/z_chw1.png new file mode 100644 index 0000000..62195e9 Binary files /dev/null and b/assets/z_chw1.png differ diff --git a/assets/z_chw1b.png b/assets/z_chw1b.png new file mode 100644 index 0000000..dcdd41e Binary files /dev/null and b/assets/z_chw1b.png differ diff --git a/assets/z_chw1bounce.png b/assets/z_chw1bounce.png new file mode 100644 index 0000000..f31eddc Binary files /dev/null and b/assets/z_chw1bounce.png differ diff --git a/assets/z_chw1fall1.png b/assets/z_chw1fall1.png new file mode 100644 index 0000000..d88d97e Binary files /dev/null and b/assets/z_chw1fall1.png differ diff --git a/assets/z_chw1fall2.png b/assets/z_chw1fall2.png new file mode 100644 index 0000000..78a1ea4 Binary files /dev/null and b/assets/z_chw1fall2.png differ diff --git a/assets/z_chw1fall3.png b/assets/z_chw1fall3.png new file mode 100644 index 0000000..b321d91 Binary files /dev/null and b/assets/z_chw1fall3.png differ diff --git a/assets/z_chw1rise.png b/assets/z_chw1rise.png new file mode 100644 index 0000000..d6d5215 Binary files /dev/null and b/assets/z_chw1rise.png differ diff --git a/assets/z_chw1s.png b/assets/z_chw1s.png new file mode 100644 index 0000000..5683c1a Binary files /dev/null and b/assets/z_chw1s.png differ diff --git a/assets/z_chw1serve.png b/assets/z_chw1serve.png new file mode 100644 index 0000000..1df2e3c Binary files /dev/null and b/assets/z_chw1serve.png differ diff --git a/assets/z_chw1toss.png b/assets/z_chw1toss.png new file mode 100644 index 0000000..23386ea Binary files /dev/null and b/assets/z_chw1toss.png differ diff --git a/assets/z_chw2.png b/assets/z_chw2.png new file mode 100644 index 0000000..aa1549d Binary files /dev/null and b/assets/z_chw2.png differ diff --git a/assets/z_chw2b.png b/assets/z_chw2b.png new file mode 100644 index 0000000..6c721ec Binary files /dev/null and b/assets/z_chw2b.png differ diff --git a/assets/z_chw2bounce.png b/assets/z_chw2bounce.png new file mode 100644 index 0000000..97de331 Binary files /dev/null and b/assets/z_chw2bounce.png differ diff --git a/assets/z_chw2fall1.png b/assets/z_chw2fall1.png new file mode 100644 index 0000000..7ca65d5 Binary files /dev/null and b/assets/z_chw2fall1.png differ diff --git a/assets/z_chw2fall2.png b/assets/z_chw2fall2.png new file mode 100644 index 0000000..3a728fc Binary files /dev/null and b/assets/z_chw2fall2.png differ diff --git a/assets/z_chw2fall3.png b/assets/z_chw2fall3.png new file mode 100644 index 0000000..cd85208 Binary files /dev/null and b/assets/z_chw2fall3.png differ diff --git a/assets/z_chw2rise.png b/assets/z_chw2rise.png new file mode 100644 index 0000000..6f02ac5 Binary files /dev/null and b/assets/z_chw2rise.png differ diff --git a/assets/z_chw2s.png b/assets/z_chw2s.png new file mode 100644 index 0000000..355866c Binary files /dev/null and b/assets/z_chw2s.png differ diff --git a/assets/z_chw2serve.png b/assets/z_chw2serve.png new file mode 100644 index 0000000..5b8ce04 Binary files /dev/null and b/assets/z_chw2serve.png differ diff --git a/assets/z_chw2toss.png b/assets/z_chw2toss.png new file mode 100644 index 0000000..f15f354 Binary files /dev/null and b/assets/z_chw2toss.png differ diff --git a/assets/z_dr1.png b/assets/z_dr1.png new file mode 100644 index 0000000..7d139df Binary files /dev/null and b/assets/z_dr1.png differ diff --git a/assets/z_dr2.png b/assets/z_dr2.png new file mode 100644 index 0000000..12f9671 Binary files /dev/null and b/assets/z_dr2.png differ diff --git a/assets/z_dr3.png b/assets/z_dr3.png new file mode 100644 index 0000000..66a599b Binary files /dev/null and b/assets/z_dr3.png differ diff --git a/assets/z_dr4.png b/assets/z_dr4.png new file mode 100644 index 0000000..4b06b58 Binary files /dev/null and b/assets/z_dr4.png differ diff --git a/assets/z_drbounce.png b/assets/z_drbounce.png new file mode 100644 index 0000000..4b02f7e Binary files /dev/null and b/assets/z_drbounce.png differ diff --git a/assets/z_drfall1.png b/assets/z_drfall1.png new file mode 100644 index 0000000..82e8310 Binary files /dev/null and b/assets/z_drfall1.png differ diff --git a/assets/z_drfall2.png b/assets/z_drfall2.png new file mode 100644 index 0000000..00f80b6 Binary files /dev/null and b/assets/z_drfall2.png differ diff --git a/assets/z_drfall3.png b/assets/z_drfall3.png new file mode 100644 index 0000000..f8df6dd Binary files /dev/null and b/assets/z_drfall3.png differ diff --git a/assets/z_drrise.png b/assets/z_drrise.png new file mode 100644 index 0000000..23ad1f5 Binary files /dev/null and b/assets/z_drrise.png differ diff --git a/assets/z_drserve.png b/assets/z_drserve.png new file mode 100644 index 0000000..90ac7a1 Binary files /dev/null and b/assets/z_drserve.png differ diff --git a/assets/z_drtoss.png b/assets/z_drtoss.png new file mode 100644 index 0000000..607a8a2 Binary files /dev/null and b/assets/z_drtoss.png differ diff --git a/assets/z_drw1.png b/assets/z_drw1.png new file mode 100644 index 0000000..5f6d0fd Binary files /dev/null and b/assets/z_drw1.png differ diff --git a/assets/z_drw1b.png b/assets/z_drw1b.png new file mode 100644 index 0000000..994ec2f Binary files /dev/null and b/assets/z_drw1b.png differ diff --git a/assets/z_drw1bounce.png b/assets/z_drw1bounce.png new file mode 100644 index 0000000..f31afac Binary files /dev/null and b/assets/z_drw1bounce.png differ diff --git a/assets/z_drw1fall1.png b/assets/z_drw1fall1.png new file mode 100644 index 0000000..1f92241 Binary files /dev/null and b/assets/z_drw1fall1.png differ diff --git a/assets/z_drw1fall2.png b/assets/z_drw1fall2.png new file mode 100644 index 0000000..d973af2 Binary files /dev/null and b/assets/z_drw1fall2.png differ diff --git a/assets/z_drw1fall3.png b/assets/z_drw1fall3.png new file mode 100644 index 0000000..42f6ffe Binary files /dev/null and b/assets/z_drw1fall3.png differ diff --git a/assets/z_drw1rise.png b/assets/z_drw1rise.png new file mode 100644 index 0000000..622589e Binary files /dev/null and b/assets/z_drw1rise.png differ diff --git a/assets/z_drw1s.png b/assets/z_drw1s.png new file mode 100644 index 0000000..2be10ce Binary files /dev/null and b/assets/z_drw1s.png differ diff --git a/assets/z_drw1serve.png b/assets/z_drw1serve.png new file mode 100644 index 0000000..4befcd9 Binary files /dev/null and b/assets/z_drw1serve.png differ diff --git a/assets/z_drw1toss.png b/assets/z_drw1toss.png new file mode 100644 index 0000000..a2e945c Binary files /dev/null and b/assets/z_drw1toss.png differ diff --git a/assets/z_drw2.png b/assets/z_drw2.png new file mode 100644 index 0000000..e732f44 Binary files /dev/null and b/assets/z_drw2.png differ diff --git a/assets/z_drw2b.png b/assets/z_drw2b.png new file mode 100644 index 0000000..799ed44 Binary files /dev/null and b/assets/z_drw2b.png differ diff --git a/assets/z_drw2bounce.png b/assets/z_drw2bounce.png new file mode 100644 index 0000000..2f252f4 Binary files /dev/null and b/assets/z_drw2bounce.png differ diff --git a/assets/z_drw2fall1.png b/assets/z_drw2fall1.png new file mode 100644 index 0000000..ebceff3 Binary files /dev/null and b/assets/z_drw2fall1.png differ diff --git a/assets/z_drw2fall2.png b/assets/z_drw2fall2.png new file mode 100644 index 0000000..aff5933 Binary files /dev/null and b/assets/z_drw2fall2.png differ diff --git a/assets/z_drw2fall3.png b/assets/z_drw2fall3.png new file mode 100644 index 0000000..0c2ae73 Binary files /dev/null and b/assets/z_drw2fall3.png differ diff --git a/assets/z_drw2rise.png b/assets/z_drw2rise.png new file mode 100644 index 0000000..bd239aa Binary files /dev/null and b/assets/z_drw2rise.png differ diff --git a/assets/z_drw2s.png b/assets/z_drw2s.png new file mode 100644 index 0000000..d44d9b6 Binary files /dev/null and b/assets/z_drw2s.png differ diff --git a/assets/z_drw2serve.png b/assets/z_drw2serve.png new file mode 100644 index 0000000..344b37b Binary files /dev/null and b/assets/z_drw2serve.png differ diff --git a/assets/z_drw2toss.png b/assets/z_drw2toss.png new file mode 100644 index 0000000..580bc76 Binary files /dev/null and b/assets/z_drw2toss.png differ diff --git a/assets/z_ma1.png b/assets/z_ma1.png new file mode 100644 index 0000000..07ec7bf Binary files /dev/null and b/assets/z_ma1.png differ diff --git a/assets/z_ma2.png b/assets/z_ma2.png new file mode 100644 index 0000000..1161094 Binary files /dev/null and b/assets/z_ma2.png differ diff --git a/assets/z_ma3.png b/assets/z_ma3.png new file mode 100644 index 0000000..cf6783b Binary files /dev/null and b/assets/z_ma3.png differ diff --git a/assets/z_ma4.png b/assets/z_ma4.png new file mode 100644 index 0000000..424555c Binary files /dev/null and b/assets/z_ma4.png differ diff --git a/assets/z_mabounce.png b/assets/z_mabounce.png new file mode 100644 index 0000000..3f5edba Binary files /dev/null and b/assets/z_mabounce.png differ diff --git a/assets/z_mafall1.png b/assets/z_mafall1.png new file mode 100644 index 0000000..1a9d353 Binary files /dev/null and b/assets/z_mafall1.png differ diff --git a/assets/z_mafall2.png b/assets/z_mafall2.png new file mode 100644 index 0000000..ff361f1 Binary files /dev/null and b/assets/z_mafall2.png differ diff --git a/assets/z_mafall3.png b/assets/z_mafall3.png new file mode 100644 index 0000000..4271a61 Binary files /dev/null and b/assets/z_mafall3.png differ diff --git a/assets/z_marise.png b/assets/z_marise.png new file mode 100644 index 0000000..c123c20 Binary files /dev/null and b/assets/z_marise.png differ diff --git a/assets/z_maserve.png b/assets/z_maserve.png new file mode 100644 index 0000000..fc547ee Binary files /dev/null and b/assets/z_maserve.png differ diff --git a/assets/z_matoss.png b/assets/z_matoss.png new file mode 100644 index 0000000..700ef48 Binary files /dev/null and b/assets/z_matoss.png differ diff --git a/assets/z_maw1.png b/assets/z_maw1.png new file mode 100644 index 0000000..bbb08a0 Binary files /dev/null and b/assets/z_maw1.png differ diff --git a/assets/z_maw1b.png b/assets/z_maw1b.png new file mode 100644 index 0000000..d2015d3 Binary files /dev/null and b/assets/z_maw1b.png differ diff --git a/assets/z_maw1bounce.png b/assets/z_maw1bounce.png new file mode 100644 index 0000000..8a31cf6 Binary files /dev/null and b/assets/z_maw1bounce.png differ diff --git a/assets/z_maw1fall1.png b/assets/z_maw1fall1.png new file mode 100644 index 0000000..9f0c0cf Binary files /dev/null and b/assets/z_maw1fall1.png differ diff --git a/assets/z_maw1fall2.png b/assets/z_maw1fall2.png new file mode 100644 index 0000000..0f4ed0a Binary files /dev/null and b/assets/z_maw1fall2.png differ diff --git a/assets/z_maw1fall3.png b/assets/z_maw1fall3.png new file mode 100644 index 0000000..a0e27af Binary files /dev/null and b/assets/z_maw1fall3.png differ diff --git a/assets/z_maw1rise.png b/assets/z_maw1rise.png new file mode 100644 index 0000000..648de51 Binary files /dev/null and b/assets/z_maw1rise.png differ diff --git a/assets/z_maw1s.png b/assets/z_maw1s.png new file mode 100644 index 0000000..0fa47bc Binary files /dev/null and b/assets/z_maw1s.png differ diff --git a/assets/z_maw1serve.png b/assets/z_maw1serve.png new file mode 100644 index 0000000..8aa3c42 Binary files /dev/null and b/assets/z_maw1serve.png differ diff --git a/assets/z_maw1toss.png b/assets/z_maw1toss.png new file mode 100644 index 0000000..095bf21 Binary files /dev/null and b/assets/z_maw1toss.png differ diff --git a/assets/z_maw2.png b/assets/z_maw2.png new file mode 100644 index 0000000..dbc2c4a Binary files /dev/null and b/assets/z_maw2.png differ diff --git a/assets/z_maw2b.png b/assets/z_maw2b.png new file mode 100644 index 0000000..9f742a2 Binary files /dev/null and b/assets/z_maw2b.png differ diff --git a/assets/z_maw2bounce.png b/assets/z_maw2bounce.png new file mode 100644 index 0000000..fb22549 Binary files /dev/null and b/assets/z_maw2bounce.png differ diff --git a/assets/z_maw2fall1.png b/assets/z_maw2fall1.png new file mode 100644 index 0000000..3052c9d Binary files /dev/null and b/assets/z_maw2fall1.png differ diff --git a/assets/z_maw2fall2.png b/assets/z_maw2fall2.png new file mode 100644 index 0000000..09c476c Binary files /dev/null and b/assets/z_maw2fall2.png differ diff --git a/assets/z_maw2fall3.png b/assets/z_maw2fall3.png new file mode 100644 index 0000000..c9eecb2 Binary files /dev/null and b/assets/z_maw2fall3.png differ diff --git a/assets/z_maw2rise.png b/assets/z_maw2rise.png new file mode 100644 index 0000000..6446fdb Binary files /dev/null and b/assets/z_maw2rise.png differ diff --git a/assets/z_maw2s.png b/assets/z_maw2s.png new file mode 100644 index 0000000..1d1884c Binary files /dev/null and b/assets/z_maw2s.png differ diff --git a/assets/z_maw2serve.png b/assets/z_maw2serve.png new file mode 100644 index 0000000..e6563c7 Binary files /dev/null and b/assets/z_maw2serve.png differ diff --git a/assets/z_maw2toss.png b/assets/z_maw2toss.png new file mode 100644 index 0000000..bc887f8 Binary files /dev/null and b/assets/z_maw2toss.png differ diff --git a/assets/z_ph1.png b/assets/z_ph1.png new file mode 100644 index 0000000..fbcf54c Binary files /dev/null and b/assets/z_ph1.png differ diff --git a/assets/z_ph2.png b/assets/z_ph2.png new file mode 100644 index 0000000..eeb2d1f Binary files /dev/null and b/assets/z_ph2.png differ diff --git a/assets/z_ph3.png b/assets/z_ph3.png new file mode 100644 index 0000000..d4ec436 Binary files /dev/null and b/assets/z_ph3.png differ diff --git a/assets/z_ph4.png b/assets/z_ph4.png new file mode 100644 index 0000000..34a2b9a Binary files /dev/null and b/assets/z_ph4.png differ diff --git a/assets/z_phbounce.png b/assets/z_phbounce.png new file mode 100644 index 0000000..fd5a500 Binary files /dev/null and b/assets/z_phbounce.png differ diff --git a/assets/z_phfall1.png b/assets/z_phfall1.png new file mode 100644 index 0000000..cbb7b1c Binary files /dev/null and b/assets/z_phfall1.png differ diff --git a/assets/z_phfall2.png b/assets/z_phfall2.png new file mode 100644 index 0000000..d183f08 Binary files /dev/null and b/assets/z_phfall2.png differ diff --git a/assets/z_phfall3.png b/assets/z_phfall3.png new file mode 100644 index 0000000..39d3b9b Binary files /dev/null and b/assets/z_phfall3.png differ diff --git a/assets/z_phrise.png b/assets/z_phrise.png new file mode 100644 index 0000000..8f06637 Binary files /dev/null and b/assets/z_phrise.png differ diff --git a/assets/z_phserve.png b/assets/z_phserve.png new file mode 100644 index 0000000..264086f Binary files /dev/null and b/assets/z_phserve.png differ diff --git a/assets/z_phtoss.png b/assets/z_phtoss.png new file mode 100644 index 0000000..527e19a Binary files /dev/null and b/assets/z_phtoss.png differ diff --git a/assets/z_phw1.png b/assets/z_phw1.png new file mode 100644 index 0000000..bea244b Binary files /dev/null and b/assets/z_phw1.png differ diff --git a/assets/z_phw1b.png b/assets/z_phw1b.png new file mode 100644 index 0000000..078446c Binary files /dev/null and b/assets/z_phw1b.png differ diff --git a/assets/z_phw1bounce.png b/assets/z_phw1bounce.png new file mode 100644 index 0000000..98a355d Binary files /dev/null and b/assets/z_phw1bounce.png differ diff --git a/assets/z_phw1fall1.png b/assets/z_phw1fall1.png new file mode 100644 index 0000000..ec4d1a2 Binary files /dev/null and b/assets/z_phw1fall1.png differ diff --git a/assets/z_phw1fall2.png b/assets/z_phw1fall2.png new file mode 100644 index 0000000..037278d Binary files /dev/null and b/assets/z_phw1fall2.png differ diff --git a/assets/z_phw1fall3.png b/assets/z_phw1fall3.png new file mode 100644 index 0000000..62483c9 Binary files /dev/null and b/assets/z_phw1fall3.png differ diff --git a/assets/z_phw1rise.png b/assets/z_phw1rise.png new file mode 100644 index 0000000..7af9b1d Binary files /dev/null and b/assets/z_phw1rise.png differ diff --git a/assets/z_phw1s.png b/assets/z_phw1s.png new file mode 100644 index 0000000..bf92e52 Binary files /dev/null and b/assets/z_phw1s.png differ diff --git a/assets/z_phw1serve.png b/assets/z_phw1serve.png new file mode 100644 index 0000000..df91aa5 Binary files /dev/null and b/assets/z_phw1serve.png differ diff --git a/assets/z_phw1toss.png b/assets/z_phw1toss.png new file mode 100644 index 0000000..bf38079 Binary files /dev/null and b/assets/z_phw1toss.png differ diff --git a/assets/z_phw2.png b/assets/z_phw2.png new file mode 100644 index 0000000..2ad300b Binary files /dev/null and b/assets/z_phw2.png differ diff --git a/assets/z_phw2b.png b/assets/z_phw2b.png new file mode 100644 index 0000000..eb05297 Binary files /dev/null and b/assets/z_phw2b.png differ diff --git a/assets/z_phw2bounce.png b/assets/z_phw2bounce.png new file mode 100644 index 0000000..c50196b Binary files /dev/null and b/assets/z_phw2bounce.png differ diff --git a/assets/z_phw2fall1.png b/assets/z_phw2fall1.png new file mode 100644 index 0000000..5f5d73f Binary files /dev/null and b/assets/z_phw2fall1.png differ diff --git a/assets/z_phw2fall2.png b/assets/z_phw2fall2.png new file mode 100644 index 0000000..1d1b5a1 Binary files /dev/null and b/assets/z_phw2fall2.png differ diff --git a/assets/z_phw2fall3.png b/assets/z_phw2fall3.png new file mode 100644 index 0000000..f46e8e3 Binary files /dev/null and b/assets/z_phw2fall3.png differ diff --git a/assets/z_phw2rise.png b/assets/z_phw2rise.png new file mode 100644 index 0000000..e88f318 Binary files /dev/null and b/assets/z_phw2rise.png differ diff --git a/assets/z_phw2s.png b/assets/z_phw2s.png new file mode 100644 index 0000000..7cd3a14 Binary files /dev/null and b/assets/z_phw2s.png differ diff --git a/assets/z_phw2serve.png b/assets/z_phw2serve.png new file mode 100644 index 0000000..aee58d9 Binary files /dev/null and b/assets/z_phw2serve.png differ diff --git a/assets/z_phw2toss.png b/assets/z_phw2toss.png new file mode 100644 index 0000000..dd64411 Binary files /dev/null and b/assets/z_phw2toss.png differ diff --git a/index.html b/index.html index fa62d0b..ca3aae1 100644 --- a/index.html +++ b/index.html @@ -431,6 +431,6 @@ if (location.hostname !== 'splatter-tennis.wuwi.one') { - + diff --git a/src/config.js b/src/config.js index 2a8a2ef..c7c7f5c 100644 --- a/src/config.js +++ b/src/config.js @@ -756,9 +756,10 @@ export const bossArt = ch => ch % 10 === 0 ? BOSS_ART[BOSS_ART.length - 1] // is a property of the table rather than something the code has to arrange. // 1-2 the ones who fetch and sweep. Slow, wild, forgiving. // 3-4 the ones who keep order. They start covering ground. -// 5-6 the ones who judge. Width arrives: they begin painting lines. -// 7-8 the ones who actually played. The club pro's own body, and the brute behind him. -// 9-10 management, and then the man whose name is on the honours board. +// 5 the entertainment. The costume still fits. +// 6-8 the ones who cover and judge. Width arrives: they begin painting lines. +// 9-10 the ones who actually played. The club pro's own body, and the brute behind him. +// 11-12 management, and then the man whose name is on the honours board. const OPPONENTS = [ { short: 'SHAMBLER', name: 'THE SHAMBLER', fam: 'sh', line: 'HE WAS HERE BEFORE ANY OF THIS.', speed: 0.72, pace: 0.72, wide: 0.75, flub: 1.35 }, @@ -768,25 +769,36 @@ const OPPONENTS = [ line: 'HE MOWED THIS LAWN FOR THIRTY YEARS.', speed: 0.9, pace: 0.88, wide: 0.9, flub: 1.05 }, { short: 'STEWARD', name: 'THE STEWARD', fam: 'st', line: 'HE IS ASKING YOU TO LEAVE.', speed: 0.98, pace: 0.95, wide: 1.0 }, + // 5-6: the entertainment, and then width arrives with the ones who judge + { short: 'MASCOT', name: 'THE MASCOT', fam: 'ma', + line: 'THE CROWD STILL LOVES HIM.', speed: 1.02, pace: 0.98, wide: 1.05, flub: 1.0 }, { short: 'RUNNER', name: 'THE RUNNER', fam: 'ru', line: 'HE COVERS THE COURT. ALL OF IT.', speed: 1.22, pace: 1.0, wide: 1.05, flub: 0.95 }, { short: 'JUDGE', name: 'THE LINE JUDGE', fam: 'lj', line: 'EVERY BALL OF YOURS IS OUT.', speed: 1.05, pace: 1.05, wide: 1.28, flub: 0.85 }, + { short: 'PHYSIO', name: 'THE PHYSIO', fam: 'ph', + line: 'HE KNOWS EXACTLY WHERE IT HURTS.', speed: 1.1, pace: 1.1, wide: 1.22, flub: 0.82 }, { short: 'CLUB PRO', name: 'THE CLUB PRO', line: 'HE GAVE LESSONS ON THIS COURT.', speed: 1.12, pace: 1.15, wide: 1.15, flub: 0.8 }, - { short: 'THE BULK', name: 'THE BULK', fam: 'br', + { short: 'THE BULK', name: 'THE BULK', fam: 'br', h: 2.0, line: 'NOTHING GETS PAST HIM TWICE.', speed: 1.0, pace: 1.3, wide: 1.2, flub: 0.75 }, - { short: 'DIRECTOR', name: 'THE DIRECTOR', + { short: 'DIRECTOR', name: 'THE DIRECTOR', fam: 'dr', line: 'HE CAN HAVE YOU REMOVED.', speed: 1.22, pace: 1.22, wide: 1.25, flub: 0.65 }, - { short: 'CHAMPION', name: 'THE CHAMPION', + // 12: the man whose name is on the honours board. `h` is WORLD height (zombieH reads it, so the + // serve aims at the body actually drawn): four family rolls could not make the art itself bulky + // against the pose sheet — see _famroll.py's pair-reference note — so his size is stature. + { short: 'CHAMPION', name: 'THE CHAMPION', fam: 'ch', h: 2.1, line: 'HE RETURNS EVERYTHING.', speed: 1.35, pace: 1.3, wide: 1.3, flub: 0.5 }, ]; -// Who opponent N is. The chapter roster's own order is reused wholesale (bossArt), so the champion -// lands on opponent 10 exactly as he lands on chapter 10 and the two rosters can never drift apart. +// Who opponent N is. This used to reuse the chapter roster's cycling (bossArt) so the two lists +// could never drift — DECOUPLED 2026-08-13 on Oliver's call: the match ladder is 12 rungs with the +// champion capping rung 12, while the chapter bosses stay a 10-design rotation with the champion +// on every tenth. The lists share CHARACTERS (a rung and a boss are the same person, anchored on +// the same art), just not arithmetic. The ladder cycles past 12 exactly as bossArt cycles past 10. export const matchFoe = n => { - const art = bossArt(n), i = BOSS_ART.indexOf(art); - return { name: art.name, key: 'L' + i, fam: 'r', ...OPPONENTS[i] }; + const i = (n - 1) % OPPONENTS.length; + return { key: 'L' + i, fam: 'r', ...OPPONENTS[i] }; }; // ---- THE SPRITE BENCH's stops (back room only, see beats.startBench). Each is either an ANIMATED @@ -821,7 +833,7 @@ export const BENCH_STAGES = ['HEALTHY', 'ARM OFF', 'HEADLESS']; // that (15 to 48 away from z_br1, where a good frame scores under 8.5) and refuses them, which is // the gate working, not the gate being wrong. Re-registering it is one entry here; see the note at // the top of _master.py for the fix its next attempt needs. -export const ZFAMS = ['r', 'br', 'sh', 'bb', 'st', 'ru', 'gk', 'lj']; +export const ZFAMS = ['r', 'br', 'sh', 'bb', 'st', 'ru', 'gk', 'lj', 'dr', 'ch', 'ph', 'ma']; // HEAD CROPS for the opponent intro card — a straight source-pixel crop out of each family's own // healthy frame (z_1.png), so the card's icon is the body on court, never separate art. @@ -838,6 +850,10 @@ export const HEAD_CROP = { ru: { x: 14, y: 0, w: 57, h: 55 }, gk: { x: 6, y: 0, w: 73, h: 65 }, lj: { x: 6, y: 0, w: 74, h: 56 }, + dr: { x: 10, y: 0, w: 62, h: 49 }, + ch: { x: 3, y: 0, w: 68, h: 63 }, + ph: { x: 12, y: 0, w: 63, h: 58 }, + ma: { x: 0, y: 0, w: 124, h: 81 }, // the mascot's head IS the ball rim too — hand-deepened }; // THE RACKET FAMILY'S THREE STAGES, as a FRACTION of the pool — never against literal hp, the rule @@ -1007,7 +1023,7 @@ export const MACH_H = 1.55; // was 1.15 — waist high read as a prop you could export const zombieH = zb => zb.variant === 'o' ? TUNE.barrelH : zb.variant === 'g' ? 2.7 : zb.variant === 'd' ? 2.15 : zb.variant === 'k' ? 3.2 : zb.variant === 'h' ? 2.4 - : zb.variant === 'r' ? 1.85 : zb.variant === 'm' ? MACH_H + : zb.variant === 'r' ? ((zb.foe && zb.foe.h) || 1.85) : zb.variant === 'm' ? MACH_H : zb.variant === 'B' ? 1.8 * (zb.scale || 1.5) : 1.8; // Where on that body the ball is aimed, and where the aim mark is drawn. Solving the shot to the // GROUND under a shambler meant the ball came down past it and struck at the ankles — the wound is diff --git a/src/render.js b/src/render.js index c84f720..02fa187 100644 --- a/src/render.js +++ b/src/render.js @@ -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 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 = 108; +export const SPRV = 109; // 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". diff --git a/test.js b/test.js index e318226..a0052b2 100644 --- a/test.js +++ b/test.js @@ -666,7 +666,7 @@ test('the opponent card is a RAMP: it starts easy and only gets harder', () => { // invisible until someone plays ten opponents deep. const { matchFoe, TUNE } = cfg; let lastSpeed = 0, lastWhiff = 1e9; - for (let n = 1; n <= 10; n++) { + for (let n = 1; n <= 12; n++) { // twelve rungs since 2026-08-13 (champion caps 12) const f = matchFoe(n); const speed = (TUNE.matchSpeedBase + (n - 1) * TUNE.matchSpeedPer) * (f.speed ?? 1); const whiff = TUNE.matchFlub * Math.pow(TUNE.matchFlubKeep, n - 1) * (f.flub ?? 1); @@ -682,7 +682,11 @@ test('the opponent card is a RAMP: it starts easy and only gets harder', () => { assert.ok(f.short.length <= 8, `${f.short} fits the board's 8-char slot`); } // ...and the opening must actually be gentle: opponent one is the softest thing on the card. - assert.ok((matchFoe(1).speed ?? 1) < (matchFoe(10).speed ?? 1) * 0.6, 'the shambler is no champion'); + assert.ok((matchFoe(1).speed ?? 1) < (matchFoe(12).speed ?? 1) * 0.6, 'the shambler is no champion'); + // the four 2026-08-13 families wear their own bodies, and the big two carry real stature + for (const [n, fam] of [[5, 'ma'], [8, 'ph'], [11, 'dr'], [12, 'ch']]) + assert.equal(matchFoe(n).fam, fam, `rung ${n} wears its own family`); + assert.ok(matchFoe(12).h > 1.85, 'the champion stands taller than a stock body'); }); test('the first opponent cannot paint lines: its returns are width-clamped', () => {