165 lines
6.3 KiB
Markdown
165 lines
6.3 KiB
Markdown
# 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.rs` and `tests/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)
|
|
1. Fill entire grid with stone
|
|
2. Random fill ~45% as empty (cave candidate)
|
|
3. 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
|
|
4. Flood fill from center, find largest connected region
|
|
5. Seal all cells not in largest region (fill with stone)
|
|
6. Place lava/acid pools in random empty areas
|
|
7. Place stalactites (stone pillars) in random positions
|
|
|
|
### BSP dungeon (depth 7+)
|
|
1. Start with full grid as stone
|
|
2. Recursively split into 2 halves (alternate H/V)
|
|
3. Stop when area < min_room_size (15x10)
|
|
4. In each leaf: place room (smaller than partition, centered)
|
|
5. Connect sibling rooms with L-shaped corridor (2 wide)
|
|
6. Place stairs in the deepest/farthest room
|
|
7. Place items in 2-3 random rooms
|
|
8. 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
|
|
1. Create `worldgen.rs` with `WorldGenerator` struct and `generate()` dispatch
|
|
2. Implement surface generation (depth 1-3) — move existing code, add RNG
|
|
3. Implement CA cave generation (depth 4-6)
|
|
4. Implement BSP dungeon generation (depth 7+)
|
|
5. Integrate into `game.rs::init_world()`
|
|
6. Write tests
|
|
7. Run all tests + scenarios
|
|
8. Push
|