Key Placement
The key words MUST, MUST NOT, SHOULD, and MAY are used as in RFC 2119.
This page governs where in its room a key pickup stands and when it appears. It does not govern which rooms hold keys or how many — that is the gate economy's scatter rule (Field Gate Economy): BFS depth < 2 from the gated room, at most 2 per room, dead ends first. The three compose: the economy picks the room and the count, this page picks the spot and the moment.
The rule
The original authors where a key sits: 836 o0c_key records over 224 of the 295 reference rooms, 2–6 distinct positions each, decoded by psz-re from object_placement_per_room.json and carried in room_reference.json. A generated field's keys MUST stand on those slots.
psz-godot previously placed the fallback key at the centroid of the cell's portal spawn points and ringed extra copies around it. That is refuted: the centroid has no relationship to where the room's floor is wide enough, which is why keys hover over voids (#627). A centroid is an average of doorways, not a placement.
When the key appears
The original puts keys on the ground when the room loads. psz-godot diverges deliberately (#639): a key MUST materialise when its room is cleared — the last enemy defeated — standing on the spot this page's resolution order picks. The authored slots decide where; the fight decides when.
The rationale is the same playtest line that removed the per-room treasure boxes: a key standing on the floor at entry is a rote pickup — walk over, collect, continue — and in a dead end it means walking back out with nothing gained. Dropping the key from the fight deletes the rote interaction and lets combat deliver the reward: dead end visible, fight, key drops, the gate ahead opens. That is an exploration reward structure, not a participation trophy. It is an intentional divergence from the RE-measured original, recorded here rather than taken silently.
Because a gate's solvability hangs on the key existing, the timing carries hard rules:
- A key room that holds a fight this visit (live enemies, or a wave still queued) MUST hold its key for the room-clear event, and MUST NOT also stand it at entry — the drop is the reward, a pre-standing copy voids it.
- A key room that never fights — an authored cell with no enemies (several in the shipped field quests), or a room whose waves all rolled empty — MUST spawn its key on entry as before. A key that never drops is an unopenable gate: a soft lock, not a design choice. The generator MUST hold up its end by never emitting a key cell without an enemy to fight.
- Re-entering a cleared key room whose keys went uncollected MUST re-drop them — the same respawn contract the collected-keys guard already gives an entry-spawned key.
- The drop fires when the final wave dies — once per clear, not once per wave.
Resolution order
| Case | Behaviour |
|---|---|
Cell carries an authored key_position | That position wins — the quest editor spoke, and generated cells never carry the field. |
| Generated cell with authored key slots | Keys MUST stand on the room's mask-filtered key slots, chosen as below. |
| Cell holds more keys than the room has distinct slots | Slots MUST be reused (modulo wrap) rather than keys dropped — the gate demands them — and each reused copy MUST be ringed apart so pickups stay separately clickable. |
| Room code has no authored slots | Fall back to the portal centroid. This is a fallback for a room code the reference never covered, not a placement policy. |
The mask filter
Authored key records are group-tagged like every other room object: a key exists only in the layout masks that build its group. Faithful placement MUST filter slots by the layout mask the room instance actually drew — the same draw that selected the room's objects (_pick_layout) — so a key never stands in a group the room did not build. One draw per room instance, shared by objects and keys; the selection MUST NOT draw twice.
Two consequences fall out of the data and are behaviour, not accidents:
- Rooms with no recoverable group table (all of s02 and s05) build flat; their key records carry no group and every slot is always eligible.
- Layout mask 4 (the only one naming group 4) is never drawn in a free field — see
authored_objects— so a slot whose only group is 4 is never eligible. Group-4 keys stay unplaced, exactly as the original's group-4 objects stay unbuilt.
When the mask leaves nothing
88 of the 224 key rooms carry slots in group 0 only, and mask 40 — layout 3 — does not build group 0. Under that draw the original would place no key in that room at all. But which rooms hold keys is not the reference table's decision: it is the gate economy's, already committed before placement runs. So when the drawn mask leaves a room no eligible slot, the mask MUST be set aside and all of the room's authored slots used — the key exists regardless, and an authored spot from another layout of the same room beats a doorway centroid over a void. The filter narrows where; it must not unplace whether.
Positions, not objects
room_reference.json says its objects never reach the game, because an unhandled kind would still eat a slot against the 20-object room cap. Keys are the second exception after enemy slots, and the argument is the same with one honest addition: a key is not a passive spawn point — it occupies interactivity, a thing the player picks up. So the split is stated rather than blurred:
- The positions are consumed (mask-filtered, seeded selection), and cost nothing against the room cap.
- The key object still does not spawn through the room-object table —
o0c_keystays undernot_imported_yet. Which rooms hold keys, and how many, remains the gate economy's own rule (Field Gate Economy), not the reference table's.
Coordinates and height
Slots are room-local, same units and orientation as authored objects. Every authored key record carries y = 0, so the pickup MUST NOT trust the authored height: it floor-snaps at the slot's (x, z) and hovers a fixed amount above whatever floor it finds — the same contract boxes already follow. A key buried under a raised floor is an unopenable gate, which is a soft lock, not a cosmetic bug.
Seeding
Which subset of a room's eligible slots a cell uses is a seeded choice made at field generation, so a given field populates identically on every revisit rather than reshuffling under an uncollected key.
What this page does not settle
Which slot the original picks for a given room instance is not known — the same undecoded per-room seed and placement weights as Enemy Placement's subset note. The positions are authored data and are exact; the selection among them is ours. Which rooms hold keys is likewise ours-by-measurement (the economy's scatter rule), not the reference table's.