Files
irrlichtandClaude Opus 5 335962800f Reorganize assets; switch the HUD to the Micro Chat font
Source packs move to assets/packs (untouched), maps to assets/maps, and the
palette/sketch leftovers from the 8-bit era go. tools/mkfont.py repacks
VEXED's Micro Chat sheet (8-column grid, 8x10 cells) into the CP437 atlas
layout the font loader already reads, cropped to 8x9 so the HUD can use an
8 px line height. The CGA atlas is dropped in its favor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 19:47:33 +02:00

82 lines
3.4 KiB
Markdown
Raw Permalink 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.
# 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/maps/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/maps/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>