Adding ceiling vents that drop meat sacks into rooms
A climbing duct can now open in a room's ceiling, with rungs up into the shaft, and a vent can hide meat sacks that drop out when the party walks in.
Ducts can now be reached from the ceiling of a room. There’s a grate overhead with rungs under it, and the party climbs up into the shaft crouched and comes back down standing. A vent can also hold an ambush, so when the party walks into the room the grate falls out and meat sacks drop onto them one at a time. The generator lays these on its own.

The film’s frames, in order, with captions.
Why climb into a duct from a room?
I’d already added ducts, and while answering a question about how often they should turn up I wrote that I wanted ducts to be reachable from the ceiling of rooms. The sentence stopped there. I was asked how it ends and picked “Both ways, and monsters too”. So a vent can be climbed into and out of, and small monsters can come out of it.
How does the vent work?
It needed no new mark on the dungeon sheet. A duct that leads up from a room’s square is a ceiling vent, and the importer can tell by the square. The ambush is one optional field on the duct, ambush, written as “meat_sack:2” or as a pack’s list of monsters.
A vent’s shaft has no ceiling and no rock over the square, and its floor stays. The room gets a rust plate round a 1.5 m hole in the ceiling, a second grate under the hole, and rails and rungs that stand free from the floor up into the shaft. While the party climbs, the grate under the hole is switched off. It’s all boxes from the kit for now, so I’d call the look a placeholder. A vent is climbed like any duct, crouched at 0.8 m per second of game time, and the party stands again once it’s down in the room.
How does the ambush work?
The ambush becomes an ordinary pack in the plan, standing on the vent’s square, so it’s planned the way any pack is. The only difference is who may be in it. A member must be a walker that fits through a crawl, which is 1.5 by 1.2 m, and VentAmbush.fits() asks the planner that question. Today only the meat sack passes. The gristle spitter is 1.3 by 2.3 m, so every generated ambush is meat sacks.
When the dungeon is built, VentAmbush.arm() hides the members in the shaft over the grate. They’re invisible and their process is off, which also takes them out of the physics world, but their homes stay where the pack stood them in the room. Each step the node checks whether the party is in the vent’s room on its floor and within 7.5 m of the vent. When it is, the grate falls with a clatter and the first member drops 0.45 s later, then one more every 0.5 s. Each one falls at 9.8 m per second squared to its home and then hunts the party. All of that counts game time, never real time.
Nothing about the ambush is saved. After a load the node looks at where the members are. If any is in the room, they’ve all dropped, so the grate lies on the floor and nothing replays. If none is there, the ambush waits again with the grate shut. A load in the middle of a drop finishes the drop at once.
How does the generator place them?
The generator turns 0.4 of its climbing ducts into ceiling vents, with a random draw of its own, so a duct that doesn’t become a vent comes out exactly as it did before. The foot goes on a room square off the edge, with nothing on or beside it. Of those vents, 0.6 hold 2 or 3 meat sacks, never in the lift’s room and never in a room that already has a pack. The map shows a CEILING VENT, with red bars when there’s an ambush.
How did I check it?
tests/ceiling_ducts passes 57 checks and the duct tests pass 52, with none failing. Sweeps of 40 one-floor and 20 three-floor seeds laid 17 climbing ducts, of which 5 were vents and 2 held ambushes, and none failed. The sounds are borrowed for now: the overhead trap’s sound for the grate and a wall hit for each landing. I want a sheet-metal clang and a wet thump instead.
Also done
- Fixed the test walker that several dungeon tests share. It gave up when a crate and a safe stood 0.75 m apart, which is less than the party’s 0.8 m, so it now opens the door and walks around the furniture to where the way leaves the room.