81c9c4a349
12 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 81c9c4a349 |
LANE8: scales that swing, bags that don't open, and fruit you can throw
The three things the greengrocer was still missing, plus the physics bug that
finding them uncovered.
HANGING SCALES. Real PinJoint3D pendulums, not animations: a static dial and
rod, and a scale-pan rigid body pinned at its own origin so it can only rotate
about the pivot. They hang at chest height down the aisles — dial at eye level,
dish below — where a real shop scale hangs and where you keep walking into it.
The dish swings 0.40 m off a light knock and the pin holds the pivot to 1.1 mm.
The pan is steel so it never breaks; what you get is a heavy brass weight loose
in a room full of stacked fruit.
THE BAG YOU CANNOT OPEN. Tearing one off the roll is the easy half — that's the
setup. Then you have to open it, and the bag has two ends, only one of which
opens, and nothing tells you which. Rubbing alternates arrow keys, because
mashing one key is not rubbing. Either the meter climbs or it doesn't, and the
only way to learn which end you're holding is to have already lost several
seconds to the other one. SPACE turns it over. That is the whole solution and
the game never says so.
CARRY AND THROW. E picks up anything under 6 kg that isn't a record; LMB throws
it at 11 m/s, six times any brittle threshold in the game. Thrown fruit bursts
on landing through the same brittle_speed path a collapse uses — no separate
thrown-object code at all.
Then the part that took the longest. Round produce got sphere colliders (a box
on an apple is why nothing ever rolled) and every display in the shop instantly
fell over. Three separate things were wrong:
- the stack radii were TYPED, not measured. The table said a cabbage was
84 mm; the model is 213 mm, so that pyramid was built with every row driven
a third of the way into the row below it. Main._glb_radius() reads the asset.
- the lattice was box geometry. For spheres the rise per row is
sqrt(4r^2 - step^2/2) — the height at which a fruit touches all four
beneath it. Anything else spawns every row above the first in mid-air.
- there was no tray. A pyramid of spheres on a bare flat table cannot stand;
nothing holds the bottom row in, so the weight above wedges it outward and
the display walks itself apart in a second. Main._stack_tray() frames each
pile in four low timber walls sized to its base row, which is what every
greengrocer on earth already does.
All 310 bodies asleep within 3 s.
While chasing that, the spawn guard fired on a cardboard box in the OFFICE. The
guard is a net, not a test: it only catches a pair Jolt happens to resolve
violently on the frames it's watching, and this one had been interpenetrating
in four levels for weeks. dev/probe_overlap.gd now finds them by measurement,
comparing every pair of dynamic colliders across all six sites. It found 15,
including a row of filing cabinets 0.70 m apart that are 0.80 m wide, and a
stapler inside a monitor. The box asset is 1.35 m across, so boxes are now
stacked into piles rather than dotted about, and the Backrooms scatter uses
rejection sampling with a 1.6 m minimum. All six sites read CLEAN.
Also: the rage veins were drawing every branch from its own start point
regardless of how far the trunk had grown, so at low rage you got disconnected
fragments floating mid-screen that read as biro scribble rather than blood. A
branch now can't appear before the trunk carrying it, and trunks are thick and
dark where capillaries are fine and pale.
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>
|
|||
| a806272a7f |
LANE7: there are two of them, and the room changes one thing every loop
THE OTHER ONE (Entity.Kind.MIRROR)
After three loops a second one turns up, and it shares the first one's silhouette on
purpose — same height, same suit, same dark — so at range you cannot tell which you are
looking at. Its rules are the hunter's rules INVERTED:
hunter the other one
while watched frozen IT COMES
while unwatched it closes inert
noise hunts it deaf
correct play keep it in view LOOK AWAY
Every instinct the first one trains into you is the way the second one kills you, and
that is the entire design. The tells are close-range only and all wrong in the same
direction: head thrown back so it stares at the ceiling instead of you, arms raised, and
feet that never reach the carpet (Backrooms floats it 14 cm).
It also makes no static and doesn't breathe, so the only warning the tape gives for it
is the edge closing in — even while you are looking straight at it.
ONE THING PER LOOP (Backrooms._disturb)
Exactly one change per wrap. Never two. That constraint IS the effect: change nothing
and a loop is just a teleport, change several and it reads as a new room, which is the
opposite of what this place is for. One change means you are never sure whether you
noticed something or imagined it — and since the room is otherwise identical, when you
ARE sure it is worse.
Escalating: early loops shift a prop half a metre, turn one, or kill a bank of lights.
Later ones hang a totem that wasn't there, permanently reveal an object nobody dusted,
or put the hunter exactly where you were standing a moment ago.
The shift is half a metre, not one — a metre is enough to shove a neighbouring prop and
read as TWO changes, and it's too obvious anyway. Half is "…was that there?"
dev/probe_two.gd verifies the inversion with numbers (unwatched 0.00 m, watched 4.39 m,
noise heat 0.00) and that the disturbance fires per loop. Note the probe has to wait out
WRAP_COOLDOWN between laps or only the first wrap ever fires.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|||
| bcd67906a4 |
LANE7: you can die in LEVEL 0, and how you died decides which death you get
NO HEALTH BAR. Nothing else in this game does damage, so hit points would be a
survival-horror UI bolted onto a demolition game. Instead: one meter, DREAD, fed by
every way this place gets at you — being stared at, something close behind you, each
loop, and your own coughing. It decays when nothing is happening to you, and every loop
makes it grip harder (DREAD_LOOP_GRIP), so the place gets better at this the longer you
stay.
At 100% you die, and WHICH source pushed it over decides the death, which is what keeps
the three memes legible at the one moment it matters:
STARE "YOU LOOKED TOO LONG / it was still. it was always still. that was never the
safe part." (Slenderman)
REACH "IT WAS ALREADY IN THE ROOM / you were making so much noise."
Sprays. This is the gory one. (the xenomorph)
LOOP "YOU ARE STILL IN THE ROOM / you walked it eleven times. you will walk it
again." Eleven loops and the room simply keeps you. (Blair Witch)
Gore.gd is procedural — seeded blobs that swell on the lens and then bleed downward in
streaks, a wet burst at the camera (physical deaths only; being taken by the static
shouldn't spray), the camera going down to land on its side on the carpet, then the
card. The sequence is deliberately SLOW: a jumpscare is a spike, but a death should take
its time, because the seconds spent watching the lens settle are where it lands. R wakes
you up.
Player.set_frozen() so you can't stroll around during your own death.
Renamed Backrooms' VHS layer to `tape` — `dread` is the meter now, and it was clearer
anyway.
dev/probe_death.gd drives all three deaths and the revive.
Note: the "Parameter material is null" warning only appears under the headless dummy
renderer; windowed runs are clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|||
| 006c324f1f |
LANE7: LEVEL 0 gets an entity, a tape, and a loop
THE ENTITY (Entity.gd) borrows from three places and each borrowing does a job:
SLENDERMAN it does not move while you look at it, and has closed the distance every
time you look back. Looking is ALSO what builds the static — so the safe
option (keep it in view) kills you slowly and the fast option (run) lets
it catch up. That's the trap, and it's why Slender worked.
XENOMORPH it hunts SOUND, and the thing you came here to do is smash furniture.
Every break and every puff of powder drops a marker it walks toward.
Playing well is what feeds it. A loud enough noise overrides the freeze,
because staring is a valid answer to a stalker and not a valid answer to
something that just heard a sledgehammer hit a filing cabinet.
BLAIR WITCH the level wraps: walk far enough and you come out of the opposite wall
into the same room, because every room here IS the same room. A wrap is
also when it repositions, so "I've been here before" and "it's already
here" land in the same second.
It can't be fought. Getting caught doesn't kill you — it costs a lungful, a spike of
rage, and the knowledge that it can do that whenever it likes.
Deliberately unresolved design: 2.5 m, faceless, arms past the knees, cranium swept
backwards. Slenderman from the front, something else in profile. A silhouette your brain
finishes beats a monster it recognises. Blair Witch stick totems hang in the maze.
THE TAPE (Dread.gd) — one shader: grain, scanlines, chroma split, a wandering tracking
tear, an edge that breathes when it's near but unseen, and Slender interference driven
straight off the stare. The static IS the read-out; there's no meter for it.
TWO BUGS WORTH THE COMMIT MESSAGE
1. `seen_now` was a pure view-cone test with NO line of sight, so it built static
through walls — and in a maze that's most of the time.
2. Fixing that exposed the real one: the generated maze was so dense there was no
sightline longer than a few metres ANYWHERE, which kills the entity outright, since
a stalker you can never see can never be stared at. Thinned the generator — skip a
third of the lattice lines, much bigger gaps — which also matches the reference
photographs far better. They're mostly open floor with occasional slabs, not
corridors.
dev/probe_horror.gd verifies all of it headlessly, including hunting for a clear
sightline first (otherwise the Slender test silently tests nothing).
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>
|
|||
| a7e409593f |
LANE5 spec: harden 3GOD (close open mode, plug SSRF) + deploy web toy publicly
LANE4 spot-checked clean (smoke test green, 20 fractured GLBs in game assets, full web catalog, boss mechanics present). Next blocker between here and a shareable link is ops: 3GOD writes are still unauthenticated + /api/fetch SSRF, and web/ only exists on localhost. LANE5 is recon-first and reversible-steps on production boxes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
|||
| 1d282d4b3b |
LANE4 spec: integrate Lane 2's fractured props into game/ + web/, pointer-lock fallback
Review of the three lanes (2026-07-14): all shipped and verified — Lane 1 smoke test clean, Lane 2's 16 props (intact+fractured) live on 3GOD with the chunk_* contract, Lane 3 play-tested via its ?debug handle (smash, rack cascade, record ritual, debris pool all work). LANE4 closes the integration gap the parallel run left open, plus two review findings: record.fractured.glb 404 spam and no pointer-lock fallback in embedded browsers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
|||
| d0cbe7267d |
Fix stale dig.js path in LANE3 spec (lives in thriftgod/web, not game/scripts)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
|||
| c03bb85c9f |
Add parallel-lane execution specs for Opus 4.8
LANES/PLAN.md — verdict, GODVERSE pipeline architecture, shared contracts, 3GOD security note LANES/LANE1-*.md — Godot: grab/slide/smash record ritual + find-the-misfiled-disc mode (owns game/) LANES/LANE2-*.md — meshgod cell-fracture stage + Destroyulator prop batch -> 3GOD (owns ~/meshgod) LANES/LANE3-*.md — thriftgod-based web smash toy, Rapier physics, 3GOD props (owns web/) LANES/KICKOFF.md — per-lane opening prompts + collision-safety rules Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |