Free-Field Generation (RE-measured)

A PSZ free field is not a hand-drawn dungeon and it is not the invented 5×5 sketch the old generator produced. It is a small, gated tree of reused room tiles whose shape, gates, keys, and enemy tables were measured out of the original ROM by the psz-re project. This page is the normative contract for reproducing one to parity. The reference observations live in data/re_reference/; the Valley field (data/field_quests/valley_field.json) is the first built to it.

The room vocabulary

Every stage ships the same room codes; the first letter of the code encodes the room's door count and shape — this is confirmed by the game's own name-format table (psz-re FUN_0206D2E4):

A field's variant letter is its area role: a/b are the two explorable areas, e the single-room transition between them, z the boss arena. So a full field is four sections — grid a → transition e → grid b → boss z.

The layout is always a tree

The generator MUST satisfy these invariants (measured across four fields, 36 rooms; tested in test_field_layout.gd):

Rotation is derived, not stored

The game does not store a room's rotation. A port MUST derive it: rotate the room's door set until it covers the directions the layout needs at that cell — there is exactly one 90° multiple that fits (psz-re "Rooms are rotated, and the rotation is derived"). In this engine the room's authored config portal directions are the door set, and StageRotation.rotate_dir is a logical label remap (never a mesh rotation — see Field Stage Lifecycle and issue #218).

Gates and keys

Each outward edge (away from the start) carries a gate value; the way-back edge is always open:

A field places exactly as many keys as its gates require, on cells reachable before the gate — so a layout is always solvable without backtracking to an unreachable room. Valley: a 2-key gate B1 → C1, with both keys on B2. This balance is a tested invariant.

Enemy population

Spawns are drawn from the measured wave tables, not authored by hand. Each room code has an allowed list of wave templates with selection weights (psz-re enemy_room_assignment); each template is a fixed set of enemy ids (enemy_wave_templates). The start and goal rooms carry no wave and spawn nothing. The exact per-instance roll (which allowed template a room draws) is not yet decoded, so the builder rolls one deterministically against the measured weights — preserving the "mostly ordinary fights, occasional rare" texture rather than making every instance identical.

psz-re internal enemy names map to this engine's ids: snake→garapython, vulture→vulkure, lion→helion, lion_rare→blaze_helion, hyena→grimble, lizard→ghowl, rappy→rappy.

The boss room

The z section is a single arena. Valley's is s01z_na1, and the assignment table's sole s01z entry is boss_dragon — so Valley's boss is Reyburn. See Boss Behavior. Clearing the boss spawns the return-to-city warp (the existing type: "boss" section handling).

What is deliberately approximated