Files
dorf/README.md
T
popertots 1a170ea9a9 docs: update README with collision, tile events, rabbit dig, visibility notes
- Add Stage 2 collision section documenting convoy / head-on / E/S yield / W/N right-of-way
- Add TileChangedEvent + path invalidation section
- Add Rabbit digging to implemented list
- Add visibility raycast note (absent tiles in loaded chunks = air)
- Remove stale warning count from build instructions
2026-03-21 15:23:11 +00:00

125 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Dorf
A Dwarf Fortress-inspired simulation game built in Rust with Bevy. The focus is on a fully data-oriented chunk engine: procedural terrain, hierarchical pathfinding, GPU-baked sprite rendering, and dynamic chunk loading/unloading — all designed to scale to a full DF-style world with z-levels, entities, and simulation.
This is an **engine foundation**, not a complete game. Many features (crafting, needs, combat, world history) exist only as stubs or are entirely absent. The chunk rendering and pathfinding systems are production-quality; the simulation layer above them is early.
## Architecture
```
src/
├── camera.rs # Panning camera, z-level control (Q/E, scroll)
├── config.rs # GameConfig, TileRegistry resources
├── constants.rs # Tile size, pathfinding constants
├── entities/ # Dorfs, pigs, rabbits — ambulatory entities
│ ├── sentient/
│ ├── livestock/
│ └── shared_*/ # Ambulatory, pathfinding, occupancy systems
├── game.rs # ZIndex resource, frame timing
├── main.rs # Bevy app bootstrap
└── world/
├── chunks/ # ChunkMap, chunk loading/unloading, connectivity
├── generation/ # Terrain blobs, forestry, foliage (stub), fauna (stub)
└── tiles/ # TileMap, TileRegistry, rendering, visibility, tile change events
```
### Chunk Architecture
- **World chunks**: 8×8 tiles (`CHUNK_SIZE = 8`, `CHUNK_SIZE_TILE = 128px`)
- **Render chunks**: 32×32 tiles (`CHUNK_TILES = 32`, `512px`). One render chunk = 4×4 world chunks. This mismatch is intentional — a single GPU texture covers multiple world chunks, which means chunk unloading must mark the render chunk dirty (not the world chunk).
- **Z-levels**: 5 below, 15 above, 21 total (`Z_BELOW = 5`, `Z_ABOVE = 15`). Terrain is rendered as quilted sprites — one texture per z-level, all stacked in the shader.
- **Loading**: `setup_initial_chunks` runs at `Startup`, emits `GenerateChunkEvent` for all chunks in `initial_chunk_radius`. `handle_chunk_events` processes events and fires terrain/forestry/foliage/fauna generation events.
- **Unloading**: `unload_chunk(chunk_pos)` is the canonical function. Currently disabled — `dynamic_chunk_unloading_system` is a no-op stub. To enable: implement a system that diffs wanted chunks (player/NPC interest radius) against `ChunkMap.loaded_chunks` and calls `unload_chunk` for each evictable chunk.
### Rendering Pipeline
1. `handle_tile_occlusion_updates` — raycasts from each tile upward to compute `visible_range` bitmask (which z-levels can see it). Tiles in loaded chunks with no floor entry are treated as air (open space).
2. `build_quilted_terrain_sprites` — GPU bake: tiles bucketed by `(render_chunk, z_level)`, blitted into pooled pixel buffers, spawned as `TerrainSprite` entities
3. `update_tile_visibility` — toggles `Visibility` on sprites based on current `ZIndex` (camera z-level)
### Pathfinding
Three tiers, gated by distance:
- **Tier 1** (<4 chunks): synchronous A* on `TileMap`
- **Tier 2** (48 chunks): provisional path + full path via async queue
- **Tier 3** (>8 chunks): hierarchical chunk graph → async segmented A*
**Stage 2 Collision** — when two entities target the same tile, the movement system resolves in four cases:
- **Convoy**: entity ahead moves in ~same direction → follow-through, no avoidance
- **Head-on**: dot product < 0.7 → both yield left (existing sidestep chain)
- **E/S yield**: entity moving east or south → sidestep chain first, excuse-me on failure
- **W/N right-of-way**: entity moving west or north → advance with excuse-me delay
`PathfindingBenchmark` resource tracks sync vs async performance per frame.
**Tile Change Invalidation**`TileChangedEvent` fires whenever a tile's standability changes at runtime. `PathfindingDirtyChunks` collects affected chunk positions; `invalidate_paths_on_tile_change` clears paths for entities stepping through changed chunks within 8 steps.
### Entity Indexing
Static terrain entities (floor tiles, fixtures, trees) are indexed in `chunk_entity_index: HashMap<IVec2, Vec<Entity>>` at spawn time. When a chunk unloads, the index is retrieved and entities are despawned. This is **O(entities in chunk)**, not O(total entities).
Mobile entities (dorfs, pigs, rabbits) are **not** in `chunk_entity_index`. They are spawned once at game start and persist. They will need per-chunk lifecycle management once fauna generation is implemented.
## Controls
| Key | Action |
|-----|--------|
| Click + drag | Pan camera |
| Scroll | Pan camera |
| Q / E | Move camera z-level down / up |
| Mouse wheel | Pan camera |
## Configuration
`config.toml`:
- `initial_chunk_radius`: chunks loaded around origin at startup (default: 7 → 15×15 = 225 chunks)
- `spawn_counts`: dorfs, pigs, rabbits spawned at game start
`tiles.toml`: tile registry — floor tiles, fixture tiles. Each floor tile has `id`, `astar_weight`, `can_stand_in`, `can_stand_on`, `transparent` flags.
## Status
### Implemented
- [x] Procedural terrain (blob-based, simplex noise heightmap)
- [x] Tile registry (`tiles.toml`) with A* weights
- [x] Floor tiles, fixture tiles, item tiles
- [x] Z-levels with quilted GPU sprite rendering
- [x] Tile occlusion / visibility raycasting (treats absent tiles in loaded chunks as air)
- [x] Forestry: tree trunks + leaves spawned per chunk
- [x] Dorfs, pigs, rabbits spawning at startup
- [x] Hierarchical async pathfinding (3 tiers)
- [x] Chunk loading infrastructure
- [x] Chunk unloading infrastructure (`unload_chunk`, `dynamic_chunk_unloading_system` stub)
- [x] Camera panning + z-level control
- [x] GPU texture pooling (pixel buffer reuse across bakes)
- [x] Render chunk dirty-tracking for incremental rebakes
- [x] Stage 2 collision resolution (convoy, E/S yield, W/N right-of-way)
- [x] TileChangedEvent + path invalidation on tile change
- [x] Rabbit digging (debug) — removes floor tile, replaces with air, fires occlusion + path invalidation
### Stubs (exist as empty functions, wired into schedules)
- [ ] Foliage generation per chunk (`generate_chunk_foliage`)
- [ ] Fauna spawning per chunk (`generate_chunk_fauna`)
- [ ] Weathering and precipitation (`generate_chunk_weathering_and_precipitation`)
### Not Started
- [ ] Crafting, stockpiles, resource gathering
- [ ] Needs, mood, stress systems
- [ ] Job assignment and task automation
- [ ] Building and construction (beyond debug rabbit digging)
- [ ] Combat and squad management
- [ ] Item persistence and inventory
- [ ] Dynamic chunk loading (player/NPC-centered, not just radius-at-startup)
- [ ] Save/load world state
- [ ] World history and procedural storytelling
- [ ] UI (menus, z-level overlay, workshop assignment)
## Building
```sh
cargo build # dev profile
cargo run # dev mode
```
`[profile.dev.package."*"] opt-level = 3` gives near-release perf in dev builds.