CSV-Map loading
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
# Map Loader
|
||||
|
||||
Loads level data from a Tiled CSV export, replacing the old hardcoded `build_world` wall loop.
|
||||
Implemented in `server/src/map.rs`; wired into the world in `server/src/main.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` on
|
||||
the client plus the flags carried through the protocol; deferred to the camera/sprite pass (09).
|
||||
|
||||
---
|
||||
|
||||
## Tile id ↔ tileset
|
||||
|
||||
Tile ids are used directly:
|
||||
|
||||
- **Server** — `tile_flags(id)` (in `map.rs`) maps an id to gameplay `TileFlags`
|
||||
(collidable / opaque). Hand-maintained vocabulary; currently everything is walkable floor.
|
||||
- **Client** — the id indexes straight into `overworld.tga` (`game.rs`). The tileset's tile
|
||||
order *is* the id space; keep the Tiled tileset and `overworld.tga` in lockstep.
|
||||
|
||||
No `firstgid` subtraction: the authored ids already line up with `overworld.tga`.
|
||||
|
||||
---
|
||||
|
||||
## World assembly
|
||||
|
||||
`load_world` slices the map into 32×32 chunks via `Chunk::generate`, which builds each chunk's
|
||||
6-bit palette automatically. The map's top-left tile sits at world (0, 0).
|
||||
|
||||
**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>
|
||||
Reference in New Issue
Block a user