6.3 KiB
6.3 KiB
World Generator Plan
Goal
Procedural world generation with depth-based biomes, rooms, corridors, and randomized features.
Current State
- Implemented in
src/world/worldgen.rs - Depth-based dispatch: surface (1-3), caves (4-6), BSP dungeon (7+)
- Randomized features, pools, trees, walls, rooms, corridors, traps
- World size: 2048x2048 for main game; 250x250 for tests/AI
- Seed-based generation with
Game::seed - Per-chunk world cache in
cache/worlds/<seed>/depth_<N>/ - Tests in
tests/worldgen.rsandtests/large_world.rs(ignored, slow)
Algorithms Researched
BSP (Binary Space Partitioning)
- Recursively divide space into rectangles
- Place room in each leaf node
- Connect siblings with corridors
- Guarantees no overlaps
- Use for: dungeon rooms (depth 7+)
Cellular Automata (4-5 rule)
- Fill grid with ~45% random walls
- 5 iterations: wall if >=4 neighbors are walls, else empty
- Produces organic cave shapes
- Flood fill to verify connectivity
- Use for: caves (depth 4-6, and underground at depth 1-3)
Drunkard's Walk
- Random walk digs tunnels through solid rock
- Creates winding cave-like paths
- Use for: tunnels connecting rooms
Brogue Room Accretion
- Start with one room, attach new rooms to existing structure
- Inherently connected (tree structure)
- Room templates: rectangle, CA blob, circle
- Use for: room placement strategy
Rooms and Mazes (Bob Nystrom)
- Place rooms → fill gaps with maze → connect → remove dead ends
- Inspirational, not directly used
Noita — Herringbone Wang Tiles
- Pre-made chunks laid in herringbone pattern
- Randomized contents within chunks
- Too complex for now, possible future enhancement
Architecture
New module: src/world/worldgen.rs
WorldGenerator
├── rng: &mut CellularAutomaton
├── generate(grid, depth) — main entry point
├── generate_surface(grid, depth) — depth 1-3: terrain + caves + features
├── generate_caves(grid, depth) — depth 4-6: full underground caves
├── generate_dungeon(grid, depth) — depth 7+: BSP rooms + corridors
│
├── Surface sub-methods:
│ ├── terrain_noise(x, depth) — multi-octave surface height
│ ├── fill_terrain(grid, depth) — fill dirt/stone/grass by depth
│ ├── carve_underground_caves(grid) — CA caves below surface
│ ├── place_trees(grid, count) — random tree placement
│ ├── place_pools(grid, count, types)— random liquid pools
│ ├── place_sand_dunes(grid, count) — sand piles
│ └── place_walls(grid, count) — stone wall obstacles
│
├── Cave sub-methods:
│ ├── ca_caves(grid, fill_prob, iterations) — cellular automata
│ ├── flood_fill_largest(grid) — find largest connected region
│ ├── seal_small_regions(grid) — fill disconnected caves
│ └── place_underground_pools(grid) — lava/acid in caves
│
├── Dungeon sub-methods:
│ ├── bsp_split(rect, depth) — recursive space partitioning
│ ├── place_room(grid, rect) — carve room interior
│ ├── connect_rooms(grid, rooms) — L-shaped corridors
│ ├── place_doors(grid, rooms) — door at room entrances
│ └── place_traps(grid, rooms) — acid/fire traps in rooms
│
└── Shared:
├── place_stairs(grid, depth) — stairs in appropriate location
└── place_items(game, rooms) — items in rooms/on surface
Depth-based Generation
| Depth | Type | Surface | Features | Algorithm |
|---|---|---|---|---|
| 1-3 | Surface | Grass/dirt | Trees, water pools, sand dunes, stone walls, underground CA caves | Multi-octave noise + CA |
| 4-6 | Caves | Stone/dirt | Large CA caves, lava pools, acid pools, stalactites | Cellular automata (4-5 rule) |
| 7+ | Dungeon | Stone | BSP rooms (5x3 to 12x8), corridors, traps, stairs | BSP + corridor connection |
Implementation Details
Surface terrain (depth 1-3)
- Multi-octave sine noise:
base + detail + micro - Amplitude: 4-8 cells variation
- Surface material: grass at depth 1, dirt at 2-3
- Below surface: dirt for 8 cells, then stone
- Border: stone walls
CA cave generation (depth 4-6)
- Fill entire grid with stone
- Random fill ~45% as empty (cave candidate)
- Run 5 iterations of 4-5 rule:
- Cell becomes wall if >=4 of 8 neighbors are walls
- Cell becomes empty if <4 neighbors are walls
- Flood fill from center, find largest connected region
- Seal all cells not in largest region (fill with stone)
- Place lava/acid pools in random empty areas
- Place stalactites (stone pillars) in random positions
BSP dungeon (depth 7+)
- Start with full grid as stone
- Recursively split into 2 halves (alternate H/V)
- Stop when area < min_room_size (15x10)
- In each leaf: place room (smaller than partition, centered)
- Connect sibling rooms with L-shaped corridor (2 wide)
- Place stairs in the deepest/farthest room
- Place items in 2-3 random rooms
- Place traps (acid pockets) in 1-2 rooms
Feature placement (all depths)
- All positions via RNG, not hardcoded
- Pool count: 2 + depth/2
- Pool radius: 4-10 cells
- Pool types by depth:
- 1-3: water, sand
- 4-6: lava, acid, water
- 7+: acid (traps in rooms)
- Tree count: 3-7 (surface only)
- Wall count: 1-3 (surface only)
Files
| File | Change |
|---|---|
src/world/worldgen.rs |
New module — all generation logic |
src/world/mod.rs |
Add pub mod worldgen |
src/game.rs |
init_world() calls WorldGenerator::generate() |
tests/worldgen.rs |
New tests |
Tests
- Stairs exist after generation at any depth
- At least 3 distinct materials present
- Player spawn position is not inside solid
- Different depths produce different structures
- Depth 7+ has rooms (empty regions > 5x3)
- CA caves are connected (flood fill test)
Implementation Order
- Create
worldgen.rswithWorldGeneratorstruct andgenerate()dispatch - Implement surface generation (depth 1-3) — move existing code, add RNG
- Implement CA cave generation (depth 4-6)
- Implement BSP dungeon generation (depth 7+)
- Integrate into
game.rs::init_world() - Write tests
- Run all tests + scenarios
- Push