feat(nightbloom): a vertical danmaku garden on the deterministic runtime#88
Draft
SummonLav wants to merge 6 commits into
Draft
feat(nightbloom): a vertical danmaku garden on the deterministic runtime#88SummonLav wants to merge 6 commits into
SummonLav wants to merge 6 commits into
Conversation
…grammar on the deterministic runtime (#3) * feat(demos): nightbloom — a plants-vs-yokai lane defense on the deterministic runtime One shrine, five lanes, an eternal night: a garden-defense action game in the Plants vs. Zombies grammar, flavored after the eternal-night yokai stories of the Touhou fangame tradition, and simulated in fixed 1/60 s micro-ticks so every simulationHz plays the SAME battle: - plants and foes both evolve I -> II -> III on two laws: plants by their own work (damage dealt / lumen gathered / blows endured, feedable), foes by the night phase — and on the spot when they finish eating a plant - attack/defense matchups as data: armor vs true petal damage, ranged mochi lobbers vs walls, a haste-aura songstress worth focusing first - possession: CIRCLE possesses any plant (the required cute cat included), held CIRCLE channels its fire, L/R switches, TRIANGLE casts its spell card — MOONRISE / PIERCING GALE / NINE LIVES / STONEHEART / PETALFALL - batches align to (t - tStart) * 60 ticks, edges land on the first tick of a frame, held input gates on a half-second plus a frame boundary, fx drift by battle-tick age: the 2 Hz world is a strict subsample of the 60 Hz world, gameplay included Content pipeline: demos/nightbloom/gen-assets.ts generates all 35 sprites and backdrops through the PixelLab pixel-art API from the seeded manifest in data.ts (stats, prompts and filenames in one table, checked by validateContent); evolution stages are init_image chains so a creature keeps its identity as it ascends. Assets are committed; the script is a no-op unless one is deleted or --force is passed. .env (the API key) is now gitignored. test/nightbloom.sim.test.ts drives THE GARDENER (a full 170 s winning night: 31 kills, one ward spent, a stage III bloom) and THE SLEEPER (an untouched loss) through the sim host and asserts repeat identity, chaos immunity, strict 4/2 Hz subsampling of both tapes, cross-rate byte-equal outcome screens, augury effect timing, and the exact end-screen ledger; wired into bun run test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(nightbloom): rework as a vertical danmaku shooter — the Imperishable Night grammar The garden was a lane defense; the night wanted a bullet-hell. NIGHTBLOOM is now a vertical STG: the player PILOTS one plant at the bottom of a portrait playfield (centered on the 480x272 screen, HUD panels both sides), the yokai horde descends from the treeline, and CIRCLE / L / R switch the piloted form mid-fight — the Imperishable Night verb, with the same PixelLab bestiary: - five pilotable forms with distinct attack/defense trades: homing orbs (the cat), piercing bolts, a true-damage petal fan, an armored tank with a full-heal spell, and a moonlight gatherer; spell cards double as bullet clears - two evolution laws: forms grow by their own work (damage dealt, motes gathered — auto-collected above the PoC line — and bullets grazed), the horde grows with the hour (dusk I, midnight II, witching III) - nine waves, a midboss, and a three-card final boss with Touhou-style timeouts; enemy danmaku is native dots off a QUANTIZED sine table (1/8192 steps) because raw Math.sin is not bit-specified across JS engines and a spiral must replay byte-exactly on every host - held verbs (movement, fire, focus) read the raw held mask — a hold's level track goes true at the same battle tick at every rate — while press edges land on a batch's first tick, so the 2 Hz world dodges the same spiral the 60 Hz world does, frame for frame test/nightbloom.sim.test.ts drives THE MARKSMAN (a full clear: every form flies, three reach stage III, the diva's last card breaks with 4 of 5 forms alive — score 8495, graze 61, 35 felled) and THE SLEEPER (an untouched loss) and asserts repeat identity, chaos immunity, strict 4/2 Hz subsampling of both tapes, cross-rate byte-equal outcome screens, augury effect timing, and the exact end-screen ledger. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(nightbloom): hit and impact sfx through a host sound sink Sound is an OUTPUT, never an input. The engine emits SfxKind events from deterministic tick state — the hit thock, the kill pop, hurt/wilt, graze pings, mote chimes, form-switch clicks, spell-card declarations, boss card breaks, and the dawn/eternal stingers — and reads nothing back, so the simulation is byte-identical with or without audio. demos/nightbloom/sfx.ts installs the sink where WebAudio exists (the web host): every voice is synthesized from oscillators plus one xorshift noise buffer (no assets, no pipeline), per-kind throttled, voice-capped, and resumed on the first user gesture per the browser autoplay policy. The headless sim and QuickJS-on-PSP never install a sink and skip the poke entirely. A __nightbloomSfxState seam lets harnesses confirm the device state without ears. Verified live: the AudioContext resumes on a trusted key press, a 26-second browser battle emits shoot/hit/kill/mote/graze/hurt/switch/ spell in sensible proportions, and voices drain to zero after their envelopes. bun run test stays green — the sim never hears a thing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(nightbloom): cuter bestiary regeneration + uniform-scale scene fix All 35 PixelLab assets regenerated from softened prompts: kawaii chibi style strings (huge sparkling eyes, blushing cheeks, plump rounded bodies, pastel glow) and per-entry rewrites that trade fierce for fluffy — marshmallow pyre flames, a chubby umbrella warlord with toy blades, an extra-fluffy moon rabbit, storybook-spooky plum for the eternal night. Seeds and init_image evolution chains are unchanged, so every creature keeps its identity across stages; f-kasa-3 / f-uta-3 still dress the bosses. The sim never sees a pixel: the ledger (score 8495, graze 61, 35 felled) is byte-for-byte the same night. Scene stretch fix: the 256x128 backdrops were drawn inset-0 w-full h-full — 1.875x wide but 2.125x tall, a 13% vertical squash the moon wore visibly. Title and outcome screens now draw the scene at 480x240 (UNIFORM 1.875x, the tidelight rule) over a 32px letterbox band that the footer legend sits in. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(nightbloom): waking roster, the last breath, the moon cat and the mountain Three player-facing changes, one night: - The roster shrinks to three pilotable forms and WAKES as you play: only the CATNIP KIT answers at dusk (the other cards show ?); the first stage-II ascension wakes the sakura SAPLING, and the midboss's fall rouses MOON PRIMROSE. Bamboo and the stone lantern stay in the tables as reserve content. - No pilot switches itself anymore: when the piloted form dies, the LAST BREATH opens — a 1.5 s window (invulnerable, guns silent, banner flashing) to switch to a waking form. Miss it, or fall with nobody else awake, and the night takes the run. The sleeper tape now ends at ~34 s for exactly that reason. - New portraits: the catnip is a black-and-gold cat with a white moon on its brow that waxes as it ascends (crescent, crescent, full); the moon primrose is a gap-moe gorilla — a mountain of carved abs under the sweetest little face — regenerated through the same seeded init_image chains. The ledger moves (score 8145, graze 42, 34 felled, 3 of 3 awakened alive) and every determinism claim re-verifies: repeat identity, chaos immunity, strict 4/2 Hz subsampling of both tapes, byte-equal outcome screens. bun run test green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(nightbloom): living avatars, pokemon growth, rainbow reveals, bananas, three true abilities Five player-facing touches, all on the tick: - The avatar is alive: a 1.2 s triangle-wave idle bob, a lean into the strafe, and an invuln blink — pure functions of the battle tick, so the animation subsamples as exactly as the sim. - Pokemon growth: each stage is its own portrait AND its own size — the avatar draws at 22 / 27 / 32 px as it ascends (the hitbox never grows). All three chains regenerated with baby -> grown -> majestic language; the moon cat and the ab-carved gorilla keep their approved looks. - When a ? card wakes, a rainbow comet (pink-cyan head, amber-pink tail) sweeps it left to right — 48 ticks of reveal, driven by unlockedAt. - MOON PRIMROSE throws BANANAS: heavy slow volleys of spinning fruit (rotation is a pure function of tick and shot id), new shot-banana art. - Three true abilities beyond shot shapes: the catnip dances with death (wider graze ring, double graze glow); the sakura heals — every damaging petal mends the most wounded waking form by 1, at the price of soft hits; the gorilla hits hard and gathers — motes are worth double glow to him. The ledger moves to score 8235 / graze 53 / 34 felled; every determinism claim re-verifies at 60/4/2 Hz. bun run test green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(nightbloom): facing mirror, slanted particle reveals, world drift + prune unused art - The avatar mirrors into its strafe (scaleX on the sprite, lean on the container so the two transforms never argue): press LEFT and the moon cat looks left, and keeps looking there until it flies the other way. - The unlock reveal is now a SLANTED shine (18 degrees, two gradient bars) trailing twelve specks of pink/cyan/amber dust whose spacing widens quadratically — densest right behind the head, thinning out. Pure translateX off the reveal progress, deterministic as ever. - The world never stops scrolling: hovering foes (wisps, sparrows) sink 10 px/s with the starfield while on station, hold for 8 s instead of 12, then accelerate off — an unkilled monster always leaves the field with the view instead of parking forever. - Pruned everything the roster cut left behind: bamboo and stone-lantern defs, their six sprites, shot-bolt.png, bg-field.png (the STG field is native-rendered), the GALE beam, the STONEHEART shield, and the "bolt" shot kind. PlantId narrows to the three real forms; the pak drops to 37 entries with zero placeholders. Ledger: score 8280 / graze 56 / 34 felled, dawn at ~162 s; the sleeper falls at ~40 s. All determinism claims re-verified at 60/4/2 Hz. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(nightbloom): healer-soft sakura, banana boomerangs with a catch, per-art facing - The sakura is a healer first: petal damage drops to 3/4/5 (still true damage, still a wide fan) — her worth is the mending, one hp to the most wounded waking form per damaging petal. - The gorilla's bananas are BOOMERANGS now: they fly up, stall against 260 px/s^2, turn, and home back to his hand at 240 px/s. A banana never despawns on a hit — it cuts through on the way out and again on the way home (one touch per 0.3 s) — and only a CAUGHT banana can be thrown again: at most three aloft, with a hand-count of banana pips on the HUD while he pilots. Throw depth grows with his stage. - Facing correction per portrait: the sakura and the gorilla came out of the generator facing left, so their strafe mirror ran backwards. Each form now declares artFacing and the renderer mirrors facing * artFacing — every pilot looks where it flies. Ledger: score 8435 / graze 78 / 33 felled, dawn at ~182.8 s, all three awakened forms alive. Determinism re-verified at 60/4/2 Hz; bun run test green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(nightbloom): boss metamorphoses — per-phase portraits, cries, and a harder diva Every spell-card change is now a visible, audible transformation: - Four new 64x64 boss portraits: IRON KASA GROWN WRONG for the midboss, and three diva forms — the stage dress, the flared-wing chanter, and a phoenix-like final form. The sprite AND its draw size change per card (52 -> 58 -> 66 px), the hit radius scales with the form, and the switch lands with a scale-in pulse and an expanding flash ring. - Each boss has its own cry, played on entry and on every metamorphosis: the diva gets a three-chirp bird call, the umbrella a beating metal clang over a low whoomp — synthesized like the rest of the sink. - The diva sings harder: card hp 720/830/950, denser rings (14), faster spirals and mochi pairs, +4 bullet speed. The gardener's tape still breaks the finale — at 184.1 s now, with the catnip down to 70 hp and 92 grazes on the ledger. Score 8575, all three forms alive. Cross-size init_image turned out to be a pixflux 500, so the two boss bases anchor identity by description; the diva's later forms chain from her first at 64x64. Determinism re-verified at 60/4/2 Hz; bun run test green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(nightbloom): the whole game in the codex — a two-page in-game book SELECT now cycles a two-page book instead of one screen: - Page one, THE PILOT'S MANUAL: every control (fly, fire, focus, switch, spell, pause), the shape of the night (nine waves, the midboss, the diva's three transforming cards), the growth laws (glow from wounds, motes and grazes; the PoC line), and the LAST BREATH rule. - Page two, FORMS AND FOES: the three forms with their ability laws, the waking rules (catnip at dusk, ascend for the sapling, fell the umbrella for the ape), the foes with theirs, and the phase table. The native host does not soft-wrap text, so the book is typeset by hand into short lines and the data laws are split at a word boundary; foe laws are tightened to single lines so both columns sit inside 272 px. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(nightbloom): left-panel long lines scroll like a ticker instead of clipping The AUGURY omen, the boss's name and the spell-card title overflowed the 134 px left panel and clipped at the box edge. They now ride a marquee: two copies loop leftward, driven by the battle tick — so the scroll subsamples exactly like everything else — and lines short enough to fit hold still. Width is estimated from the glyph count. The hero screenshot is retaken on the FINALE with the moon cat piloting (NINE LIVES charging) and the panels mid-scroll. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(nightbloom): dawn medals — decorations for the wrong things Clear the night and the dawn screen pins HONORS, OF A SORT on you: up to three roast medals derived from the run's ledger — MERCY MEDAL (foes that strolled off unharmed), PINCUSHION (hits taken, and proud), COMPOST AWARD (gardeners wilted on your watch), OUTSTAYED WELCOME (cards that died of old age), LITTERBUG (motes left in the grass), PERSONAL SPACE (not one graze all night). A spotless run earns SUSPICIOUSLY PERFECT — the night demands a rematch. Win screen only; the eternal night roasts nobody. The engine grows a roast ledger (escaped / hitsTaken / motesMissed / cardTimeouts counters plus seam accessors), the end screen is compacted to fit the medal case, and the sim test pins the marksman's exact decorations: 2 strolled off, struck 33 times, 24 motes in the grass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(nightbloom): the dawn medal ceremony — score first, then the stamp The dawn screen is a two-act sequence now, on the outcome screens' own clock: - Act one: the score takes center stage and ROLLS UP arcade-style over the first 1.2 s; the rest of the ledger waits in small print below. - Act two, at the 2 s mark: ONE medal — the roast pick of the run — is slapped onto the glass: a PixelLab gold-and-ribbon medal base slams in from 3x scale, tilted, lands at -12 degrees with a screen shake and a thud from the sound sink, and the paperwork dims beneath it. The title is stamped in layered art lettering (amber over pink over shadow) with the detail line on a plaque below. The end clock is (wall ticks - the tick the outcome settled on): both terms are rate-aligned, and the settling frame updates it in-batch, so the count-up and the slam land on the same virtual moments at every simulationHz — the full 190 s runs, stamp animation included, remain strict 4/2 Hz subsamples of 60 Hz. The sim test pins the final frame: score 8575 on stage, MERCY MEDAL stamped over it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Batch PSP swarm rendering, fix the 59-second particle corruption, reduce second-phase combat load, and rebalance healing around Moon Primrose.
Author
|
PSP performance follow-up is now included in
Measurements are PPSSPP PSP-side timers, not real-hardware proof. |
Reduce Nightbloom PSP frame cost, move Moon Primrose to a 28-mote unlock, and render PixelLab cat-paw projectiles through one shared particle texture.
Author
|
Nightbloom PSP performance follow-up is now included in this PR at
Validation:
|
Rename the playable cat, remove botanical cues from its PixelLab prompts, and replace its projectile with a pearly white prismatic paw while preserving the shared particle batch.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A new demo: NIGHTBLOOM, a vertical danmaku shooter in the Imperishable Night grammar with a Plants-vs-Zombies-flavored cast — built as a stress test of the framework's determinism story on a genre that usually resists it: a real-time bullet hell.
What it is
A 480×272 portrait-field STG (
demos/nightbloom/): you pilot one plant at the bottom of the field and swap between a roster of three — a graze-master cat, a healer sakura whose every hit mends the most wounded form, and a gap-moe gorilla throwing banana boomerangs that must be caught (max 3 aloft). Foes descend in phased waves toward a midboss and a three-card diva whose every metamorphosis brings new art and her own voice. Locked roster cards wake through play (first stage-II ascension, midboss fall), each with a slanted rainbow sweep and particle tail. If your piloted form wilts and you don't swap within 1.5 s, the night is eternal — LAST BREATH. A run ends in a two-act dawn ceremony: the score counts up center stage, then a gold medal slams on like a sticker — always a roast (MERCY MEDAL: "2 foes strolled off unharmed").Full gameplay + controls live in the in-game codex (SELECT) and
demos/nightbloom/README.md.Why it belongs in this repo
The whole game runs on the deterministic contract:
hashes4[m] === hashes60[15(m+1)−1]and cross-ratefinalFrameequality.{maxSleepMs: 6, gcEvery: 32}hashes identically to the clean run.respond()behindrunEffect, delivered on the virtual clock exactly one virtual second later at every hz.globalThissfx sink; the browser host installs a WebAudio synth, sim and PSP hosts install nothing, so audio never touches the state stream.engine.tsheader.Content pipeline
All 35 sprites/scenes/portraits are generated by the PixelLab API from a seeded manifest in
data.ts(bun demos/nightbloom/gen-assets.ts), with evolution stages chained viainit_imageso each creature keeps its identity as it grows, then re-encoded into the pak-safe canonical PNG subset. Generated art is committed; the script is a no-op unless assets are deleted or--forced. Same contract as tidelight's pipeline.Tests
bun run testnow ends with the nightbloom chain: content-table validation, repeat identity, chaos, 60/4/2 Hz subsampling on two tapes (a 190 s winning run and a no-input sleeper), cross-rate final-frame equality, augury delivery timing, and end-ledger probes (score, graze, medal text). 10 tests, 1320 assertions, green alongside the existing suites.🤖 Generated with Claude Code