feat(engine): Tiled P1 — rt_tmap model + GID resolver + render + projection (#69)
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:
parent
071c268de7
commit
bc301c8d17
29 changed files with 12932 additions and 9582 deletions
1981
assets/kenney/tiny-dungeon/Tiled/sampleMap.tmj
Normal file
1981
assets/kenney/tiny-dungeon/Tiled/sampleMap.tmj
Normal file
File diff suppressed because it is too large
Load diff
9
assets/tiled-fixtures/collision.tsx
Normal file
9
assets/tiled-fixtures/collision.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Hand-authored collision tileset for the Ludic Tiled demos (issue #69). CC0. -->
|
||||||
|
<tileset version="1.10" tiledversion="1.10.2" name="collision" tilewidth="16" tileheight="16" tilecount="2" columns="1">
|
||||||
|
<tile id="1">
|
||||||
|
<properties>
|
||||||
|
<property name="oneway" type="bool" value="true"/>
|
||||||
|
</properties>
|
||||||
|
</tile>
|
||||||
|
</tileset>
|
||||||
14
assets/tiled-fixtures/grid_maze.tmx
Normal file
14
assets/tiled-fixtures/grid_maze.tmx
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Hand-authored map for the Ludic Tiled demos (issue #69). CC0. -->
|
||||||
|
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="8" height="5" tilewidth="16" tileheight="16" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<tileset firstgid="1" source="collision.tsx"/>
|
||||||
|
<layer id="1" name="collision" width="8" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
1,1,1,1,1,1,1,1,
|
||||||
|
1,0,0,0,0,0,0,1,
|
||||||
|
1,0,1,1,1,1,0,1,
|
||||||
|
1,0,0,0,0,1,0,1,
|
||||||
|
1,1,1,1,1,1,1,1
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
||||||
19
assets/tiled-fixtures/physics_map.tmx
Normal file
19
assets/tiled-fixtures/physics_map.tmx
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Hand-authored map for the Ludic Tiled demos (issue #69). CC0. -->
|
||||||
|
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="16" height="10" tilewidth="16" tileheight="16" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<tileset firstgid="1" source="collision.tsx"/>
|
||||||
|
<layer id="1" name="collision" width="16" height="10">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,2,2,2,0,0,0,
|
||||||
|
0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
||||||
4
changes/index-addr-member-index.md
Normal file
4
changes/index-addr-member-index.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
bump: patch
|
||||||
|
type: fix
|
||||||
|
|
||||||
|
**Correct the element type of a slice indexed by a member-access expression.** `emit_index_addr` set the global `g_addr_ty` to the slice's element type *before* evaluating the index expression, so an index that was itself a struct-field access (`slice[obj.field]`) overwrote it — the load then came back typed as the field, and a following field access failed with "member access on non-aggregate". The slice branch now sets `g_addr_ty` last, matching the raw-pointer branches. Also de-duplicate the `@strcmp` declaration (centralised in the head prelude) so a program that pulls in both the world table and the filesystem prelude links.
|
||||||
|
|
@ -5,3 +5,4 @@ type: feat
|
||||||
|
|
||||||
- **P0 parsing primitives (#67)** — a minimal pure-Ludic XML reader (`Xml.*`) for the element/attribute/CDATA subset TMX/TSX/TX use; standard base64 decode/encode (`Base64.*`, RFC 4648), whose decoder ignores the whitespace Tiled wraps into `<data>`; and gzip framing (`z_gunzip`, RFC 1952) wrapping the existing DEFLATE inflater. zlib and the JSON reader already shipped. A curated, attributed golden corpus lands under `assets/tiled-fixtures/`.
|
- **P0 parsing primitives (#67)** — a minimal pure-Ludic XML reader (`Xml.*`) for the element/attribute/CDATA subset TMX/TSX/TX use; standard base64 decode/encode (`Base64.*`, RFC 4648), whose decoder ignores the whitespace Tiled wraps into `<data>`; and gzip framing (`z_gunzip`, RFC 1952) wrapping the existing DEFLATE inflater. zlib and the JSON reader already shipped. A curated, attributed golden corpus lands under `assets/tiled-fixtures/`.
|
||||||
- **P0.5 TMX/TSX reader (#68)** — `Tiled.read` / `Tiled.read_tsx` map the native XML formats onto the *same* intermediate the JSON path produces — a `Value` tree in Tiled's JSON schema, with every tile layer's data decoded to a dense GID list (CSV, base64, base64+zlib, base64+gzip). A CSV `.tmx` and a base64+zlib `.tmj` of the same map read structurally identically; the in-repo Kenney `sampleMap.tmx` + external `sampleSheet.tsx` load with no manual JSON re-export.
|
- **P0.5 TMX/TSX reader (#68)** — `Tiled.read` / `Tiled.read_tsx` map the native XML formats onto the *same* intermediate the JSON path produces — a `Value` tree in Tiled's JSON schema, with every tile layer's data decoded to a dense GID list (CSV, base64, base64+zlib, base64+gzip). A CSV `.tmx` and a base64+zlib `.tmj` of the same map read structurally identically; the in-repo Kenney `sampleMap.tmx` + external `sampleSheet.tsx` load with no manual JSON re-export.
|
||||||
|
- **P1 core load + render (#69)** — the runtime `rt_tmap` model (heap-allocated to `w·h`, lifting the old `96×64` cap), the GID resolver (`Tiled.resolve` → tileset / local id / H·V·D flips), image-backed rendering (`Tiled.draw`, flips applied at blit), and the legacy-tilemap compatibility projection so `Grid.*`/`Path.*`/`esys_move` keep working. `Tiled.load` reads either format, resolves external tilesets + images, and auto-projects a `collision` layer. The Kenney sample loads and renders pixel-identically from `.tmx` and `.tmj`; the `grid` and `physics_tiles` demos now run off a loaded map.
|
||||||
|
|
|
||||||
14
docs/language/tiled/tiled-draw.md
Normal file
14
docs/language/tiled/tiled-draw.md
Normal 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.
|
||||||
14
docs/language/tiled/tiled-gid.md
Normal file
14
docs/language/tiled/tiled-gid.md
Normal 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.
|
||||||
14
docs/language/tiled/tiled-height.md
Normal file
14
docs/language/tiled/tiled-height.md
Normal 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.
|
||||||
14
docs/language/tiled/tiled-layer_count.md
Normal file
14
docs/language/tiled/tiled-layer_count.md
Normal 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).
|
||||||
14
docs/language/tiled/tiled-layer_name.md
Normal file
14
docs/language/tiled/tiled-layer_name.md
Normal 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.
|
||||||
14
docs/language/tiled/tiled-load.md
Normal file
14
docs/language/tiled/tiled-load.md
Normal 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.
|
||||||
14
docs/language/tiled/tiled-project.md
Normal file
14
docs/language/tiled/tiled-project.md
Normal 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>.
|
||||||
14
docs/language/tiled/tiled-resolve.md
Normal file
14
docs/language/tiled/tiled-resolve.md
Normal 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).
|
||||||
14
docs/language/tiled/tiled-tile_prop.md
Normal file
14
docs/language/tiled/tiled-tile_prop.md
Normal 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.
|
||||||
14
docs/language/tiled/tiled-tree.md
Normal file
14
docs/language/tiled/tiled-tree.md
Normal 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.
|
||||||
14
docs/language/tiled/tiled-width.md
Normal file
14
docs/language/tiled/tiled-width.md
Normal 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.
|
||||||
|
|
@ -7,13 +7,16 @@ program Grid {
|
||||||
model Marker { Tag }
|
model Marker { Tag }
|
||||||
|
|
||||||
handler Boot phase Start {
|
handler Boot phase Start {
|
||||||
|
# The maze is now a loaded Tiled map (issue #69): its `collision` layer —
|
||||||
|
# 1 = wall, 0 = open — projects to the same byte tilemap Map.row used to fill,
|
||||||
|
# so every Grid.* / Path.* query below is byte-identical.
|
||||||
# 01234567
|
# 01234567
|
||||||
Map.size(8, 5)
|
# row 0 ########
|
||||||
Map.row(0, "########")
|
# row 1 #......#
|
||||||
Map.row(1, "#......#")
|
# row 2 #.####.#
|
||||||
Map.row(2, "#.####.#")
|
# row 3 #....#.#
|
||||||
Map.row(3, "#....#.#")
|
# row 4 ########
|
||||||
Map.row(4, "########")
|
Tiled.load("assets/tiled-fixtures/grid_maze.tmx")
|
||||||
|
|
||||||
# --- Bresenham line ---
|
# --- Bresenham line ---
|
||||||
let hl = Grid.line(1, 1, 6, 1)
|
let hl = Grid.line(1, 1, 6, 1)
|
||||||
|
|
|
||||||
|
|
@ -35,13 +35,13 @@ program PhysicsTiles {
|
||||||
let f_grd = World.field_id(pb, "on_ground")
|
let f_grd = World.field_id(pb, "on_ground")
|
||||||
let f_hw = World.field_id(pb, "hit_wall")
|
let f_hw = World.field_id(pb, "hit_wall")
|
||||||
|
|
||||||
# 16px tiles; '#' (35) is solid, '=' (61) is a one-way platform.
|
# 16px tiles; the map is now a loaded Tiled map (issue #69). Its `collision`
|
||||||
Map.size(16, 10)
|
# layer projects to the same byte tilemap: a non-zero GID becomes '#' (35,
|
||||||
Map.row(4, " === ") # one-way platform, cols 10-12 (y 64)
|
# solid), and a GID whose tile carries the bool `oneway` property becomes
|
||||||
Map.row(5, " # ") # wall column, col 6, rows 5-7
|
# '=' (61), a one-way platform — so esys_move sees the identical grid.
|
||||||
Map.row(6, " # ")
|
# one-way platform, cols 10-12 (y 64); wall column, col 6, rows 5-7;
|
||||||
Map.row(7, " # ")
|
# solid floor, row 8 (top edge y 128).
|
||||||
Map.row(8, "################") # solid floor, row 8 (top edge y 128)
|
Tiled.load("assets/tiled-fixtures/physics_map.tmx")
|
||||||
spawn Config { Solids { tile: 16, wall: 35, oneway: 61 } }
|
spawn Config { Solids { tile: 16, wall: 35, oneway: 61 } }
|
||||||
|
|
||||||
# ---- T1. fall and land on the tile floor -------------------------------
|
# ---- T1. fall and land on the tile floor -------------------------------
|
||||||
|
|
|
||||||
100
examples/library/tiled_p1.ludic
Normal file
100
examples/library/tiled_p1.ludic
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
# tiled_p1.ludic — the Tiled P1 core (issue #69): the rt_tmap model, the GID
|
||||||
|
# resolver (flip decode), image-backed render, and load-and-draw over the in-repo
|
||||||
|
# Kenney sampleMap. Proves the map loads and renders identically from both the
|
||||||
|
# `.tmx` and its `.tmj` export, and that flipped tiles draw mirrored.
|
||||||
|
#
|
||||||
|
# A game so the runtime (framebuffer + tilemap) is initialised; the Start handler
|
||||||
|
# runs the checks and Update quits. A full run prints:
|
||||||
|
# 1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
||||||
|
program TiledP1 {
|
||||||
|
property Tag { v: int = 0 }
|
||||||
|
model Marker { Tag }
|
||||||
|
|
||||||
|
# a rolling hash of the whole framebuffer (deterministic; equal iff identical).
|
||||||
|
function fb_hash() -> int {
|
||||||
|
var h = 0
|
||||||
|
var y = 0
|
||||||
|
while y < 240 {
|
||||||
|
var x = 0
|
||||||
|
while x < 320 {
|
||||||
|
h = h * 31 + rt_get_px(x, y)
|
||||||
|
x = x + 4
|
||||||
|
}
|
||||||
|
y = y + 4
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
handler Boot phase Start {
|
||||||
|
let m = Tiled.load("assets/kenney/tiny-dungeon/Tiled/sampleMap.tmx")
|
||||||
|
if Tiled.width(m) == 32 { print(1) }
|
||||||
|
if Tiled.height(m) == 20 { print(2) }
|
||||||
|
if Tiled.gid(m, 0, 0, 0) == 14 { print(3) }
|
||||||
|
|
||||||
|
# resolve a flip-flagged GID: 1610612787 = 0x60000033 -> V+D, global tile
|
||||||
|
# 0x33 = 51; firstgid 1 makes the tileset-local id 50; tileset 0
|
||||||
|
let r = Tiled.resolve(m, 1610612787)
|
||||||
|
if r.local == 50 { print(4) }
|
||||||
|
if r.fv == 1 { print(5) }
|
||||||
|
if r.fd == 1 { print(6) }
|
||||||
|
if r.fh == 0 { print(7) }
|
||||||
|
if r.tileset == 0 { print(8) }
|
||||||
|
let e = Tiled.resolve(m, 0)
|
||||||
|
if e.empty == 1 { print(9) }
|
||||||
|
|
||||||
|
# render the .tmx and hash the framebuffer
|
||||||
|
rt_clear(0)
|
||||||
|
Tiled.draw(m, 0, 0)
|
||||||
|
let hx = fb_hash()
|
||||||
|
if hx != 0 { print(10) } # something was drawn
|
||||||
|
|
||||||
|
# render the .tmj of the same map — must be pixel-identical
|
||||||
|
let j = Tiled.load("assets/kenney/tiny-dungeon/Tiled/sampleMap.tmj")
|
||||||
|
rt_clear(0)
|
||||||
|
Tiled.draw(j, 0, 0)
|
||||||
|
if fb_hash() == hx { print(11) }
|
||||||
|
|
||||||
|
# --- flips draw with the correct orientation ---
|
||||||
|
# find a horizontally-asymmetric tile so the mirror test is meaningful
|
||||||
|
var chosen = 0 - 1
|
||||||
|
var t = 0
|
||||||
|
while t < 132 {
|
||||||
|
rt_clear(0)
|
||||||
|
tmap_draw_gid(m, 1 + t, 0, 0)
|
||||||
|
var asym = 0
|
||||||
|
var yy = 0
|
||||||
|
while yy < 16 {
|
||||||
|
var xx = 0
|
||||||
|
while xx < 8 {
|
||||||
|
if rt_get_px(xx, yy) != rt_get_px(15 - xx, yy) { asym = 1 }
|
||||||
|
xx = xx + 1
|
||||||
|
}
|
||||||
|
yy = yy + 1
|
||||||
|
}
|
||||||
|
if asym == 1 and chosen < 0 { chosen = t }
|
||||||
|
t = t + 1
|
||||||
|
}
|
||||||
|
if chosen >= 0 { print(12) }
|
||||||
|
|
||||||
|
# draw the chosen tile normal at x=0 and H-flipped at x=16
|
||||||
|
rt_clear(0)
|
||||||
|
tmap_draw_gid(m, 1 + chosen, 0, 0)
|
||||||
|
tmap_draw_gid(m, (1 + chosen) | (1 << 31), 16, 0)
|
||||||
|
var mirror = 1
|
||||||
|
var diff = 0
|
||||||
|
var yy2 = 0
|
||||||
|
while yy2 < 16 {
|
||||||
|
var xx2 = 0
|
||||||
|
while xx2 < 16 {
|
||||||
|
if rt_get_px(xx2, yy2) != rt_get_px(31 - xx2, yy2) { mirror = 0 } # H-flip mirrors
|
||||||
|
if rt_get_px(xx2, yy2) != rt_get_px(15 - xx2, yy2) { diff = 1 } # the tile isn't symmetric
|
||||||
|
xx2 = xx2 + 1
|
||||||
|
}
|
||||||
|
yy2 = yy2 + 1
|
||||||
|
}
|
||||||
|
if mirror == 1 { print(13) }
|
||||||
|
if diff == 1 { print(14) }
|
||||||
|
}
|
||||||
|
|
||||||
|
handler Run phase Update { quit() }
|
||||||
|
}
|
||||||
|
|
@ -442,3 +442,349 @@ function tiled_read_tsx(path: pointer) -> Val {
|
||||||
if tiled_first_byte(text) == 60 { return tsx_to_value(xml_parse(text)) }
|
if tiled_first_byte(text) == 60 { return tsx_to_value(xml_parse(text)) }
|
||||||
return json_parse(text)
|
return json_parse(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# P1 (#69) — the runtime map model (`rt_tmap`), the GID resolver, the legacy
|
||||||
|
# `rt_map` compatibility projection, and image-backed rendering. Built from the
|
||||||
|
# intermediate Value tree above, so both the TMX and TMJ paths feed it.
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
property TmTileset {
|
||||||
|
firstgid: int = 0
|
||||||
|
columns: int = 0
|
||||||
|
tilew: int = 0
|
||||||
|
tileh: int = 0
|
||||||
|
spacing: int = 0
|
||||||
|
margin: int = 0
|
||||||
|
tilecount: int = 0
|
||||||
|
imgid: int = 0 - 1 # loaded image handle (rt_image_load), -1 = none
|
||||||
|
meta: Val # the tileset Value object (per-tile metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
property TmLayer {
|
||||||
|
kind: int = 0 # 0 tilelayer, 1 objectgroup, 2 imagelayer, 3 group
|
||||||
|
name: pointer = null
|
||||||
|
w: int = 0
|
||||||
|
h: int = 0
|
||||||
|
visible: int = 1
|
||||||
|
gids: []int # dense w*h GID array (tilelayer only)
|
||||||
|
data: Val # the layer Value object (objects, image, …)
|
||||||
|
}
|
||||||
|
|
||||||
|
property Tmap {
|
||||||
|
w: int = 0 # map width in tiles
|
||||||
|
h: int = 0
|
||||||
|
tilew: int = 0 # map tile size in pixels
|
||||||
|
tileh: int = 0
|
||||||
|
orientation: pointer = null
|
||||||
|
layers: []TmLayer
|
||||||
|
tilesets: []TmTileset
|
||||||
|
tree: Val # the source intermediate tree
|
||||||
|
coll: int = 0 - 1 # designated collision layer index, or -1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- GID resolver ----------------------------------------------------------
|
||||||
|
property GidInfo {
|
||||||
|
tileset: int = 0 - 1 # index into Tmap.tilesets (-1 = none/empty)
|
||||||
|
local: int = 0 # local tile id within that tileset
|
||||||
|
fh: int = 0 # horizontal flip
|
||||||
|
fv: int = 0 # vertical flip
|
||||||
|
fd: int = 0 # anti-diagonal flip
|
||||||
|
empty: int = 0 # gid was 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# decompose a raw GID: strip the three flip flags (0x80000000 H, 0x40000000 V,
|
||||||
|
# 0x20000000 D), keep the low 29 bits as the global tile id, and find the tileset
|
||||||
|
# whose firstgid is the greatest not exceeding it. gid == 0 is the empty cell.
|
||||||
|
function tmap_resolve(m: Tmap, gid: int) -> GidInfo {
|
||||||
|
let r = new GidInfo
|
||||||
|
if gid == 0 { r.empty = 1; r.tileset = 0 - 1; return r }
|
||||||
|
let H = 1 << 31
|
||||||
|
let V = 1 << 30
|
||||||
|
let D = 1 << 29
|
||||||
|
if (gid & H) != 0 { r.fh = 1 }
|
||||||
|
if (gid & V) != 0 { r.fv = 1 }
|
||||||
|
if (gid & D) != 0 { r.fd = 1 }
|
||||||
|
let id = gid & 536870911 # low 29 bits = global tile id
|
||||||
|
var best = 0 - 1
|
||||||
|
var bestfg = 0
|
||||||
|
var i = 0
|
||||||
|
while i < len(m.tilesets) {
|
||||||
|
let fg = m.tilesets[i].firstgid
|
||||||
|
if fg <= id and fg >= bestfg { bestfg = fg; best = i }
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
r.tileset = best
|
||||||
|
if best >= 0 { r.local = id - m.tilesets[best].firstgid }
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- accessors -------------------------------------------------------------
|
||||||
|
function tmap_width(m: Tmap) -> int { return m.w }
|
||||||
|
function tmap_height(m: Tmap) -> int { return m.h }
|
||||||
|
function tmap_layer_count(m: Tmap) -> int { return len(m.layers) }
|
||||||
|
function tmap_layer_name(m: Tmap, i: int) -> pointer {
|
||||||
|
if i < 0 or i >= len(m.layers) { return "" }
|
||||||
|
if m.layers[i].name == null { return "" }
|
||||||
|
return m.layers[i].name
|
||||||
|
}
|
||||||
|
function tmap_tree(m: Tmap) -> Val { return m.tree }
|
||||||
|
|
||||||
|
# raw GID at (x,y) in tile layer `layer` (0 out of bounds / non-tile layer).
|
||||||
|
function tmap_gid(m: Tmap, layer: int, x: int, y: int) -> int {
|
||||||
|
if layer < 0 or layer >= len(m.layers) { return 0 }
|
||||||
|
let l = m.layers[layer]
|
||||||
|
if l.kind != 0 { return 0 }
|
||||||
|
if x < 0 or y < 0 or x >= l.w or y >= l.h { return 0 }
|
||||||
|
return l.gids[y * l.w + x]
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- per-tile metadata (property convention) -------------------------------
|
||||||
|
# does the tile a GID resolves to carry bool custom property `name` = true?
|
||||||
|
function tmap_tile_prop(m: Tmap, gid: int, name: pointer) -> int {
|
||||||
|
let r = tmap_resolve(m, gid)
|
||||||
|
if r.tileset < 0 { return 0 }
|
||||||
|
let ts = m.tilesets[r.tileset]
|
||||||
|
if value_kind(ts.meta) != 6 { return 0 }
|
||||||
|
let tiles = value_get(ts.meta, "tiles")
|
||||||
|
if value_kind(tiles) != 5 { return 0 }
|
||||||
|
var i = 0
|
||||||
|
while i < value_count(tiles) {
|
||||||
|
let t = value_at(tiles, i)
|
||||||
|
if value_as_int(value_get(t, "id")) == r.local {
|
||||||
|
let props = value_get(t, "properties")
|
||||||
|
if value_kind(props) == 5 {
|
||||||
|
var j = 0
|
||||||
|
while j < value_count(props) {
|
||||||
|
let p = value_at(props, j)
|
||||||
|
if value_as_str(value_get(p, "name")) == name {
|
||||||
|
if value_as_str(value_get(p, "value")) == "true" { return 1 }
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- legacy rt_map compatibility projection --------------------------------
|
||||||
|
# project the designated collision layer down to the byte tilemap so Grid.* /
|
||||||
|
# Path.* / esys_move keep working unchanged: an empty cell is ' ' (passable), a
|
||||||
|
# tile whose GID carries the `oneway` property is '=' (61), any other non-zero
|
||||||
|
# GID is '#' (35, solid). The legacy 96x64 cap clips, so old code is byte-exact.
|
||||||
|
function tmap_project(m: Tmap, layer: int) -> void {
|
||||||
|
m.coll = layer
|
||||||
|
rt_map_size(m.w, m.h) # clamps to 96x64, clears to ' '
|
||||||
|
if layer < 0 or layer >= len(m.layers) { return }
|
||||||
|
let l = m.layers[layer]
|
||||||
|
if l.kind != 0 { return }
|
||||||
|
var y = 0
|
||||||
|
while y < l.h {
|
||||||
|
if y < 64 {
|
||||||
|
var x = 0
|
||||||
|
while x < l.w {
|
||||||
|
if x < 96 {
|
||||||
|
let gid = l.gids[y * l.w + x]
|
||||||
|
if gid != 0 {
|
||||||
|
var ch = 35 # '#'
|
||||||
|
if tmap_tile_prop(m, gid, "oneway") == 1 { ch = 61 } # '='
|
||||||
|
rt_map[y * 96 + x] = ch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x = x + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
y = y + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# find a tile layer named collision/solids/walls (case-sensitive), or -1.
|
||||||
|
function tmap_find_collision(m: Tmap) -> int {
|
||||||
|
var i = 0
|
||||||
|
while i < len(m.layers) {
|
||||||
|
let n = m.layers[i].name
|
||||||
|
if m.layers[i].kind == 0 and (n == "collision" or n == "solids" or n == "walls" or n == "Collision" or n == "Solids") { return i }
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
return 0 - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- rendering -------------------------------------------------------------
|
||||||
|
# blit one tilew x tileh tile from a tileset image (source origin sx,sy) to the
|
||||||
|
# framebuffer at (dx,dy), applying the three flip flags. Square tiles assumed
|
||||||
|
# for the diagonal flip (Kenney art is 16x16), which is the orthogonal case.
|
||||||
|
function tmap_blit_tile(imgid: int, sx: int, sy: int, tw: int, th: int, dx: int, dy: int, fh: int, fv: int, fd: int) -> void {
|
||||||
|
if imgid < 0 { return }
|
||||||
|
let s: words = img_px[imgid]
|
||||||
|
let iw = img_w[imgid]
|
||||||
|
var j = 0
|
||||||
|
while j < th {
|
||||||
|
var i = 0
|
||||||
|
while i < tw {
|
||||||
|
var u = i
|
||||||
|
var v = j
|
||||||
|
if fd == 1 { u = j; v = i }
|
||||||
|
if fh == 1 { u = tw - 1 - u }
|
||||||
|
if fv == 1 { v = th - 1 - v }
|
||||||
|
let argb = s[(sy + v) * iw + (sx + u)]
|
||||||
|
rt_blend_px(dx + i, dy + j, argb)
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# draw one tile GID at map cell (x,y) with the camera offset already applied.
|
||||||
|
function tmap_draw_gid(m: Tmap, gid: int, dx: int, dy: int) -> void {
|
||||||
|
if gid == 0 { return }
|
||||||
|
let r = tmap_resolve(m, gid)
|
||||||
|
if r.tileset < 0 { return }
|
||||||
|
let ts = m.tilesets[r.tileset]
|
||||||
|
if ts.imgid < 0 { return }
|
||||||
|
let cols = ts.columns
|
||||||
|
if cols <= 0 { return }
|
||||||
|
let cx = r.local - (r.local / cols) * cols
|
||||||
|
let cy = r.local / cols
|
||||||
|
let sx = ts.margin + cx * (ts.tilew + ts.spacing)
|
||||||
|
let sy = ts.margin + cy * (ts.tileh + ts.spacing)
|
||||||
|
# Tiled anchors a tile by its bottom-left, so a tile taller than the map cell
|
||||||
|
# rises above the cell.
|
||||||
|
let ddy = dy - (ts.tileh - m.tileh)
|
||||||
|
tmap_blit_tile(ts.imgid, sx, sy, ts.tilew, ts.tileh, dx, ddy, r.fh, r.fv, r.fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
# draw every visible tile layer in file order, offset by the camera (camx,camy).
|
||||||
|
function tmap_draw(m: Tmap, camx: int, camy: int) -> void {
|
||||||
|
var li = 0
|
||||||
|
while li < len(m.layers) {
|
||||||
|
let l = m.layers[li]
|
||||||
|
if l.kind == 0 and l.visible != 0 {
|
||||||
|
var y = 0
|
||||||
|
while y < l.h {
|
||||||
|
var x = 0
|
||||||
|
while x < l.w {
|
||||||
|
let gid = l.gids[y * l.w + x]
|
||||||
|
if gid != 0 {
|
||||||
|
tmap_draw_gid(m, gid, x * m.tilew - camx, y * m.tileh - camy)
|
||||||
|
}
|
||||||
|
x = x + 1
|
||||||
|
}
|
||||||
|
y = y + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
li = li + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- build the model from the intermediate Value tree ----------------------
|
||||||
|
function tmap_gids_from_layer(lv: Val) -> []int {
|
||||||
|
let out = new []int
|
||||||
|
let data = value_get(lv, "data")
|
||||||
|
var i = 0
|
||||||
|
while i < value_count(data) { push(out, value_as_int(value_at(data, i))); i = i + 1 }
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
# fill a TmTileset's geometry from a tileset Value object (its per-tile metadata
|
||||||
|
# stays in `.meta` for the resolver / property convention).
|
||||||
|
function tmap_tileset_from_value(tv: Val) -> TmTileset {
|
||||||
|
let ts = new TmTileset
|
||||||
|
ts.firstgid = value_as_int(value_get(tv, "firstgid"))
|
||||||
|
ts.tilew = value_as_int(value_get(tv, "tilewidth"))
|
||||||
|
ts.tileh = value_as_int(value_get(tv, "tileheight"))
|
||||||
|
ts.spacing = value_as_int(value_get(tv, "spacing"))
|
||||||
|
ts.margin = value_as_int(value_get(tv, "margin"))
|
||||||
|
ts.tilecount = value_as_int(value_get(tv, "tilecount"))
|
||||||
|
ts.columns = value_as_int(value_get(tv, "columns"))
|
||||||
|
if ts.columns <= 0 and ts.tilew > 0 { # derive columns from the image
|
||||||
|
let iw = value_as_int(value_get(tv, "imagewidth"))
|
||||||
|
if iw > 0 { ts.columns = (iw - 2 * ts.margin + ts.spacing) / (ts.tilew + ts.spacing) }
|
||||||
|
}
|
||||||
|
ts.meta = tv
|
||||||
|
return ts
|
||||||
|
}
|
||||||
|
|
||||||
|
function tiled_join(a: pointer, b: pointer) -> pointer { return Path.normalize(Path.join(a, b)) }
|
||||||
|
|
||||||
|
# build the runtime map from an intermediate tree, resolving external tilesets
|
||||||
|
# and loading tileset images relative to `basedir`.
|
||||||
|
function tmap_build(tree: Val, basedir: pointer) -> Tmap {
|
||||||
|
let m = new Tmap
|
||||||
|
m.tree = tree
|
||||||
|
m.layers = new []TmLayer
|
||||||
|
m.tilesets = new []TmTileset
|
||||||
|
m.orientation = value_as_str(value_get(tree, "orientation"))
|
||||||
|
m.w = value_as_int(value_get(tree, "width"))
|
||||||
|
m.h = value_as_int(value_get(tree, "height"))
|
||||||
|
m.tilew = value_as_int(value_get(tree, "tilewidth"))
|
||||||
|
m.tileh = value_as_int(value_get(tree, "tileheight"))
|
||||||
|
# tilesets
|
||||||
|
let tss = value_get(tree, "tilesets")
|
||||||
|
var i = 0
|
||||||
|
while i < value_count(tss) {
|
||||||
|
var tv = value_at(tss, i)
|
||||||
|
var imgdir = basedir
|
||||||
|
let src = value_as_str(value_get(tv, "source"))
|
||||||
|
if src != "" { # external .tsx/.tsj
|
||||||
|
let tsxpath = tiled_join(basedir, src)
|
||||||
|
let ext = tiled_read_tsx(tsxpath)
|
||||||
|
value_put(ext, "firstgid", value_get(tv, "firstgid"))
|
||||||
|
tv = ext
|
||||||
|
imgdir = Path.dir(tsxpath)
|
||||||
|
}
|
||||||
|
let ts = tmap_tileset_from_value(tv)
|
||||||
|
let img = value_as_str(value_get(tv, "image"))
|
||||||
|
if img != "" { ts.imgid = rt_image_load(tiled_join(imgdir, img)) }
|
||||||
|
push(m.tilesets, ts)
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
# layers (flat; groups' children are lifted so tile layers render/resolve)
|
||||||
|
let ls = value_get(tree, "layers")
|
||||||
|
i = 0
|
||||||
|
while i < value_count(ls) { tmap_add_layer(m, value_at(ls, i)); i = i + 1 }
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
function tmap_add_layer(m: Tmap, lv: Val) -> void {
|
||||||
|
let ty = value_as_str(value_get(lv, "type"))
|
||||||
|
let l = new TmLayer
|
||||||
|
l.data = lv
|
||||||
|
l.name = value_as_str(value_get(lv, "name"))
|
||||||
|
var vis = 1
|
||||||
|
if value_has(lv, "visible") == 1 { if value_as_int(value_get(lv, "visible")) == 0 { vis = 0 } }
|
||||||
|
l.visible = vis
|
||||||
|
if ty == "tilelayer" {
|
||||||
|
l.kind = 0
|
||||||
|
l.w = value_as_int(value_get(lv, "width"))
|
||||||
|
l.h = value_as_int(value_get(lv, "height"))
|
||||||
|
l.gids = tmap_gids_from_layer(lv)
|
||||||
|
push(m.layers, l)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ty == "objectgroup" { l.kind = 1; push(m.layers, l); return }
|
||||||
|
if ty == "imagelayer" { l.kind = 2; push(m.layers, l); return }
|
||||||
|
if ty == "group" {
|
||||||
|
l.kind = 3; push(m.layers, l)
|
||||||
|
let sub = value_get(lv, "layers")
|
||||||
|
var i = 0
|
||||||
|
while i < value_count(sub) { tmap_add_layer(m, value_at(sub, i)); i = i + 1 }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
push(m.layers, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- top-level load --------------------------------------------------------
|
||||||
|
# load a Tiled map file into the runtime model: read + parse (TMX or TMJ), build
|
||||||
|
# the model (resolving external tilesets + images), and project the collision
|
||||||
|
# layer (a tile layer named collision/solids/walls) down to the legacy tilemap.
|
||||||
|
function tiled_load(path: pointer) -> Tmap {
|
||||||
|
let tree = tiled_read(path)
|
||||||
|
let m = tmap_build(tree, Path.dir(path))
|
||||||
|
let c = tmap_find_collision(m)
|
||||||
|
if c >= 0 { tmap_project(m, c) }
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,16 +43,16 @@ function emit_index_addr(e: Node) -> pointer {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
let el = slice_elem(base.ty)
|
let el = slice_elem(base.ty)
|
||||||
g_addr_ty = el
|
|
||||||
# load the data pointer from the slice header (field 0)
|
# load the data pointer from the slice header (field 0)
|
||||||
let dp = nreg()
|
let dp = nreg()
|
||||||
emit(" "); emit(dp); emit(" = getelementptr inbounds %LSlice, ptr ")
|
emit(" "); emit(dp); emit(" = getelementptr inbounds %LSlice, ptr ")
|
||||||
emit(base.code); emit(", i32 0, i32 0\n")
|
emit(base.code); emit(", i32 0, i32 0\n")
|
||||||
let data = nreg()
|
let data = nreg()
|
||||||
emit(" "); emit(data); emit(" = load ptr, ptr "); emit(dp); emit("\n")
|
emit(" "); emit(data); emit(" = load ptr, ptr "); emit(dp); emit("\n")
|
||||||
let ix = emit_expr(e.b)
|
let ix = emit_expr(e.b) # (evaluate index BEFORE setting
|
||||||
let r = nreg()
|
let r = nreg() # g_addr_ty — a member-access index
|
||||||
emit(" "); emit(r); emit(" = getelementptr inbounds "); emit(llty(el))
|
emit(" "); emit(r); emit(" = getelementptr inbounds "); emit(llty(el)) # overwrites it)
|
||||||
emit(", ptr "); emit(data); emit(", i32 "); emit(ix.code); emit("\n")
|
emit(", ptr "); emit(data); emit(", i32 "); emit(ix.code); emit("\n")
|
||||||
|
g_addr_ty = el # set last so the caller sees the element type
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -611,8 +611,19 @@ function emit_ns_call(ns: pointer, meth: pointer, e: Node) -> Val {
|
||||||
# read/read_tsx produce the intermediate Value tree (#68); load/gid/resolve/
|
# read/read_tsx produce the intermediate Value tree (#68); load/gid/resolve/
|
||||||
# draw/prop operate on the loaded map model (#69+).
|
# draw/prop operate on the loaded map model (#69+).
|
||||||
if (ns == "Tiled") {
|
if (ns == "Tiled") {
|
||||||
if (meth == "read") { bare = "tiled_read"; push(labels, "path") }
|
if (meth == "read") { bare = "tiled_read"; push(labels, "path") }
|
||||||
if (meth == "read_tsx") { bare = "tiled_read_tsx"; push(labels, "path") }
|
if (meth == "read_tsx") { bare = "tiled_read_tsx"; push(labels, "path") }
|
||||||
|
if (meth == "load") { bare = "tiled_load"; push(labels, "path") }
|
||||||
|
if (meth == "gid") { bare = "tmap_gid"; push(labels, "map"); push(labels, "layer"); push(labels, "x"); push(labels, "y") }
|
||||||
|
if (meth == "resolve") { bare = "tmap_resolve"; push(labels, "map"); push(labels, "gid") }
|
||||||
|
if (meth == "width") { bare = "tmap_width"; push(labels, "map") }
|
||||||
|
if (meth == "height") { bare = "tmap_height"; push(labels, "map") }
|
||||||
|
if (meth == "layer_count") { bare = "tmap_layer_count"; push(labels, "map") }
|
||||||
|
if (meth == "layer_name") { bare = "tmap_layer_name"; push(labels, "map"); push(labels, "index") }
|
||||||
|
if (meth == "draw") { bare = "tmap_draw"; push(labels, "map"); push(labels, "camx"); push(labels, "camy") }
|
||||||
|
if (meth == "project") { bare = "tmap_project"; push(labels, "map"); push(labels, "layer") }
|
||||||
|
if (meth == "tree") { bare = "tmap_tree"; push(labels, "map") }
|
||||||
|
if (meth == "tile_prop") { bare = "tmap_tile_prop"; push(labels, "map"); push(labels, "gid"); push(labels, "name") }
|
||||||
}
|
}
|
||||||
# #62: a package-provided namespace (declared with @Namespace(Foo)) that none
|
# #62: a package-provided namespace (declared with @Namespace(Foo)) that none
|
||||||
# of the hardcoded core blocks matched — alias Foo.method to the bare function
|
# of the hardcoded core blocks matched — alias Foo.method to the bare function
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ function emit_header() -> void {
|
||||||
emith("declare ptr @memcpy(ptr, ptr, i64)\n")
|
emith("declare ptr @memcpy(ptr, ptr, i64)\n")
|
||||||
emith("declare ptr @memset(ptr, i32, i64)\n")
|
emith("declare ptr @memset(ptr, i32, i64)\n")
|
||||||
emith("declare i64 @strlen(ptr)\n")
|
emith("declare i64 @strlen(ptr)\n")
|
||||||
|
emith("declare i32 @strcmp(ptr, ptr)\n")
|
||||||
emith("declare i32 @strncmp(ptr, ptr, i64)\n")
|
emith("declare i32 @strncmp(ptr, ptr, i64)\n")
|
||||||
emith("declare ptr @strstr(ptr, ptr)\n")
|
emith("declare ptr @strstr(ptr, ptr)\n")
|
||||||
emith("declare i32 @atoi(ptr)\n")
|
emith("declare i32 @atoi(ptr)\n")
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
# ids are assignment order in the source; field ids are declaration order.
|
# ids are assignment order in the source; field ids are declaration order.
|
||||||
function emit_world_table() -> void {
|
function emit_world_table() -> void {
|
||||||
let me = itoa(MAX_ENT)
|
let me = itoa(MAX_ENT)
|
||||||
emith("declare i32 @strcmp(ptr, ptr)\n")
|
|
||||||
|
|
||||||
# EV7 — schema opening: a mod can register a brand-new component at runtime.
|
# EV7 — schema opening: a mod can register a brand-new component at runtime.
|
||||||
# Compile-time components take prop ids 0..NC-1; mod-defined ones take NC.. and
|
# Compile-time components take prop ids 0..NC-1; mod-defined ones take NC.. and
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,6 @@ function emit_fs_prelude() -> void {
|
||||||
emith("declare ptr @opendir(ptr)\n")
|
emith("declare ptr @opendir(ptr)\n")
|
||||||
emith("declare ptr @readdir(ptr)\n")
|
emith("declare ptr @readdir(ptr)\n")
|
||||||
emith("declare i32 @closedir(ptr)\n")
|
emith("declare i32 @closedir(ptr)\n")
|
||||||
emith("declare i32 @strcmp(ptr, ptr)\n")
|
|
||||||
emit_fs_common()
|
emit_fs_common()
|
||||||
emit_fs_path()
|
emit_fs_path()
|
||||||
emit_fs_io()
|
emit_fs_io()
|
||||||
|
|
|
||||||
19817
selfhost/ludicc.seed.ll
19817
selfhost/ludicc.seed.ll
File diff suppressed because it is too large
Load diff
|
|
@ -684,6 +684,17 @@
|
||||||
],
|
],
|
||||||
"tiled": [
|
"tiled": [
|
||||||
"tiled-read",
|
"tiled-read",
|
||||||
"tiled-read_tsx"
|
"tiled-read_tsx",
|
||||||
|
"tiled-load",
|
||||||
|
"tiled-gid",
|
||||||
|
"tiled-resolve",
|
||||||
|
"tiled-width",
|
||||||
|
"tiled-height",
|
||||||
|
"tiled-layer_count",
|
||||||
|
"tiled-layer_name",
|
||||||
|
"tiled-draw",
|
||||||
|
"tiled-project",
|
||||||
|
"tiled-tree",
|
||||||
|
"tiled-tile_prop"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -207,6 +207,7 @@ function cmd_test() -> int {
|
||||||
feat_case("library/serialize", "", "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16", "serialize.ludic (Value tree + Json encode/parse + Reflect.serialize/apply — bit-exact save/load; issue #44)")
|
feat_case("library/serialize", "", "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16", "serialize.ludic (Value tree + Json encode/parse + Reflect.serialize/apply — bit-exact save/load; issue #44)")
|
||||||
feat_case("library/tiled_p0", "", "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21", "tiled_p0.ludic (Tiled P0: Xml reader + Base64 decode/encode + gzip framing over inflate; issue #67)")
|
feat_case("library/tiled_p0", "", "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21", "tiled_p0.ludic (Tiled P0: Xml reader + Base64 decode/encode + gzip framing over inflate; issue #67)")
|
||||||
feat_case("library/tiled_p05", "", "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30", "tiled_p05.ludic (Tiled P0.5: TMX/TSX reader -> same Value-tree intermediate as TMJ; Kenney sampleMap.tmx; issue #68)")
|
feat_case("library/tiled_p05", "", "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30", "tiled_p05.ludic (Tiled P0.5: TMX/TSX reader -> same Value-tree intermediate as TMJ; Kenney sampleMap.tmx; issue #68)")
|
||||||
|
feat_case("library/tiled_p1", "", "1 2 3 4 5 6 7 8 9 10 11 12 13 14", "tiled_p1.ludic (Tiled P1: rt_tmap model + GID resolver/flip decode + render; loads+draws Kenney map identically from .tmx and .tmj; issue #69)")
|
||||||
feat_case("library/render", "", "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18", "render.ludic (Screen pixel/oval/camera/clip/blend_mode/measure_text + Camera set/follow/shake, verified by pixel readback)")
|
feat_case("library/render", "", "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18", "render.ludic (Screen pixel/oval/camera/clip/blend_mode/measure_text + Camera set/follow/shake, verified by pixel readback)")
|
||||||
feat_case("library/lighting", "", "1 2 3 4 5 6 7 8 9 10 11 12 13 14", "lighting.ludic (Light ambient/point radial falloff + occluder hard shadows — 2D light accumulation, verified by pixel readback)")
|
feat_case("library/lighting", "", "1 2 3 4 5 6 7 8 9 10 11 12 13 14", "lighting.ludic (Light ambient/point radial falloff + occluder hard shadows — 2D light accumulation, verified by pixel readback)")
|
||||||
feat_case("library/light_tiers", "", "1 1 1 1 1 1 1 1 1", "light_tiers.ludic (Light spot/falloff/soft/gel/normal/time_of_day — render-quality tiers 3-4; issue #49)")
|
feat_case("library/light_tiers", "", "1 1 1 1 1 1 1 1 1", "light_tiers.ludic (Light spot/falloff/soft/gel/normal/time_of_day — render-quality tiers 3-4; issue #49)")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue