The 64-entry per-chunk palette was a wire-format artifact and capped a chunk at 64 distinct tiles. A Chunk is now Box<[u16; 1024]> with flags derived from the id on lookup — the same layout a binary on-disk format would use. Map path becomes the first CLI argument; assets/map_large is a generated 256×256 stress map (loads in ~0.5 ms release). The snapshot test takes SNAPSHOT_MAP. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
82 lines
3.4 KiB
Markdown
82 lines
3.4 KiB
Markdown
# Map Loader
|
||
|
||
Loads level data from a Tiled CSV export, replacing the old hardcoded `build_world` wall loop.
|
||
Implemented in `sim/src/map.rs`; wired into the world in `sim/src/lib.rs::load_world`.
|
||
|
||
---
|
||
|
||
## Format
|
||
|
||
A bare comma-separated tile grid — Tiled's "CSV" layer export, stripped of the surrounding
|
||
TMX/XML. No header, no dimensions. The loader derives them from the content:
|
||
|
||
- **width** = number of fields in the first row
|
||
- **height** = number of rows
|
||
|
||
The current test map lives at `assets/map_test` (30×30, tile ids 25 / 74 / 75 / 146).
|
||
A trailing comma per row is tolerated; ragged rows panic (they indicate a corrupt export).
|
||
|
||
---
|
||
|
||
## Tile flip flags — the "negative numbers"
|
||
|
||
Tiled packs per-tile flip/rotation into the **top 3 bits** of each 32-bit GID. A flipped tile
|
||
therefore shows up in the CSV as a large *negative* decimal when read as `i32`. Example:
|
||
|
||
```
|
||
-2147483574 as u32 = 0x8000004A
|
||
│ └── 0x4A = 74 ← the real tile id
|
||
└────────── 0x80000000 = horizontal flip
|
||
```
|
||
|
||
| Bit | Mask | Meaning |
|
||
|-----|--------------|---------------------|
|
||
| 31 | `0x80000000` | horizontal flip |
|
||
| 30 | `0x40000000` | vertical flip |
|
||
| 29 | `0x20000000` | diagonal flip (rot) |
|
||
|
||
The loader parses each field as `i64`, reinterprets the low 32 bits as `u32`, and strips the
|
||
flip flags with `& 0x1FFF_FFFF`, leaving the bare tile id. **Flip orientation is discarded for
|
||
now** — flipped tiles render unflipped. Real flipping would need a flip-aware `blit_tile` plus
|
||
a flip flag next to the tile id in the chunk; deferred to the camera/sprite pass (09).
|
||
|
||
---
|
||
|
||
## Tile id ↔ tileset
|
||
|
||
Tile ids are used directly:
|
||
|
||
- **Sim** — `tile_flags(id)` (in `map.rs`) maps an id to gameplay `TileFlags`
|
||
(collidable / opaque) via the `sim::tile_collidable` vocabulary. Hand-maintained; test values only.
|
||
- **Renderer** — the id indexes straight into `overworld.png` (`game.rs`). The tileset's tile
|
||
order *is* the id space; keep the Tiled tileset and `overworld.png` in lockstep.
|
||
|
||
No `firstgid` subtraction: the authored ids already line up with `overworld.png`.
|
||
|
||
---
|
||
|
||
## World assembly
|
||
|
||
`load_world` slices the map into 32×32 chunks via `Chunk::generate`. A chunk is a plain
|
||
`[u16; 1024]` of tile ids; flags are derived from the id on lookup (`map::tile_flags`). That
|
||
array is also the natural on-disk layout should chunks ever be streamed from disk (RLE over
|
||
the `u16`s would do) — the CSV stays the interchange format for external tooling. The map's
|
||
top-left tile sits at world (0, 0). Loading a 256×256 map takes well under a millisecond in
|
||
release; the map path is the game's first CLI argument (`game assets/map_large`).
|
||
|
||
**Edge border:** a 30×30 map only fills part of chunk (0,0) (which spans tiles 0–31). Tiles
|
||
inside a loaded chunk but outside the authored map are emitted as an *invisible solid border*
|
||
(id 0, `COLLIDABLE`) so the walkable edge sits flush with the visible map rim rather than two
|
||
tiles past it at the chunk boundary. Beyond any loaded chunk, movement is blocked anyway by
|
||
`sim.rs`'s `map_or(true)` (no chunk = world edge).
|
||
|
||
---
|
||
|
||
## Not done yet
|
||
|
||
- Multiple layers (only a single tile layer is read)
|
||
- Tile properties from Tiled (collision comes from the hand-maintained `tile_flags`, not the file)
|
||
- Object layers (spawns, triggers)
|
||
- Tile flipping (orientation discarded — see above)
|
||
</content>
|