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>
204 lines
9.8 KiB
Python
204 lines
9.8 KiB
Python
#!/usr/bin/env python3
|
|
"""assets.py — the not-tonight generation manifest (docs/VENUES.md §6).
|
|
|
|
One entry per sprite the three new venues need. `run_batch.py` walks this list;
|
|
nothing here executes anything itself, so it stays readable and diffable.
|
|
|
|
Two routes, per the A/B recorded in docs/SPACES.md:
|
|
|
|
flat — flux draws the finished sprite. For things with no volume: signage,
|
|
screens, boards, wall art, backdrops. Flux picking its own camera does
|
|
not matter when the object IS a flat plane.
|
|
mesh — flux draws a clean PRODUCT SHOT, TRELLIS.2 reconstructs it, Blender
|
|
renders it from the ONE fixed ortho camera every prop shares. For
|
|
anything with volume. The product shot is deliberately NOT a top-down:
|
|
TRELLIS reconstructs best from a clear view of the object, and the
|
|
game's camera angle is applied at render time by `rake`.
|
|
|
|
`rake` follows the house rule the hard way round (LANEHANDOVER, FABLE-SOLO-25):
|
|
**60° for low and wide, ~76° for tall.** Lowering the rake for a tall object to
|
|
"see more face" is wrong — a tall object is already all face, and at 55° it
|
|
renders as a featureless slab.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from typing import NamedTuple
|
|
|
|
# Shared prompt tails. The mesh tail is tuned for TRELLIS: one object, no
|
|
# cropping, plain ground, even light, nothing for the reconstructor to mistake
|
|
# for geometry.
|
|
MESH_TAIL = (
|
|
"single object centred, full object visible, product photograph, "
|
|
"plain mid-grey seamless background, soft even studio lighting, sharp focus, "
|
|
"no people, no text, no lettering, no watermark"
|
|
)
|
|
FLAT_TAIL = (
|
|
"front-on orthographic view, flat to camera, no perspective, "
|
|
"dark background, grimy nightclub, video game texture art, "
|
|
# flux likes to sign its work in the bottom corner. Invisible on a 16px
|
|
# prop, very visible on a 640px street plate.
|
|
"no watermark, no logo, no signature"
|
|
)
|
|
|
|
|
|
class Asset(NamedTuple):
|
|
kind: str
|
|
route: str # 'flat' | 'mesh'
|
|
size: tuple[int, int] # ship size in px, must match PROP_SIZES/TARGETS
|
|
prompt: str
|
|
seed: int
|
|
rake: int = 60 # mesh route only
|
|
gen: tuple[int, int] = (1024, 1024) # generation resolution
|
|
quantise: bool = True
|
|
# True for PLATES (backdrops, skylines, a roller door filling its wall):
|
|
# they are meant to be opaque, and the importer's near-black-to-alpha pass
|
|
# would eat exactly the parts that make a night scene look like night.
|
|
# False for OBJECTS, which want cutting out of their background.
|
|
keep_bg: bool = False
|
|
|
|
|
|
def M(kind: str, size: tuple[int, int], seed: int, rake: int, subject: str) -> Asset:
|
|
return Asset(kind, "mesh", size, f"{subject}, {MESH_TAIL}", seed, rake=rake)
|
|
|
|
|
|
def F(kind: str, size: tuple[int, int], seed: int, subject: str,
|
|
gen: tuple[int, int] = (1024, 1024), quantise: bool = True,
|
|
keep_bg: bool = False) -> Asset:
|
|
return Asset(kind, "flat", size, f"{subject}, {FLAT_TAIL}", seed,
|
|
gen=gen, quantise=quantise, keep_bg=keep_bg)
|
|
|
|
|
|
# ---- The Royal — suburban pub -------------------------------------------------
|
|
ROYAL = [
|
|
M("pokie", (16, 32), 4400, 76,
|
|
"a single poker machine gaming cabinet, glowing screen, red and gold trim, worn"),
|
|
M("bistroTable", (16, 16), 4401, 60,
|
|
"a small square pub bistro table with two dining chairs, laminate top"),
|
|
M("picnicTable", (32, 16), 4402, 60,
|
|
"a weathered wooden picnic table with attached bench seats, beer garden furniture"),
|
|
M("trough", (48, 16), 4403, 70,
|
|
"a long stainless steel trough urinal, wall mounted, public bar toilet fixture"),
|
|
M("keg", (16, 16), 4404, 76,
|
|
"a stack of two stainless steel beer kegs, brewery barrels"),
|
|
M("jukebox", (16, 32), 4405, 76,
|
|
"a classic jukebox with illuminated arched top, chrome and coloured lights"),
|
|
M("raffleDrum", (16, 16), 4406, 60,
|
|
"a small wire mesh raffle barrel on a stand with a crank handle"),
|
|
M("bainMarie", (48, 16), 4407, 60,
|
|
"a stainless steel bain-marie hot food counter with sneeze guard, pub bistro"),
|
|
M("beerUmbrella", (32, 32), 4408, 80,
|
|
"a large outdoor patio umbrella open, canvas canopy seen from high above"),
|
|
M("ashtray", (16, 16), 4409, 60,
|
|
"a battered metal outdoor ashtray bin on a short pole"),
|
|
F("tabBoard", (48, 16), 4410,
|
|
"a bank of three wall mounted betting screens showing horse racing odds, glowing blue"),
|
|
F("dartboard", (16, 16), 4411,
|
|
"a dartboard on a scuffed wooden backboard with darts in it"),
|
|
F("tvSport", (32, 16), 4412,
|
|
"a wall mounted flat screen television showing a night rugby match, glowing"),
|
|
]
|
|
|
|
# ---- Elevate — rooftop --------------------------------------------------------
|
|
ELEVATE = [
|
|
M("cocktailBar", (96, 16), 4420, 60,
|
|
"a short modern cocktail bar counter with backlit bottle shelving, marble top"),
|
|
M("banquette", (32, 32), 4421, 60,
|
|
"a curved upholstered velvet banquette booth seat, luxury lounge furniture"),
|
|
M("ropePost", (16, 16), 4422, 76,
|
|
"a single polished brass rope stanchion post with red velvet rope"),
|
|
M("planter", (32, 16), 4423, 60,
|
|
"a long rectangular concrete planter box full of green foliage and grasses"),
|
|
M("balustrade", (64, 16), 4424, 76,
|
|
"a section of frameless glass balustrade railing with a steel handrail"),
|
|
M("djPlinth", (48, 32), 4425, 60,
|
|
"a raised white platform plinth stage with a DJ controller on top"),
|
|
M("marbleSink", (48, 16), 4426, 70,
|
|
"a luxury marble bathroom vanity with two vessel basins and brass taps"),
|
|
M("ashUrn", (16, 16), 4427, 76,
|
|
"a tall stainless steel standing ashtray urn, hotel lobby style"),
|
|
M("champBucket", (16, 16), 4428, 60,
|
|
"a silver champagne bucket on a stand full of ice and a bottle"),
|
|
M("loungeChair", (16, 16), 4429, 60,
|
|
"a low modern outdoor rattan lounge chair with a cushion"),
|
|
F("liftDoor", (32, 32), 4430,
|
|
"a pair of closed brushed steel lift elevator doors with a call panel"),
|
|
F("skylineCard", (128, 32), 4431,
|
|
"a distant night city skyline of lit office towers, warm gold windows, "
|
|
"seen across a dark horizon", gen=(2048, 512), quantise=False, keep_bg=True),
|
|
]
|
|
|
|
# ---- ROOM — concrete warehouse -----------------------------------------------
|
|
ROOM = [
|
|
M("stackF1", (32, 48), 4440, 76,
|
|
"a huge professional nightclub speaker stack tower, three stacked black "
|
|
"bass bins and horns, sound system rig"),
|
|
M("subBass", (32, 16), 4441, 60,
|
|
"a large black subwoofer bass bin cabinet lying wide, professional audio"),
|
|
M("plywoodBar", (128, 16), 4442, 60,
|
|
"a rough improvised bar counter made of plywood sheets on scaffolding poles"),
|
|
M("pallet", (32, 16), 4443, 60,
|
|
"a stack of two wooden shipping pallets"),
|
|
M("wheelieBin", (16, 16), 4444, 76,
|
|
"a large plastic wheelie rubbish bin with the lid closed, industrial"),
|
|
M("oldCouch", (48, 16), 4445, 60,
|
|
"a sagging ruined old fabric three seater couch, stained and torn"),
|
|
M("pillar", (16, 16), 4446, 76,
|
|
"a square raw concrete structural column pillar, industrial warehouse"),
|
|
M("strobe", (16, 16), 4447, 76,
|
|
"a black professional strobe light fixture on a short stand"),
|
|
M("smokeMachine", (16, 16), 4448, 60,
|
|
"a black fog smoke machine unit with a nozzle, stage equipment"),
|
|
M("cableSnake", (32, 16), 4449, 60,
|
|
"a coiled bundle of thick black stage power cables taped to the floor"),
|
|
M("brokenBasin", (32, 16), 4450, 70,
|
|
"a cracked dirty ceramic wall mounted bathroom basin with exposed pipes"),
|
|
M("canStack", (16, 16), 4451, 60,
|
|
"a stack of plain unbranded aluminium drink cans in a cardboard tray"),
|
|
M("cdj", (32, 32), 4452, 60,
|
|
"a professional CDJ media player deck, black with a large jog wheel"),
|
|
F("rollerDoor", (64, 32), 4453,
|
|
"a large corrugated steel roller shutter door, half open, loading dock",
|
|
keep_bg=True),
|
|
F("ruleBoard", (32, 32), 4454,
|
|
"a scuffed black letter board sign with white push-in plastic letters, "
|
|
"cryptic house rules, chipped frame"),
|
|
]
|
|
|
|
# ---- Staff figures (set dressing props, never patrons) ------------------------
|
|
STAFF = [
|
|
M("staffBartender", (16, 32), 4460, 76,
|
|
"a single bartender standing, black shirt, apron, arms at sides, full body"),
|
|
M("staffDj", (16, 32), 4461, 76,
|
|
"a single DJ standing wearing headphones around the neck, full body"),
|
|
M("staffGlassie", (16, 32), 4462, 76,
|
|
"a single hospitality worker standing holding a stack of empty pint glasses, full body"),
|
|
M("staffSecurity", (16, 32), 4463, 76,
|
|
"a single security guard standing, black bomber jacket, arms folded, full body"),
|
|
]
|
|
|
|
# ---- Door-scene street plates, one per venue ---------------------------------
|
|
STREETS = [
|
|
F("street:theRoyal", (640, 176), 4470,
|
|
"a wide suburban Australian pub frontage at night, painted brick, tiled dado, "
|
|
"a lit beer sign, bins, wet footpath, warm amber light",
|
|
gen=(2048, 576), quantise=False, keep_bg=True),
|
|
F("street:elevate", (640, 176), 4471,
|
|
"a wide polished lobby entrance of a rooftop bar at night, brushed steel lift doors, "
|
|
"marble, planters, cool white downlight, glass",
|
|
gen=(2048, 576), quantise=False, keep_bg=True),
|
|
F("street:room", (640, 176), 4472,
|
|
"a wide unmarked concrete warehouse wall at night, no signage, a single steel door, "
|
|
"graffiti, sodium orange streetlight, puddles, rain",
|
|
gen=(2048, 576), quantise=False, keep_bg=True),
|
|
]
|
|
|
|
ALL: list[Asset] = [*ROYAL, *ELEVATE, *ROOM, *STAFF, *STREETS]
|
|
|
|
BY_KIND = {a.kind: a for a in ALL}
|
|
|
|
if __name__ == "__main__":
|
|
flat = sum(1 for a in ALL if a.route == "flat")
|
|
print(f"{len(ALL)} assets — {len(ALL) - flat} mesh, {flat} flat")
|
|
for a in ALL:
|
|
print(f" {a.route:4} {a.kind:16} {a.size[0]:>4}x{a.size[1]:<3} rake={a.rake}")
|