diff --git a/PROJECT.md b/PROJECT.md index 1224cb9..180f210 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -3556,9 +3556,11 @@ never an interstitial. An interstitial after death would land exactly in the "on **THE CAPTURE RIG IS BUILT (2026-08-18), AND THE SHOT LIST IS DATA.** `python3 tools/capture.py` fills `promo/clips/`; `python3 tools/capture.py horde boss` shoots two; `python3 tools/capture.py my-shots.json` shoots another list entirely; `--seed 77` re-rolls every take. What to shoot lives - in `tools/scenes.json` — nine scenes today — and its `"_"` block documents every knob: - `seconds`, `setting` (gore / zombie), `beat` (transition / horde / boss / death / mopboy), - `court`, `boss`, `weapons`, `movement`, `density`, `diversity`, `gore`, `games`, `screen`. + in `tools/scenes.json` — eleven scenes today — and its `"_readme"` block IS the schema, written so + an agent can open that one file and write a new batch: every key with its range and default, all + eight tins and what each does, the five beats, the crowd roster `diversity` walks, the ten bosses + by chapter, and the traps (a serve tin is spent in ~4 s of autopilot fire; `movement` fights the + match's own auto-run legs, so a zombie-tennis scene leaves it 0). `tools/clips.js` is the engine that knows how to shoot any of it: `tools/capture.py` serves the repo with it injected, opens a throwaway-profile Chrome, and the page stages the scene, records canvas + audio, POSTs the webm back; ffmpeg writes h264/aac. Nothing is faked for the camera — diff --git a/tools/capture.py b/tools/capture.py index 33bde92..de1bf49 100644 --- a/tools/capture.py +++ b/tools/capture.py @@ -26,7 +26,7 @@ CHROME = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' INJECT = b'\n' COURTS = ('grass', 'clay', 'hard', 'gloom') -WEAPONS = ('bomb', 'triple', 'beacon', 'flamer', 'rocket') +WEAPONS = ('bomb', 'triple', 'fire', 'laser', 'gun', 'beacon', 'flamer', 'rocket') # config.WEAPONS, all of it BEATS = ('transition', 'horde', 'boss', 'death', 'mopboy') SCREENS = ('phone', 'tall', '9x16') DIALS = ('movement', 'density', 'diversity', 'gore') diff --git a/tools/scenes.json b/tools/scenes.json index e4de3b0..743a0a7 100644 --- a/tools/scenes.json +++ b/tools/scenes.json @@ -1,28 +1,94 @@ { - "_": [ - "THE SHOT LIST. `python3 tools/capture.py` shoots every scene below into promo/clips/;", - "`python3 tools/capture.py horde boss` shoots two; `python3 tools/capture.py my.json` shoots", - "another list entirely. Keys starting with _ are ignored, which is where the comments go.", + "_readme": [ + "THE SHOT LIST for the promo clip factory. Every object in `scenes` becomes one vertical mp4 in", + "promo/clips/, named after its `name`. Keys starting with _ are ignored everywhere, which is", + "where comments go. This block is the whole spec: nothing else needs reading to write a batch.", "", - "name file name, and the name you pass on the command line", - "seconds length of the take (3-60)", - "setting gore | zombie — the splatter run, or the tennis match against the roster", - "beat transition | horde | boss | death | mopboy — the staged moment, optional", - "court grass | clay | hard | gloom", - "boss 1-10, the chapter whose boss walks on (gore only; it owns the court, so no match)", - "weapons rack tins granted and cycled through: bomb, triple, beacon, flamer, rocket", - "movement 0 statue .. 1 sweeps the full width (only bites in a rally or under a burst)", - "density 0 .. 1 how many bodies are kept on court (1 is about fourteen)", - "diversity 0 shamblers only .. 1 the whole roster (steward, runner, ball boy, brute, bouncer)", - "gore 0 .. 1 blood already on the court when the clip opens", - "games run progress the HUD shows and the speed ramp reads (default 8, or the boss chapter)", - "screen phone (default, 393x852 -> 1080x2340) | tall | 9x16", - "seed whole-shoot seed lives on the command line: --seed 1234 re-rolls every take" + "RUN IT", + " python3 tools/capture.py every scene in this file", + " python3 tools/capture.py horde boss only those, in that order", + " python3 tools/capture.py my-batch.json a different list entirely (same schema)", + " python3 tools/capture.py --seed 77 re-roll every take (default seed 20260818)", + " python3 tools/capture.py --keep keep the raw webm beside the mp4", + "A scene takes its own length plus ~8 s of boot. Chrome opens and plays each one; don't click in", + "it. Bad values are rejected before the shoot with the scene index and the reason.", + "", + "REQUIRED KEYS", + " name file name. letters, digits, - and _ only. Must be unique in the file.", + " seconds 3..60. The whole take, from the first staged beat. 12-18 is the useful range.", + " setting gore | zombie", + " gore = the splatter run: slingshot serves at a crowd that walks at you.", + " zombie = ZOMBIE TENNIS: real tennis rules against the roster, one game per", + " opponent, and the opponent bursts when you take his game.", + "", + "OPTIONAL KEYS (defaults in brackets)", + " beat [none] the staged moment — see THE BEATS below.", + " court [grass] grass | clay | hard | gloom", + " grass = CENTRE COURT (green, day), clay = TERRE ROUGE (orange),", + " hard = THE SLAB (blue-grey), gloom = THE NIGHT COURT (dark, floodlit).", + " weapons [none] tins granted at full stock and cycled through — see THE RACK below.", + " weaponEvery [4] seconds between rack switches when `weapons` has more than one.", + " movement [0] 0 = statue, 1 = sweeps the full width. See MOVEMENT below.", + " density [0] bodies kept on court, 0..1 (1 is about fourteen). Topped up as they die.", + " diversity [0] 0 = shamblers only .. 1 = the whole roster. See THE CROWD below.", + " gore [0] blood already on the court when the clip opens, 0..1 (1 is about thirty", + " stains across both halves). The clip adds its own on top.", + " games [8, or boss*10] the run's game count: the HUD shows it and the zombie speed ramp", + " reads it. Higher = faster, meaner bodies. 20+ reads as a deep run.", + " boss [none] 1..10, the chapter whose boss walks on. gore only — see THE BEATS.", + " screen [phone] phone (393x852 -> 1080x2340) | tall (412x915) | 9x16 (270x480 -> 1080x1920)", + " phone is the framing a player actually sees; 9x16 only if a platform insists.", + " turnAt [4.5] transition beat only: when the opponent turns. Everything before it is", + " ordinary tennis, and the cinematic runs about 9 s after it.", + " gamePoint [true] zombie setting only: every serve is game point, so the burst lands inside a", + " short clip. false to play it straight (and probably show no kill).", + "", + "THE BEATS — the staged moment, all reached through the doors a player uses", + " transition THE FLAGSHIP. Opens on an ordinary tennis rally; at `turnAt` the opponent bursts", + " and stands back up wrong, the mob walks on, THE MATCH HAS CHANGED. Give it 17 s.", + " Do not dress this one with weapons or a crowd — the reveal IS the clip.", + " horde A wave is called at 2.5 s: the racket fires itself and you steer. `weapons` and", + " the rack are locked out for its duration, by the game's own rule.", + " boss The chapter boss walks on at 1.2 s. Needs `boss`, and `setting: gore` — one mode", + " owns the court, so a boss cannot start inside a match and the config refuses it.", + " death You are killed 6 s before the end: the blood pool, then GAME, SET, MASSACRE and", + " the run summary. Pair with a high `density` so the death is earned.", + " mopboy The ball boy walks out and calmly mops the blood, 8 s before the end; the", + " autopilot stops shooting so he can work. He only comes for a mess on HIS side of", + " the net, so give it `density` and `gore` or he stays home.", + "", + "THE RACK — all eight tins, granted at nine each and selected in the order listed", + " bomb serve detonates on contact, clears a whole crowd", + " triple serve one serve, three balls fanned wide", + " fire serve kills on any touch AND sets the others alight — the epidemic ball", + " laser serve a beam through the lane", + " gun burst 8 s of spray; you hold the line and steer", + " beacon burst 11 s of self-throwing balls, twice size, every colour", + " flamer burst 6 s wall of fire, and the fire spreads", + " rocket burst 9 s of self-launching rockets, each one a bomb", + " A SERVE tin spends one per shot and the autopilot shoots every ~0.45 s, so nine tins is about", + " four seconds of screen time before the slot falls back to plain balls — list two or three and", + " keep `weaponEvery` at 3-4 for a clip that shows all of them. A BURST tin arms on the next pull", + " and then fires itself for its own length; nothing switches during it.", + "", + "THE CROWD — `diversity` walks this list in order, so 0.3 reaches the steward, 1 the roller man", + " shambler (two skins) · the steward (hi-vis, tougher) · the runner (sprints) · the ball boy ·", + " the spectator (blocks with his chair) · the brute (drags a pole) · the bouncer (built like a", + " door) · the roller man (hauls the court roller). They walk in from behind the far baseline.", + "", + "MOVEMENT — held arrow keys on the real input path, a slow patrol rather than a jitter. It only", + " bites during a rally or under a burst, because the game itself forbids walking between points.", + " Leave it 0 for `setting: zombie`: the match brings its own auto-run legs and held keys", + " override them, so a moving config makes the match player worse, not livelier.", + "", + "THE BOSSES, by chapter: 1 the groundskeeper · 2 the line judge · 3 the chair umpire · 4 the head", + " coach · 5 the physio · 6 the sponsor mascot · 7 the stringer · 8 the tournament director ·", + " 9 the strawberry seller · 10 THE CHAMPION." ], "scenes": [ { - "_": "THE FLAGSHIP. Four seconds of an ordinary SNES tennis game, then the turn. Nothing else in the clip needs to happen — do not add weapons or a crowd to it.", + "_": "THE FLAGSHIP. Four seconds of an ordinary SNES tennis game, then the turn. Nothing else in this clip needs to happen.", "name": "transition", "seconds": 17, "setting": "gore", "beat": "transition", "court": "grass", "movement": 0.35, "gore": 0 }, @@ -31,11 +97,17 @@ "court": "clay", "movement": 0.85, "density": 0.8, "diversity": 0.9, "gore": 0.45, "games": 14 }, { - "_": "The epidemic: a wave lined up across the far court and a flamethrower opened on it.", + "_": "The wall of fire, and the fire spreads.", "name": "flamer", "seconds": 14, "setting": "gore", "court": "grass", "weapons": ["flamer", "rocket"], "weaponEvery": 6, "movement": 0.7, "density": 0.85, "diversity": 0.6, "gore": 0.3, "games": 9 }, + { + "_": "THE EPIDEMIC: the fire ball kills on any touch and lights the rest of the wave off the body it hit.", + "name": "epidemic", "seconds": 13, "setting": "gore", + "court": "hard", "weapons": ["fire"], "weaponEvery": 3, + "movement": 0.5, "density": 1, "diversity": 0.5, "gore": 0.35, "games": 12 + }, { "_": "Artillery night: three tins in fourteen seconds on the dark court.", "name": "arsenal", "seconds": 14, "setting": "gore", @@ -43,7 +115,7 @@ "movement": 0.6, "density": 0.7, "diversity": 1, "gore": 0.55, "games": 16 }, { - "_": "The ball boy calmly mopping a massacre. The pilot stops shooting so he can work.", + "_": "The ball boy calmly mopping a massacre. The autopilot stops shooting so he can work.", "name": "mopboy", "seconds": 15, "setting": "gore", "beat": "mopboy", "court": "hard", "movement": 0.4, "density": 0.55, "diversity": 0.7, "gore": 0.5, "games": 10 }, @@ -52,17 +124,22 @@ "court": "clay", "movement": 0.5, "density": 0.9, "diversity": 0.8, "gore": 0.7, "games": 11 }, { - "_": "Chapter three's boss walks on with the whole rack already gathered.", + "_": "Chapter three's boss — the chair umpire — walks on with the rack already gathered.", "name": "boss", "seconds": 16, "setting": "gore", "beat": "boss", "boss": 3, "court": "grass", "weapons": ["rocket", "bomb"], "movement": 0.75, "gore": 0.4 }, + { + "_": "THE CHAMPION, chapter ten, on the night court.", + "name": "champion", "seconds": 16, "setting": "gore", "beat": "boss", "boss": 10, + "court": "gloom", "weapons": ["rocket", "laser"], "weaponEvery": 5, "movement": 0.8, "gore": 0.6 + }, { "_": "ZOMBIE TENNIS: a rally on real tennis rules, the opponent bursts, the next one is announced.", "name": "match", "seconds": 18, "setting": "zombie", "court": "grass", "movement": 0, "gore": 0.25 }, { - "_": "...and the same mode on clay, deeper into the roster, with the court already bloody.", + "_": "...the same mode on clay, deep into a run, with the court already bloody.", "name": "match-clay", "seconds": 16, "setting": "zombie", "court": "clay", "movement": 0, "gore": 0.8, "games": 20 }