The ladder has names, the bodies play tennis, and the stats screen splits
THE SPRITES. The match opponent borrowed the turned CPU's art — a body drawn to SHAMBLE at the camera with its racket hanging at its side, which is what was playing tennis points against you. It has its own family now, 'r', drawn off the LIVING opponent's poses: a ready stance, a shuffle pair and a real forehand, at every wound stage, men's and women's — 20 frames, six batches. 'c' keeps its shamble untouched, because in the gore mode shambling is exactly what the turned CPU should be doing. The pose comes from racketAI's own `pose`, so the picture cannot drift from the behaviour, and a one-armed opponent still plays tennis instead of reverting to a walk. Two pipeline bugs found doing it, both fixed at the root in assets/: - _fine.py keyed EVERY magenta pixel, not just the background, and the generator paints racket strings in the backdrop colour more often than not — so frame after frame installed a man holding an empty hoop. Background is now the region connected to the border; magenta left INSIDE a body is repainted pale grey rather than punched out. - the flood fill silently did nothing, because Image.fromarray images are read-only and PIL's floodfill swallows the write error. .copy() plus a real error if it ever fills zero pixels. THE LADDER (handoff slice 2). Rung N takes design N's name off the chapter roster — the club's dead staff play you at tennis here and come for you in gore, same joke twice — plus a personality that is DIALS, not new mechanics: pace, width, flub and speed scalings multiplying numbers racketAI already reads. A rung is a data row. Name plate on the walk-on, and the kill files under the rung rather than under the generic body. THE STATS SCREEN follows the mode split: BODY COUNT / THE LADDER / THE RECORD behind a mode strip, each with its own three figures, its own roster heading and its own footer. The weapon shelf and the roster hide in the modes that have neither, rather than printing a line about something the mode cannot do. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c89d4a3ff9
commit
c629ae1108
16 files changed
+138
-33
No files matched your search
@@ -667,6 +667,35 @@ export const BOSS_ART = [
|
||||
export const bossArt = ch => ch % 10 === 0 ? BOSS_ART[BOSS_ART.length - 1]
|
||||
: BOSS_ART[(ch - 1 - Math.floor(ch / 10)) % (BOSS_ART.length - 1)];
|
||||
|
||||
// ---- THE MATCH LADDER. Zombie tennis walks the SAME dead club staff onto the court as the gore
|
||||
// mode's chapter bosses — the roster is the joke, and it is worth telling twice: in gore they come
|
||||
// for you, here they play you at tennis. So rung N takes design N's NAME (bossArt, champion on
|
||||
// ten) and a PERSONALITY, and the body stays the racket zombie's, which is the one drawn to play.
|
||||
//
|
||||
// A personality is DIALS, not new mechanics: every field below multiplies a number racketAI and the
|
||||
// return shaping already read, so a rung is a data row and never a branch. All optional.
|
||||
// pace scale on its returns' speed wide scale on how far off centre they may land
|
||||
// flub scale on its whiff chance speed scale on how fast it covers the ground
|
||||
// line the personality, said once, on the name plate as it walks on
|
||||
const LADDER_TRAITS = [
|
||||
{ line: 'HE MOWED THIS LAWN FOR THIRTY YEARS.', speed: 0.85, pace: 0.9, wide: 0.85 },
|
||||
{ line: 'EVERY BALL OF YOURS IS OUT.', wide: 1.25, flub: 0.8 },
|
||||
{ line: 'HE WILL NOT BE OVERRULED.', speed: 1.1, flub: 0.7 },
|
||||
{ line: 'HE HAS SEEN YOUR BACKHAND.', pace: 1.15, wide: 1.1 },
|
||||
{ line: 'HE IS HERE TO TREAT YOU.', speed: 1.2, pace: 1.1 },
|
||||
{ line: 'THE CROWD LOVES HIM.', flub: 1.2, wide: 1.3 },
|
||||
{ line: 'HE RESTRUNG THIS RACKET HIMSELF.', pace: 1.2, flub: 0.75 },
|
||||
{ line: 'HE CAN HAVE YOU REMOVED.', speed: 1.15, wide: 1.2 },
|
||||
{ line: 'WITH CREAM.', pace: 1.1, speed: 1.1, wide: 1.15 },
|
||||
{ line: 'HE RETURNS EVERYTHING.', pace: 1.25, speed: 1.3, wide: 1.25, flub: 0.5 },
|
||||
];
|
||||
// Rung N's identity. The chapter roster's own walk is reused wholesale (bossArt), so the champion
|
||||
// lands on rung 10 exactly as he lands on chapter 10 and the two rosters can never drift apart.
|
||||
export const matchRung = n => {
|
||||
const art = bossArt(n), i = BOSS_ART.indexOf(art);
|
||||
return { name: art.name, key: 'L' + i, ...LADDER_TRAITS[i] };
|
||||
};
|
||||
|
||||
// The roster, as the BODY COUNT screen reads it. Until now a variant's NAME lived only in the
|
||||
// comment above its hp value — fine while nothing ever had to print one. The key is the `variant`
|
||||
// char the zombie already carries, except that 'a' and 'b' fold together (they are one shambler in
|
||||
@@ -694,6 +723,12 @@ export const ENEMIES = {
|
||||
for (let i = 0; i < BOSS_ART.length; i++) // the chapter bosses, named where they already are
|
||||
ENEMIES['B' + i] = { name: BOSS_ART[i].name, spr: BOSS_ART[i].walk[0], boss: true,
|
||||
sub: i === BOSS_ART.length - 1 ? 'THE FINAL · EVERY TENTH CHAPTER' : 'CHAPTER BOSS' };
|
||||
// ...and the same roster again as MATCH opponents ('L0'..), because that is who walks on in zombie
|
||||
// tennis. Separate keys, not shared ones: the two are different fights with different bodies, they
|
||||
// are counted on different screens now (the mode split in stats.js), and one row cannot mean both.
|
||||
for (let i = 0; i < BOSS_ART.length; i++)
|
||||
ENEMIES['L' + i] = { name: BOSS_ART[i].name, spr: 'z_r1', ladder: true,
|
||||
sub: LADDER_TRAITS[i].line };
|
||||
|
||||
// Which wound stage a body is in: 0 = unhurt, 1..n = the skins, spread evenly over its full health.
|
||||
// This is the rule the chapter boss already used, and it turns out to reproduce the brute's old
|
||||
|
||||
Reference in new issue
Block a user