feat(engine): Tiled P1 — rt_tmap model + GID resolver + render + projection (#69)
All checks were successful
bootstrap / cfree-fixpoint (push) Successful in 20s
ci / build-and-test (push) Successful in 1m48s
commit-lint / conventional-commits (push) Successful in 3s
docs / build-and-deploy (push) Successful in 27s

The heart of Tiled support: load a map and draw it.

- rt_tmap model (Tmap/TmLayer/TmTileset in tiled.ludic): map header + ordered
  layers (dense int32 GID arrays, heap-allocated to w*h, lifting the 96x64 cap)
  + tilesets. Built from the intermediate Value tree, so the TMX and TMJ paths
  both feed it.
- GID resolver (Tiled.resolve): gid -> (tileset, localId, flipH/V/D); the three
  flip flags masked off before the local-id lookup, returned alongside. gid==0
  is empty.
- Image-backed render (Tiled.draw): every visible tile layer in file order,
  blitting each tile from its tileset image with flips applied at draw.
- Compatibility projection (Tiled.project / auto on load): a designated
  collision layer projects to the legacy byte tilemap ('#' solid, '=' one-way
  via the oneway property, ' ' empty) so Grid.*/Path.*/esys_move are unchanged.
- Tiled.load resolves external tilesets + images relative to the map file and
  auto-projects a collision/solids/walls layer.
- The grid and physics_tiles demos now run off a loaded map (grid_maze.tmx /
  physics_map.tmx) instead of hand-authored Map.row strings, byte-identically.

Two compiler fixes fell out of this (see the changeset):
- emit_index_addr set g_addr_ty before evaluating the index, so slice[obj.field]
  came back mis-typed; set it last, like the raw-pointer branches.
- @strcmp was declared by both the world table and the fs prelude; centralise
  it in the head prelude so a game that uses Fs/Path links.

Proven by library/tiled_p1.ludic (14 assertions: loads+renders Kenney map
identically from .tmx and .tmj, flip mirroring) + the converted grid/
physics_tiles demos. x test: 92 passed; self-host bootstrap fixpoint intact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-01 14:43:29 +03:00
parent 071c268de7
commit bc301c8d17
29 changed files with 12932 additions and 9582 deletions

View file

@ -0,0 +1,14 @@
---
id: tiled-draw
name: Tiled.draw
category: tiled
kind: namespace-method
tokens: Tiled.draw
sig: Tiled.draw(map, camx: int, camy: int) -> void
tip: Draw every visible tile layer.
order: 10
ns: Tiled
member: draw
---
Draws every visible tile layer in file order to the framebuffer, offset by the camera <code>(camx, camy)</code>, blitting each tile from its tileset image and applying the GID's flip flags at draw time.

View file

@ -0,0 +1,14 @@
---
id: tiled-gid
name: Tiled.gid
category: tiled
kind: namespace-method
tokens: Tiled.gid
sig: Tiled.gid(map, layer: int, x: int, y: int) -> int
tip: The raw GID at a cell in a tile layer.
order: 4
ns: Tiled
member: gid
---
Returns the raw global tile id (with its flip flags still set) at cell <code>(x,y)</code> in tile <code>layer</code>, or <code>0</code> (empty) out of bounds or for a non-tile layer.

View file

@ -0,0 +1,14 @@
---
id: tiled-height
name: Tiled.height
category: tiled
kind: namespace-method
tokens: Tiled.height
sig: Tiled.height(map) -> int
tip: The map height in tiles.
order: 7
ns: Tiled
member: height
---
Returns the map's height in tiles.

View file

@ -0,0 +1,14 @@
---
id: tiled-layer_count
name: Tiled.layer_count
category: tiled
kind: namespace-method
tokens: Tiled.layer_count
sig: Tiled.layer_count(map) -> int
tip: How many layers the map has.
order: 8
ns: Tiled
member: layer_count
---
Returns the number of layers (tile, object, image and group layers are all counted; a group's children are flattened in after it).

View file

@ -0,0 +1,14 @@
---
id: tiled-layer_name
name: Tiled.layer_name
category: tiled
kind: namespace-method
tokens: Tiled.layer_name
sig: Tiled.layer_name(map, index: int) -> string
tip: A layer's name by index.
order: 9
ns: Tiled
member: layer_name
---
Returns the name of the layer at <code>index</code>, or <code>""</code> if the index is out of range.

View file

@ -0,0 +1,14 @@
---
id: tiled-load
name: Tiled.load
category: tiled
kind: namespace-method
tokens: Tiled.load
sig: Tiled.load(path: string) -> map
tip: Load a map file into the runtime map model.
order: 3
ns: Tiled
member: load
---
Reads a map file (TMX or TMJ), builds the runtime <code>rt_tmap</code> model — resolving external tilesets and loading their images relative to the map file — and projects the designated collision layer (a tile layer named <code>collision</code>/<code>solids</code>/<code>walls</code>) down to the legacy byte tilemap so <a href="grid"><code>Grid</code></a>/<a href="path"><code>Path</code></a> and <code>esys_move</code> keep working unchanged. Returns the loaded map.

View file

@ -0,0 +1,14 @@
---
id: tiled-project
name: Tiled.project
category: tiled
kind: namespace-method
tokens: Tiled.project
sig: Tiled.project(map, layer: int) -> void
tip: Project a collision layer to the byte tilemap.
order: 11
ns: Tiled
member: project
---
Designates tile <code>layer</code> as the collision layer and projects it onto the legacy byte tilemap: an empty cell is a space (passable), a GID whose tile carries the bool <code>oneway</code> property is <code>'='</code>, any other non-zero GID is <code>'#'</code> (solid). <a href="tiled-load"><code>Tiled.load</code></a> calls this automatically for a layer named <code>collision</code>/<code>solids</code>/<code>walls</code>.

View file

@ -0,0 +1,14 @@
---
id: tiled-resolve
name: Tiled.resolve
category: tiled
kind: namespace-method
tokens: Tiled.resolve
sig: Tiled.resolve(map, gid: int) -> gidinfo
tip: Decode a GID into tileset, local id and flip flags.
order: 5
ns: Tiled
member: resolve
---
Decomposes a raw GID: strips the three flip flags (<code>0x80000000</code> horizontal, <code>0x40000000</code> vertical, <code>0x20000000</code> anti-diagonal), keeps the low 29 bits as the global tile id, and finds the owning tileset. Returns a value with fields <code>tileset</code> (index, -1 if none), <code>local</code> (id within that tileset), <code>fh</code>/<code>fv</code>/<code>fd</code> (the flip flags) and <code>empty</code> (1 when the GID was 0).

View file

@ -0,0 +1,14 @@
---
id: tiled-tile_prop
name: Tiled.tile_prop
category: tiled
kind: namespace-method
tokens: Tiled.tile_prop
sig: Tiled.tile_prop(map, gid: int, name: string) -> int
tip: Whether a GID's tile carries a bool property.
order: 13
ns: Tiled
member: tile_prop
---
Returns <code>1</code> if the tile the GID resolves to carries a custom bool property <code>name</code> set to true, else <code>0</code> — the property convention (<code>solid</code>/<code>oneway</code>/<code>trigger</code>) the collision projection reads.

View file

@ -0,0 +1,14 @@
---
id: tiled-tree
name: Tiled.tree
category: tiled
kind: namespace-method
tokens: Tiled.tree
sig: Tiled.tree(map) -> value
tip: The underlying intermediate value tree.
order: 12
ns: Tiled
member: tree
---
Returns the intermediate <a href="value"><code>Value</code></a> tree the map was built from — the full Tiled JSON schema (layers, objects, tilesets, custom properties), for queries the typed accessors don't cover.

View file

@ -0,0 +1,14 @@
---
id: tiled-width
name: Tiled.width
category: tiled
kind: namespace-method
tokens: Tiled.width
sig: Tiled.width(map) -> int
tip: The map width in tiles.
order: 6
ns: Tiled
member: width
---
Returns the map's width in tiles.