115 lines
4.2 KiB
Markdown
115 lines
4.2 KiB
Markdown
# World
|
||
|
||
Source: `src/dusk/rpg/overworld/`
|
||
|
||
---
|
||
|
||
## Coordinate system
|
||
|
||
Three nested coordinate spaces, each defined in `worldpos.h`:
|
||
|
||
| Type | Description | Unit |
|
||
|---|---|---|
|
||
| `worldpos_t` | Tile-level absolute position `{x, y, z}` | `worldunit_t` (int16) |
|
||
| `chunkpos_t` | Chunk-grid position `{x, y, z}` | `chunkunit_t` (int16) |
|
||
| `fixed_t[3]` | Smooth sub-tile position used by entities | Q24.8 fixed-point |
|
||
|
||
One chunk = `CHUNK_WIDTH × CHUNK_HEIGHT × CHUNK_DEPTH` tiles (16 × 16 × 8).
|
||
The loaded world window = `MAP_CHUNK_WIDTH × MAP_CHUNK_HEIGHT × MAP_CHUNK_DEPTH` chunks (5 × 5 × 3).
|
||
|
||
Conversion helpers (all in `worldpos.c`):
|
||
|
||
```c
|
||
worldPosToChunkPos(&worldPos, &chunkPos); // tile → chunk grid
|
||
chunkPosToWorldPos(&chunkPos, &worldPos); // chunk grid → tile origin
|
||
worldPosToChunkTileIndex(&worldPos); // tile → index within its chunk
|
||
chunkPosToIndex(&chunkPos); // chunk grid → linear index in MAP.chunks[]
|
||
worldPosToFixed(&worldPos, fixedOut); // tile → entity fixed position
|
||
fixedToWorldPos(fixedPos); // entity fixed → tile (truncates frac)
|
||
```
|
||
|
||
---
|
||
|
||
## Tiles
|
||
|
||
Defined in `tile.h` as a plain `tile_t` enum:
|
||
|
||
```
|
||
TILE_SHAPE_NULL — empty / unloaded
|
||
TILE_SHAPE_GROUND — solid flat tile
|
||
TILE_SHAPE_RAMP_* — directional ramps (N/S/E/W + diagonals NE/NW/SE/SW)
|
||
```
|
||
|
||
Key predicates:
|
||
- `tileIsWalkable(tile)` — true for GROUND and all ramp shapes.
|
||
- `tileIsRamp(tile)` — true only for ramp shapes.
|
||
|
||
Entity walk code (`entity.c`) checks both the current tile and the target tile to decide whether the entity steps forward flat, raises one Z level (walking up a ramp), or falls one Z level (stepping onto a downward ramp from above).
|
||
|
||
---
|
||
|
||
## Chunks
|
||
|
||
`chunk_t` (`chunk.h`) holds:
|
||
- `position` — its `chunkpos_t` in the world grid
|
||
- `tiles[CHUNK_TILE_COUNT]` — flat array of `tile_t`, indexed by `chunkGetTileIndex()`
|
||
- `vertices[CHUNK_VERTEX_COUNT]` / `mesh` — pre-baked mesh uploaded to GPU on load
|
||
- `entities[CHUNK_ENTITY_COUNT_MAX]` — indices into `ENTITIES[]` currently in this chunk (sentinel `0xFF`)
|
||
- `testColor` — temporary debug color (checkerboard), will be replaced by real tileset data
|
||
|
||
Tile layout within a chunk is `z * W*H + y * W + x` (Z-major, row-major in XY).
|
||
|
||
---
|
||
|
||
## Map
|
||
|
||
`map_t MAP` (`map.h`) is the single global map instance.
|
||
|
||
```c
|
||
chunk_t chunks[MAP_CHUNK_COUNT]; // flat storage — index is NOT world position
|
||
chunk_t *chunkOrder[MAP_CHUNK_COUNT]; // draw-order sorted pointers into chunks[]
|
||
chunkpos_t chunkPosition; // world-grid origin of the loaded window
|
||
bool_t loaded;
|
||
```
|
||
|
||
### Load / unload
|
||
|
||
`mapInit()` allocates chunk meshes and performs the initial load of all chunks in the starting window.
|
||
|
||
`mapPositionSet(newPos)` shifts the window:
|
||
1. Determines which of the `MAP_CHUNK_COUNT` slots remain within the new window vs. fall outside it.
|
||
2. Calls `mapChunkUnload()` on every chunk that falls outside (nulls its entity slots, zeroes `vertCount`).
|
||
3. Reuses freed slots for newly-in-range chunks; calls `mapChunkLoad()` on each.
|
||
4. Rebuilds `chunkOrder[]` in XYZ order for the new position.
|
||
|
||
### Chunk load (current stub)
|
||
|
||
`mapChunkLoad()` currently:
|
||
- Fills all tiles with `TILE_SHAPE_GROUND`
|
||
- Assigns a checkerboard debug color based on chunk XY parity
|
||
- Bakes a flat sprite-batch quad mesh for the z=0 layer and uploads it via `meshFlush()`
|
||
- Skips mesh generation for z > 0 chunks (they're empty)
|
||
|
||
### Tile lookup
|
||
|
||
```c
|
||
tile_t mapGetTile(const worldpos_t position);
|
||
```
|
||
|
||
Converts `position` to its chunk, looks up the chunk in `chunkOrder`, then indexes into `chunk->tiles[]`. Returns `TILE_SHAPE_NULL` for any out-of-bounds position or when the map is not loaded.
|
||
|
||
---
|
||
|
||
## Camera
|
||
|
||
`rpgcamera_t RPG_CAMERA` (`rpgcamera.h`) has two modes:
|
||
|
||
```c
|
||
RPG_CAMERA_MODE_FREE // free worldpos; camera.free holds the position
|
||
RPG_CAMERA_MODE_FOLLOW_ENTITY // tracks ENTITIES[followEntityId]
|
||
```
|
||
|
||
`rpgCameraGetPosition()` returns the active world tile position in either mode.
|
||
|
||
The scene renderer (`sceneoverworld.c`) uses `rpgCameraGetPosition()` to build the `glm_lookat` view matrix. When following an entity, it sub-tile interpolates between `entity->lastPosition` and `entity->position` using `entity->animTime / ENTITY_ANIM_WALK_DURATION` to smooth movement.
|