FireMaze — Prop & Decor Spawner

Automatically place decorative objects on wall faces, in dead-end cells, and at entrance/exit openings. Marked as EXPERIMENTAL — props are spawned at generation time and cleaned up on maze clear or regeneration.

← Back to FireMaze

1. Overview

The prop spawner operates as the final step of maze generation (inside _spawn_decorations). It:

  • Scans every cell in the maze for valid placement locations.
  • Uses per-cell seeded random number generation for deterministic placement.
  • Places copies of the assigned source objects, not linked duplicates.
  • Groups all spawned props under a scoped sub-collection.
  • Supports incremental rebuild during interactive editing — only props on dirty cells are re-evaluated.

2. Prop Collection

All spawned props are organized into a sub-collection:

FireMaze_Props_{parent_collection_name}

For example, a maze in the FireMaze collection gets props in FireMaze_Props_FireMaze.

Lifecycle

  • The props collection is created if it doesn't exist and linked as a child of the main maze collection.
  • Before repopulating, all existing props in the collection with the fire_maze custom property are removed.
  • Each prop object is tagged with:
    • fire_maze (True) — Marks it for scene cleanup (Clear Maze).
    • fire_maze_cell (cell_id) — Links the prop to its parent cell for incremental updates.

Incremental Editing

When dirty_cells are provided (interactive editing), only props whose fire_maze_cell is in the dirty set are removed and re-spawned. Props on clean cells are left untouched.

3. Placement Helper

All props are placed via place_prop(src_obj, pos, rot_z, cell_id):

  1. Copy the source object with src_obj.copy().
  2. Link the copy to the props collection.
  3. Set location to the target world position.
  4. Set rotation — preserves the source's X and Y euler rotation; overrides Z rotation to the calculated facing angle.
  5. Set scale to match the source.
  6. Tag with fire_maze and fire_maze_cell custom properties.

4. Torches

Source: prop_torch_mesh (Object pointer). Density: prop_torch_density (0.0–1.0, default 0.2)

Torches are placed on wall faces that border open space, at 60% of wall height above the floor (z * level_height + 0.6 * wh). A small offset (0.02 × tile_size) moves the torch slightly away from the wall surface.

Rectangular — Thin Wall Mode

Each of the four wall flags (N, S, E, W) is checked independently. A torch is placed on every wall segment that is present, subject to density:

Wall flagPositionRotation (Z)
North (c[0])(x*ts + ts/2, (y+1)*ts - offset, ...)π (facing south)
South (c[1])(x*ts + ts/2, y*ts + offset, ...)0 (facing north)
East (c[2])((x+1)*ts - offset, y*ts + ts/2, ...)π/2 (facing west)
West (c[3])(x*ts + offset, y*ts + ts/2, ...)−π/2 (facing east)

Rectangular — Cube Mode

Wall cubes are checked. A torch is placed on each side of a wall cube that faces an open (non-wall) neighbor cell:

DirectionConditionPositionRotation
Northcells[y+1][x][0] == False0.02 × ts outside north face0 (north)
Southcells[y-1][x][0] == False0.02 × ts outside south faceπ (south)
Eastcells[y][x+1][0] == False0.02 × ts outside east faceπ/2 (east)
Westcells[y][x-1][0] == False0.02 × ts outside west face−π/2 (west)

Polar — Cube Mode

Checks boundary walls between wedge-shaped cells:

BoundaryConditionPlacement
CW radialWall cell has open CW neighborTorch on CW boundary facing CW
CW radial (open)Open cell has wall CW neighborTorch on CW boundary facing CCW/inward
IN angularWall cell has open inward neighborTorch on IN boundary facing IN
IN angular (open)Open cell has wall inward neighborTorch on IN boundary facing OUT

Polar — Thin Wall Mode

Checks the CW and IN wall flags on each cell:

Wall flagPlacement
CW (cells[r][theta][0])Torch on clockwise radial boundary
IN (cells[r][theta][1])Torch on inward angular boundary

Outermost Ring (Polar)

On the outermost ring (r == rings − 1), torches are also placed on the outer boundary of open cells, skipping entrance and exit cells.

5. Chests

Source: prop_chest_mesh (Object pointer). Density: prop_chest_density (0.0–1.0, default 0.5)

Chests are placed in dead-end cells — cells with exactly one open neighbor (one accessible direction). Stair cells, entrances, and exits are excluded.

Dead-End Detection

Rectangular — Thin Wall Mode

A cell is a dead-end when exactly 3 of its 4 wall flags are set (i.e., sum(c[:4]) == 3). The single open direction becomes the chest's orientation.

Rectangular — Cube Mode

An open cell (not a wall) is a dead-end when exactly 1 of its 4 orthogonal neighbors is also open.

Polar

Accessible neighbors are counted by checking:

  • CW/CCW radial boundaries — open if the adjacent cell in the same ring is not a wall (or is a stair cell).
  • IN/OUT angular boundaries — open if the adjacent cell in the neighboring ring is not a wall (or is a stair cell). Overlapping sector cells in the adjacent ring are resolved according to the ring sector ratio.

A cell with exactly 1 accessible neighbor (and r > 0 to exclude the center) is a dead-end.

Chest Orientation

The chest faces toward its single open direction, placed 0.15 × tile_size from the cell center toward the opening:

Open directionPosition offsetRotation (Z)
North+Y from center0
South−Y from centerπ
East+X from centerπ/2
West−X from center−π/2

For polar, directional mapping:

  • IN → positioned toward inner ring, faces IN (rotation + π)
  • OUT → positioned toward outer ring, faces OUT
  • CW → positioned toward CW neighbor, rotation theta_mid − π/2
  • CCW → positioned toward CCW neighbor, rotation theta_mid + π/2

Exclusions

Chests are not placed on:

  • Stair cells (any floor).
  • Entrance cell (floor 0).
  • Exit cells (top floor).

6. Doors

Source: prop_door_mesh (Object pointer). No density control — doors always spawn at valid locations.

Doors are placed at entrance and exit openings on the maze boundary.

Entrance Door (Floor 0)

One door at the entrance cell, positioned at the center of the boundary opening:

Entrance sidePositionRotation (Z)
North(ex*ts + ts/2, (ey+1)*ts, z*level_height)0
South(ex*ts + ts/2, ey*ts, z*level_height)0
East((ex+1)*ts, ey*ts + ts/2, z*level_height)π/2
West(ex*ts, ey*ts + ts/2, z*level_height)−π/2

Exit Doors (Top Floor)

One door at each exit cell, same positioning logic as the entrance door.

For polar mazes, doors are placed at the outer boundary of the entrance/exit cell, oriented toward the cell's mid-angle.

7. Seeded Randomness

Each cell receives a deterministic random number generator to control torch and chest placement:

global_seed = props.seed if props.seed is not None else 0
cell_seed = global_seed + z * 1000000 + y * 1000 + x
cell_rng = random.Random(cell_seed)
  • The same props.seed value always produces identical prop placement.
  • A seed of 0 uses a different random layout each generation.
  • Per-cell seeding ensures stability during incremental edits — cells outside the dirty set retain their existing props.

8. UI Reference

Panel: VIEW3D_PT_fire_maze_props — Prop & Decor Spawner EXPERIMENTAL

ControlPropertyTypeDefault
Torch Objectprop_torch_meshObject pointerNone
Torch Densityprop_torch_densityFloat 0.0–1.00.2
Chest Objectprop_chest_meshObject pointerNone
Chest Densityprop_chest_densityFloat 0.0–1.00.5
Door Objectprop_door_meshObject pointerNone

Density sliders only appear when the corresponding object pointer is assigned.