Laying small props on furniture in a Godot dungeon
Desks, shelves, pallets and sacks in the Breakout Site now carry jars, books and tools, placed by a dresser that reads sockets off each piece of furniture.
The desks, benches, counters and shelves in the Breakout Site now have things on them: jars, pots, bottles, stacks of books and papers, small devices, hand tools, coins and scrap. There’s also a camera on some walls, and pallets, sacks, cushions and mattresses on the floors. It took a few rounds of work, each by a different agent. Floor 1 went from 339 pieces with every top bare to 522, which is 1.5 times the bare count.
I’d drawn a note in a room on floor 1 saying there weren’t enough props in it or in most of the others, and asked for twice the density and twice the variety. I wanted the clutter to be separate small models placed by the dresser, not built into the furniture, so I had the desk, table, bench and counter rebuilt bare.
How does the dresser know where a bowl can go?
Each piece of furniture says where its tops are. In the prop kit, a host calls Maker.surf(x0, x1, y0, y1, z, clear) while it’s being built, and that writes a surface socket into its manifest: a position, a rectangle (across and deep) and how much room there must be above it. The game’s table, content/dressing.json, doesn’t have these written by hand. A script reads them back out of the built manifests, so a table row is [x, y, z, across, deep, clear] for each top, and 16 hosts have them.
A small thing that goes on a top is called a lay: a few things together, up to about 0.4 m across. A lay is built on a foot that sits 1 cm under y = 0, and the build stops if anything sticks out past the lay’s width and depth.
Each room type has a clutter list of lays and a strewn list for things on the floor. The dresser deals a room’s list like a shuffled deck. A top carries something with a 70% chance, up to 3 lays in a row, covering at most 60% of its length. Each lay is turned up to 14 degrees, and takes the theme and state of the furniture it’s on. A decayed lay is sparse and a dead one is crumbs, so the dresser never lays a dead one. Lays are drawn out to 25 m and furniture further than that.
What went wrong along the way
An agent found a fault in the finish of every lay. When a decayed state let a loose part drop below the lay’s foot, the whole lay lifted and the foot was left as a dark slab. It showed up in dishes and bottles too. I had every lay built again, and the 17 installed models whose bytes changed went back through the pipeline’s gate.
I also tried merging all the lays on one host into one mesh. In the busiest room that saved under 2% of the draw calls, so I dropped it. And the dead state of a small thing on the floor leaves a ground slab far bigger than the thing, so I don’t install it and filed it as a bug to fix later.
The count ended short of twice the bare one. I was asked whether to raise the fill from 70% and 3 groups to about 90% and 4, and picked “Keep 70% and 3, let the other makers bring the count”. I was also asked whether the small dark things on the black cyberpunk tops should be made light, and said “Leave them dark”.
Adding cases, pallets, sacks and bedding
The first round left two kinds of thing unbuilt. A later agent built them: a case, a pallet with a strapped load, parts bins, a sack, a heap of three sacks, folded cloth, a cushion and a mattress. That’s 8 more kinds and 72 models, in the same three themes and three states.
They came out of the same kit. The chest kind learned a few things: bins, rake, deck and upper, and it can be a lay now. The soft kind gained stuff, deep and blanket. A lay is now recognized by its against key, so a chest or a soft thing can be one. The sacks use the cloth role for their color and not the body role, because the steampunk theme’s body color is copper.
Then I let the worker decide where they go. Stores and staging rooms get pallets and sacks, the kitchen gets sacks, the bunks and the cell get a mattress, and the cushion goes in the bunks, the rec room and the chief’s room. A case, bins and folded cloth go on tops. It decided a mattress is a separate model on the floor, because the bunk and the cot already carry a mattress, and giving each bed a socket would change 27 installed models. It also decided one size of case and one of pallet. I picked “Walked over, as built” for the cushion and the mattress, so they have no collision box, while the pallet and the sacks are solid. Cloth on beds I said “Not yet”.
Floor 1 went from 510 pieces to 521, with 13 new on the floor and 11 of the lays being the new kinds. Floor 3 went from 67 to 71. The busiest room on floor 1, with 62 pieces, measured at 13.44 ms per frame before and 13.45 after, 4,324 draw calls against 4,283, and 463 thousand triangles against 485 thousand.
Why a second pallet?
I asked whether one size of pallet and one of case was enough, and picked “Add a low, half-loaded pallet”. That’s 9 more models. It’s the same slatted deck, 1.2 by 1.0 m, with one low crate of boards on its back left corner and bare slats beside it. It’s 0.45 m high where the full pallet is 0.95, so the two look like different shapes across a room.
The kit needed a new key for it. My first try put the load off-center with the key load, which the shelf and the rack already use, and that changed the shelf’s bytes. I threw it out and added stow, which takes (w, d, x, y): the load’s own footprint and where its middle stands on the deck. For this pallet that’s 0.62 by 0.6 m, 0.2 m left and 0.12 m back. A stowed load writes no surface socket, so nothing is laid on it, and its feet are the deck’s corners. It carries no lid battens either, so a theme can take its lid. The crate loses boards as the theme decays, and the eldritch one stands open.
The dresser puts it last in the strewn list of storage and staging rooms, so everything that was dealt before it is dealt the same. To prove no installed model changed, I had 99 of them built again with the finished kit. All 99 came out as the same bytes.
Floor 1 went from 521 pieces to 522 and floor 3 from 71 to 72. When I saw the film I was asked whether the load being a crate of boards and not a strapped box was right, and I picked “Keep the crate of boards”.
One thing is left. A lay dealt onto the full pallet’s lid could end up inside its second box. I haven’t seen that happen.
Why did floor 1’s store have no pallets?
The store is 5 by 4 squares, 180 square meters, and its fill of 0.24 allows 43.2 square meters of furniture. The furniture stops at 30 pieces on 18.6, not because of the fill but because nothing more on its list keeps every path open. A pallet has 2 places in the open and 6 at a wall after that, and none of them leaves the ways clear, so the store stood no pallet at all.
I’d picked “Pallets first in a store’s list”. That wasn’t enough when I measured it. With the pallets first among the store’s small things the store stood the same 62 pieces and still no pallet, because the furniture is dealt before them. So I made pallet and palletlow the last two entries of the storage kind’s lay, which is the furniture list, and took them out of strewn. It’s a change to one data row and no code. I tried 11 positions for them in the list, and the end keeps the most pieces where they stood.
It costs more than I’d asked for. The whole store gets laid again. Floor 1’s store goes from 62 pieces to 63, with 2 full pallets, 1 low one and crates down from 5 to 2, and 27 of the 62 pieces stand where they stood. Floor 3’s store goes from 72 to 67, with 2 full and 2 low pallets, and 19 of the 72 stand where they stood. Every other room on the four floors is the same. I was shown that and picked “Keep it: land it as built”.

Floor 1’s store from its door, before on the left and after on the right, rendered in game. Look at the empty floor on the left and the pallets on the right.