e588508436
9 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| e588508436 |
LANE10: the whole shop — four rooms, stitched by its own portal graph
The hip-hop room has stock (7 racks, 47 bins, 2,865 records); the entry hall and back room are empty. Import all four anyway: the empty rooms are the connective tissue, and they are the only floor in the building big enough to get an office chair up to speed. A portal pair is one doorway seen from both sides, so the importer walks the graph and places each room by making its portal land exactly on its neighbour's. Every pair here sits on parallel walls with opposite facings, so each join is a pure translation — and when one isn't, it says so rather than quietly getting it wrong. It then recentres the union, emits the interior walls with a gap at each doorway, and puts the spawn in the entry hall, because that is the way in. hiphop-room <-> entry-hall <-> shop-floor <-> back-room 12.96 x 15.41 m - 36 racks - 325 bins - 302 real genre labels - 24,024 records Levels.record_store() now takes the walls and rooms from the import, hangs the street glazing on the entry hall's outer wall, and lays a light grid across whatever footprint the rooms came out as. probe_store.gd also checks the new facts: five doorways exist, and the spawn has nothing in it (excluding the player, who is supposed to be standing there — that false positive was the probe's bug, not the level's). All gates green: smoke clean, 7/7 sites 0.00 m/s, overlap CLEAN, all probes pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|||
| 8e61640c38 |
LANE10: SITE 7 — the real shop, imported from its own database
The level the whole game started as, and the only one that isn't authored. robotmonster.party/store/ is assembled procedurally from Postgres (the wowplatter virtual_* schema, served by recordgod), and this game's sites are already data — so tools/import_store.py reads the live shop and emits assets/store/shop_floor.json: the actual 6.66 x 9.51 m floor, 35 racks across 21 archetypes, 278 bins carrying their real genre labels (HARD TRANCE, $12-$15 EURO/ITALO HOUSE), 21,159 records of stock. Levels.record_store() loads it; 'store' joins ORDER as the seventh site. The shop's schema IS the cascade. virtual_crate.rack_id already records which rack holds which bin, so bins arrive frozen and supported_by their rack and none of the structure is authored. To carry that, smashables gained two optional generic keys — 'id' and 'supported_by' — so any level can express support as data. Every existing level is unaffected. Two reconciliations, because a storefront is a renderer: - Mesh sizes are a lie: 21 archetypes share a few stand-in GLBs, so each piece carries 'fit' (the archetype's real dims from the shop's table) and _glb_piece scales the visual to it and builds the collider from it. Also fixed racks being flattened to the floor — pos_y is a piece's BOTTOM, which is what sit_on means, so the ceiling beams, aircon and wall shelves now stay up where they belong. - Nothing collides in a renderer: racks that sat inside each other for years get separated (weighted, so racks barely move and aisles keep their shape — 3 m of correction across 35), rack footprints inset 1.5 cm a side for the few genuinely wedged units, and the importer AUDITS ITS OWN OUTPUT with the same test probe_overlap runs. Bins are deliberately not separated from their rack: cargo inside a container is what the shop means, they spawn frozen, and the audit skips frozen bodies because a frozen body cannot be depenetrated across the room. dev/probe_store.gd: 22 racks hold bins, 255 labels survived, and smashing the rack carrying 22 bins releases exactly those 22 while the other 256 stay frozen. All gates green: smoke clean, 7/7 sites 0.00 m/s, overlap CLEAN, LANE9 probes pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|||
| c96c2f1a67 |
LANE8: the greengrocer — soft stock, stacks that collapse, and litres
The only site whose contents are mostly SOFT, and that one material change rewrites the
level. `produce` is a new Smashable material that throws no shards at all — Splat.gd
bursts it instead: coloured pulp, a puddle that STAYS, a squelch, and litres counted,
because "how much juice did you make" is a far better score for a greengrocer than "how
many objects did you destroy".
Two knock-ons make the level play itself, both falling out of existing systems rather
than needing new ones:
* produce has a low brittle_speed, so anything heavy landing on it squashes it — which
means a pyramid crushes its OWN bottom layer as it collapses, with no special code.
Knock one off the top and the pile does the rest. Shove test: 257 items -> 45, and
42 litres of juice out of the collapse.
* puddles are slippery, cutting ACCELERATION rather than top speed so it reads as
sliding rather than as being slowed down. The more mess you make, the less the floor
cooperates.
Stacks are built bottom-up by Main._build_stack and nothing is glued or frozen — they
stand because they are stacked, so pulling one out of the bottom row does what you'd
hope. 258 produce items spawn and settle to 0.00 m/s with no spawn crush. The glass
bottles behind the juice bar are the deliberate exception: one shelf in the room that
still rewards a proper swing, and the contrast between litres and shards is the joke.
Round produce is authored CENTRED, not floor-centred like every other prop in this repo,
because a rolling body whose origin sits on the floor plane wobbles like a loaded die.
_glb_piece grew a `centred` flag for it.
REAL BUG FOUND, and it was costing hits in every level: _strike took the NEAREST collider
of any kind, so a static surface could eat a swing. Leaning over a display table, the
table edge is a few centimetres nearer than the fruit piled on it, so every swing hit the
table and nothing broke — 0/306 for the whole first take. It now prefers a Smashable and
only falls back to loose bodies if there isn't one. The offices were quietly losing hits
on desks and shelves the same way.
tools/gen_produce.py: apple, orange, tomato, watermelon, cabbage, banana hand, glass
juice bottle, display crate. The melon's stripes were radial boxes first time and it came
out looking like a sea mine; they're thin lenses through the middle now.
dev/probe_grocer.gd verifies stacks settle, that produce yields litres and NO debris,
that a collapse crushes, and that none of it leaks to the next site.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|||
| 7c282c298e |
LANE7: LEVEL 0 — the Backrooms, generated walls, and the dust mechanic
"stuff is invisible have to release dust or gas or something to coat items to see them to destroy them? and you inhale some of it too" THE SITE (Levels.backrooms) Mono-yellow, 2.55 m ceiling, damp carpet, a dense grid of buzzing troffers, no windows and no exit. THE WALLS ARE GENERATED — this is the site that proves the Floorplan spec can be emitted rather than authored: a seeded RNG lays partial wall runs on a 4.6 m lattice with random gaps, plus 22 freestanding pillars. It follows the reference photos rather than a maze algorithm on purpose — the Backrooms aren't corridors, they're big offset slabs in an open floor, and a proper maze would be legible. Legible is wrong. THE MECHANIC (Dust.gd) — it argues with itself, which is the whole point. Everything smashable here is invisible but SOLID; you find things by walking into them. RMB with the extinguisher throws a cloud of powder. Anything it touches is coated, glows faintly out of all that yellow for 7 seconds, then settles back into nothing. And you breathe it. Every cloud puts a little in your lungs, and the haze that builds is on YOUR side of the glass. So: spray more to see individual objects, and see the room less. Past 72% you cough — camera jolt, screen bloom, and a rage tick, because of course it does. Intended rhythm is spray -> switch to something heavy -> break it before the coating settles -> spray again, coughing. Pairs with TOTAL DESTRUCTION, since the level IS a search. Tuning that mattered: the first cloud was a wall of big blocky quads that hid the very thing it was revealing (150 finer, shorter-lived particles now), and a coated object has to POP out of a room that is entirely one colour, so the coat is near-white and faintly self-lit rather than a subtle tint. 78 objects, not 34 — a search with nothing in it is just a walk. Bug found while testing: `x as Smashable` on a freed object is itself an error in Godot 4, so the validity check has to come BEFORE the cast — destroying anything mid-level was spewing "trying to cast a freed object" every frame. dev/probe_backrooms.gd verifies the whole loop headlessly, including that the mechanic does NOT leak into the other four sites when you leave. All five sites still settle to 0.00 m/s. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|||
| e5f4e8c5e4 |
LANE7: the Gauntlet, four modes, two more provocations, and ambient attrition
THE GAUNTLET (Gauntlet.gd + Cabinet.gd)
Five identical cabinets, each with its alphabetical range on a Label3D. The brief names
a CLIENT, never a cabinet, so working out that HENDERSON lives in F-J is the puzzle —
no waypoint, nothing glows. Destruction is granular on purpose: pull a drawer, it
slides out on a spring and goes live, and the folders inside become individual
smashables. Doing that by hand inside the clock isn't really possible.
The perfect tool: every site hides a SHREDDER that eats a whole open drawer at once,
and the game never says so. Noticing it and working out what it's for IS the level.
Getting it wrong isn't fatal, just slow — the right shape for a secret, since it
rewards the observant without hard-failing everyone else.
Each site nominates its own records area in its spec. Building the bank 3.4 m in front
of wherever the player happened to be standing put it through walls.
MODES (Modes.gd, M cycles)
CUBICLE HELL the main loop
THE GAUNTLET above
TOTAL DESTRUCTION no job, no pay, clear the site, scored on time
AGAINST THE CLOCK fixed timer, maximise damage VALUE — the exact inverse of Cubicle
Hell, where expensive things become the goal rather than the
mistake. Same level, opposite instinct.
Entering a mode arms only what that mode needs; Cubicle Hell and the Gauntlet are
mutually exclusive, because you cannot be doing your job and shredding the Henderson
file at the same time.
TWO MORE PROVOCATIONS (Tasks.gd)
STAPLES SPACE lifts a staple, then it must be pulled the way it's bent — and it's
bent differently each time. Dither and the sheet tears, bundle reprints.
GUILLOTINE align to a mark and cut. The guide has a PERMANENT offset and nothing on
screen admits it. Once you work out the error it's easy, which is the
joke: you have to learn to distrust the equipment.
Desks now alternate spreadsheet/staples so "a desk" isn't always the same minigame.
AMBIENCE (Ambience.gd) — the attrition you can't play around. The tube above your desk
flickers, and actually flickers the real lights. Fish in the microwave. A phone ringing
four desks away. Reply-all, forty-one recipients, "thanks!". Small periodic ticks with
no interaction at all; it's what makes the room hostile rather than merely beige.
Fixes: the payslip was rendering over the Gauntlet (it belongs to Cubicle Hell, and only
once the shift has actually ended); gauntlet drawer reach was 2.0 m when the true
distance from standing-at-a-cabinet to a bottom drawer is over 2 m.
dev/probe_gauntlet.gd drives a whole run headlessly — mode switch, labels, open every
drawer, shred, win — because that catches the failure that matters: a run you can't
finish because the files never become destroyable.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|||
| dd905d908d |
LANE7: CUBICLE HELL — the tilt loop, the job, and the payslip
The game stopped being a smash sandbox. You work a shift, the work provokes you, a
meter fills, you snap, and you cannot go back to your desk until you have broken
enough things. Then you sit back down and try to earn the rest of your day.
The score is your payslip: WAGES (tasks completed) - DAMAGES (everything you broke).
The load-bearing detail is that rage drains a FLAT amount per object destroyed while
each object bills at its OWN value — so during a meltdown the play is to wreck a lot
of cheap rubbish fast rather than put the sledgehammer through a filing cabinet. You
don't choose whether you lose your temper, you choose how much it costs. FURY (objects
broken while melting down) is tracked separately and deliberately isn't money, so
"I ended the shift owing them $2,400" stays a brag.
Rage.gd — WORKING -> MELTDOWN -> COOLDOWN. During a meltdown the clock STALLS if you
stop destroying; you must actually be breaking things.
RageOverlay.gd — the blood vessel. A vignette that closes in with rage and jumps
inward on every heartbeat, procedural veins that creep further into frame the angrier
you get, and a synthesized lub-dub going 62 -> 172 BPM. Zero assets: the shader is
built in code, the veins are draw_polyline from a seeded RNG, the heartbeat is a
generated WAV. Tops out as a heavy frame rather than a red screen — you have to be
able to see what you're about to destroy.
Tasks.gd — stations you walk to and are LOCKED IN PLACE at, because you're at your
desk and that's the joke. Two so far:
SPREADSHEET the selected cell silently drifts one cell partway through entry, with
no sound and no animation. Commit without noticing and the figure lands
in the wrong cell and you start again. This is the whole thesis of the
game in about forty lines.
PHOTOCOPIER jams, and wants the right tray out of 1, 2, 2A, 3, 3B.
Snapping ejects you from the station at no penalty — you didn't choose to walk away.
CubicleHell.gd — 5-minute shift, wages vs damages, meltdown count, end-of-shift
payslip with a verdict line.
Also: HUD gets a monospace face (Godot's fallback is proportional, so the spreadsheet
grid and the work-order docket never lined up), and the arcade HUD's headline becomes
net pay, red when negative.
LANES/LANE7-cubicle-hell.md captures the rest of the design: more provocations, the
GAUNTLET mode (find which of many cabinets, pull drawers, destroy files individually,
and work out which tool the level wants without being told), the other three
workplaces, and why doratracyer/floor_plan can't help — it's OpenSCAD SVG->STL for
3D printing, no generation, no room polygons, GPL-3.0. Office.gd is already most of
a parametric floor-plan builder; lifting its numbers into a data spec is the real path.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|||
| d17e2314aa |
LANE6: LEVEL 01 is an office — Dunder Mifflin floor plan, and a spawn guard
The game had no room. Props sat on a grey disc in a black void, which quietly broke the premise: a game about wrecking your workplace needs a workplace. Office.gd — LEVEL 01 - Open-plan office laid out after The Office (US) floor plan: bullpen of facing desk pairs behind cubicle partitions, reception, glass-walled manager's office, conference room, break room with vending machines, copier alcove, warehouse roller door. - Drop ceiling on a T-bar grid with fluorescent troffers that ARE the light sources (the old scene lit an interior with one outdoor directional lamp, which is exactly why it read as "props on a plane"), window wall with blinds, magnolia and carpet. - Office owns the shell + static fittings; Main._populate() owns everything smashable and asks Office where things go. Walls/desks/counters are boxes because they ARE boxes; chairs, vending machines, microwave and plant come from a new Blender generator (tools/gen_office_props.py) because a box reads as wrong for those. The level no longer falls over on its own - Records were stacked 3 cm apart vertically while being 30 cm TALL, so Jolt resolved 27 cm of interpenetration explosively on frame one and shoved the furniture over before the player touched anything. They now stand side by side. - Per-material mass (MASSES): everything was 1 kg, so a thrown record could tip a filing cabinet. - Spawn guard: placing ~40 props by formula guarantees an occasional overlap, and depenetration is violent (a chair left the building at 500 m/s). Dynamic bodies are speed-limited for 0.75 s, and each offender is named once with the position it was PLACED at, so the cause stays visible instead of being papered over. It then found the real bug: bullpen rows 3.6 m apart left the two rows' chairs meeting back-to-back with 3 cm to spare. Rows are now 4.8 m apart. - dev/DemoDriver.gd act 0 touches nothing for 5 s and prints total body speed. Now reads 0.00 m/s with zero spawn warnings. Two real melee bugs found while testing the level - The hit test was a SPHERE parked at `reach`, so anything CLOSER than the weapon's reach fell in front of it and was missed — you could stand against the printer with a sledgehammer and swing straight through it. Now a capsule swept from the camera. - Swings started at eye height (1.6 m), so a carton on the floor was ~1.5 m away even standing over it and short-reach weapons could never touch anything on the ground. Swings now originate at hand height. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|||
| 5852322606 |
Restructure into monorepo: game/ is the Godot lane
Repo root moves up from game/ to the project folder. Lanes live as top-level dirs (game/ now, web/ planned); LANES/ will hold parallel-session specs. 3D-STORE asset vault stays out of git. History preserved as renames. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
|||
| a8b735f79e |
Destroyulator prototype: first-person cascade smasher (Godot 4.7)
Mac-native (Apple Silicon / Metal), Jolt physics. - First-person controller + semi-opaque viewmodel hands - Punch (L-click) / kick (R-click) via a swappable MeleeAttack resource - Structural support-graph cascade: knock the legs -> the rack pancakes - Brittle vinyl records shatter on hard impact - Day-0 stress gate (B: rain 500) — holds 60fps on the M3 Ultra Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |