127 sprites had been generated and about eight had ever been looked at. The
pipeline will happily render the wrong object, confidently and well-lit, and no
assertion can tell — barrier and flightCase are both "a grey box with edges" as
far as any test goes. tools/gen/contact_sheet.py puts every shipped sprite on one
page at integer upscale on the venue's own floor colour. It immediately showed
about twenty props reading as pale blobs.
First instinct was that the vertex baker had lost the albedo. That was wrong:
sat=0 on ashUrn, marbleSink and pillar is honest, because stainless and marble
and concrete really are grey. The fault was BRIGHTNESS. Props that read sit at
median luminance 23-123 (patioHeater 23, arcade 45, poolTable 53, cdj 68); the
blobs were 150-190. The venue is painted at ~35 under a 50% black sheet and props
are lit by the room's own LIGHTS, so a prop arriving at 180 doesn't read as a
bright object — it reads as self-illuminated. Every prop that worked before
worked by accident of subject matter; the first pale subjects exposed the gap.
props_import --dim scales RGB until median opaque luminance is <=110. Only ever
darkens, so anything already in band passes through untouched. Fixed 48 sprites
with no regeneration at all — the 512px Blender renders were still in
art_incoming, which is exactly why that directory is kept.
Also: poster7 shipped with 29% of its pixels and poster9 with 42%, because the
near-black-to-alpha pass was eating the dark half of a dark poster. Posters are
rectangular plates on a wall, so 4-9 now keep their background and fill the frame.
And cf_flux --probe was reporting EXHAUSTED on an account with a full 10,000
neurons: the probe prompt was "a grey square", Cloudflare's safety filter refused
it as NSFW, and every non-429 fell through to SystemExit. A health check that
can't tell "refused" from "empty" will eventually call a healthy system dead.
Gate: lint clean, build clean, 821 tests passing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The venue ladder has always listed four venues in data/venues.ts, but they
shared ONE floor map and differed only by difficulty knobs — so a promotion you
survived a whole week for looked exactly like the room you had just left.
- venueMap.ts grows a FloorLayout contract (map + props + lights + posts +
probes + palette) and the newGrid() primitives every room is painted with.
Voltage stays IN venueMap rather than moving to layouts/, so the new rooms can
import primitives from it without closing an import cycle.
- Three new rooms in src/scenes/floor/layouts/: The Royal (horseshoe public bar,
pool room, pokies corner, trough + two cubicles, a beer garden that is plainly
the nicest room in the pub, and a DJ "corner" that is a folding table because
this pub never built a booth), Elevate (open-air rooftop — most of the grid is
sky, the DJ is on an unwalled plinth, you arrive by lift), ROOM (concrete
warehouse, pillars you path around, central booth, loading-dock smoking area,
twelve lights in the whole venue).
- Nothing about a room is a module singleton any more. FloorView, sweep and
FloorDemoScene take the layout; the door picks its street plate by venue id.
- FloorDemoScene's six hardcoded station spots (TAPS_SPOT, DECKS_SPOT, ...) were
Voltage's tile coordinates. At four venues a hardcoded (47,3) puts the bar
shift inside The Royal's pool room, so each layout now names its own stations
and the scene reads them from there.
- tests/floor/layoutInvariants.ts is an executable rulebook: perimeter holds,
every walkable tile reachable from the entry, anchors on walkable ground,
posts not sealed (a post on a sealed tile freezes the player for the night —
that has shipped twice), probes reachable, props on-grid. Proved against
Voltage BEFORE the new rooms were authored against it.
Dev route #floor:<venueId> boots the floor straight into one room, because
otherwise seeing ROOM means surviving three weeks of the ladder.
Gate: lint clean, build clean, 821 tests passing (was 784).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Audit found the dunnies were the biggest room in the venue with ONE prop in it
(and a minigame happening there). Nine new kinds placed across the five thinnest
spaces. House-style note recorded: props are raked three-quarter top-down, not
strict orthographic — a fridge from directly overhead is a grey box.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>