diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100644 index 0000000..1ebdad0 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,13 @@ +#!/bin/sh +# Sporel API-Doc-Convention pre-commit hook. +SPOREL_EXE="${SPOREL_EXE:-$(command -v Sporel.exe 2>/dev/null || command -v sporel 2>/dev/null)}" +if [ -z "$SPOREL_EXE" ]; then + echo "INFO: Sporel.exe not on PATH. Skipping lint." >&2 + exit 0 +fi +"$SPOREL_EXE" --lint="$(pwd)" --fix +RC=$? +if git diff --cached --name-only | grep -qx 'README.md'; then + git add README.md +fi +exit $RC diff --git a/README.md b/README.md index 3095524..52f0b6b 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,145 @@ # lib-core.maps -P.0 single-tile-grid-map implementation. Loads JSON map + tilemap files, -provides tile-fetch + walkability queries. +Single tile-grid map implementation. Loads JSON map + tilemap files, registers maps by id, and provides tile-fetch + walkability + size queries with current-map sugar. -- Lib-ID: `lib-core.maps` -- Version: `0.1.0` -- Spec: `meta/docs/superpowers/specs/2026-05-09-p0-lib-maps-design.md` +**Version:** 0.1.1 +**Lib-ID:** lib-core.maps +**Requires:** (none) +**Tags:** maps, tile-grid, walkability, tilemap -Forward-compat stubs (DEPRECATED-MVP) for multi-map graph, lifecycle states, -walls, sprite-fields, generators, save-integration. See -`meta/docs/architecture/map-topology.md` for the full target spec. +## Topology + + +```mermaid +graph LR + this["lib-core.maps"] + engine["engine.*"] + this --> engine +``` + ## API -- `maps.load(path)` — load + register a map from JSON -- `maps.create(table)` — programmatic creation (tests, procedural) -- `maps.tile_at(map_id, tx, ty)` / `maps.tile_at(tx, ty)` — current-map sugar -- `maps.is_walkable(...)`, `maps.size(...)`, `maps.tile_size(...)` -- `maps.current()`, `maps.set_current(id)`, `maps.list()` -- Stubs (deferred): `maps.state()`, `maps.pin()` + +### `maps.load(path)` +**Syntax:** `maps.load(path: string) -> string` + +**Example:** +```lua +local id = maps.load("maps/forest.json") +maps.set_current(id) +``` + +**Description:** Loads a JSON map-file from disk, resolves + loads its referenced tilemap (local module-tilemap-id or fully-qualified), validates the map-table, and registers it. Returns the map-id. Errors if the id is already registered. + +### `maps.create(t)` +**Syntax:** `maps.create(t: {id: string, tilemap_table: table, ...}) -> string` + +**Example:** +```lua +local id = maps.create({ + id = "test-tiny", + size = { w = 4, h = 4 }, + tile_size = { w = 16, h = 16 }, + tilemap_table = my_tilemap, + tiles = { 0,0,0,0, 0,1,1,0, 0,1,1,0, 0,0,0,0 }, +}) +``` + +**Description:** Programmatic creation for tests + procedural generators. Caller must supply a fully-built `tilemap_table` (not a path/id reference). Otherwise identical to `load`. + +### `maps.size(map_id)` +**Syntax:** `maps.size(map_id: string | nil) -> {w: integer, h: integer}` + +**Description:** Returns map-size in tiles. Falls back to current-map when `map_id` is nil. Errors if no current map. + +### `maps.tile_size(map_id)` +**Syntax:** `maps.tile_size(map_id: string | nil) -> {w: integer, h: integer}` + +**Description:** Returns tile-size in pixels. Falls back to current-map when nil. + +### `maps.tile_at(a, b, c)` +**Syntax:** `maps.tile_at(map_id: string, tx: integer, ty: integer) -> table | nil` *(also: `maps.tile_at(tx, ty)` uses current-map)* + +**Example:** +```lua +local tile = maps.tile_at(5, 3) +if tile then engine.print(tile.id) end +``` + +**Description:** Arity-flex sugar: `tile_at(tx, ty)` uses current-map; `tile_at(map_id, tx, ty)` is explicit. Returns the resolved tile-record from the tilemap, or `nil` if out-of-bounds. + +### `maps.is_walkable(a, b, c)` +**Syntax:** `maps.is_walkable(map_id: string, tx: integer, ty: integer) -> bool` *(also: `is_walkable(tx, ty)` uses current-map)* + +**Description:** Convenience: returns `true` iff the tile exists and has `walkable == true`. Out-of-bounds is `false`. + +### `maps.tilemap_id(map_id)` +**Syntax:** `maps.tilemap_id(map_id: string | nil) -> string` + +**Description:** Returns the fully-qualified tilemap-id referenced by a map. + +### `maps.current()` +**Syntax:** `maps.current() -> string | nil` + +**Description:** Returns the currently-active map-id, or `nil` if none. + +### `maps.set_current(map_id)` +**Syntax:** `maps.set_current(map_id: string) -> void` + +**Description:** Switches the current-map pointer. Errors if `map_id` is not registered. + +### `maps.list()` +**Syntax:** `maps.list() -> string[]` + +**Description:** Returns an array of all registered map-ids. + +### `maps.state(map_id)` +**Syntax:** `maps.state(map_id: string) -> string` + +**Description:** DEPRECATED-MVP stub. Always returns `"Active"` in v0.1.x. Full lifecycle (`Virgin`/`Inert`/`Passive`/`Active`/`Pinned`) lands in the multi-map slice. + +### `maps.pin(map_id, reason)` +**Syntax:** `maps.pin(map_id: string, reason: string) -> void` + +**Description:** DEPRECATED-MVP stub. Emits a warn in v0.1.x. Pinning prevents lifecycle-eviction in the multi-map slice. + +## Conventions + +- Pixel-coords + tile-coords kept distinct: `size`/`tiles` index in tile-units; `tile_size` is the conversion to pixels. +- Tilemap-ids may be local (`.`) or fully-qualified; `load()` resolves both. +- `tile_at` is bounds-checked: out-of-bounds returns `nil` (not error). +- Y-down-positive per ADR-0031. + +## Consumer pattern + +```lua +local maps = require("lib-core.maps") + +local id = maps.load("maps/forest.json") +maps.set_current(id) + +local sz = maps.size() +for ty = 0, sz.h - 1 do + for tx = 0, sz.w - 1 do + if not maps.is_walkable(tx, ty) then + -- mark blocked cell + end + end +end +``` + +## CHANGELOG + +### v0.1.1 +- Tilemap-id resolution handles module-ids with dots (prefix-match instead of first-dot split). + +### v0.1.0 (P.0) +- Initial release: load/create + tile_at + is_walkable + current-map management. + +## References + +- Spec v0.1.0 (P.0): `meta/docs/superpowers/specs/2026-05-09-p0-lib-maps-design.md` +- Architecture: `meta/docs/architecture/map-topology.md` +- ADR-0001 (engine knows verbs, libs bring nouns) +- ADR-0031 (pixel-convention: Y-down-positive) +- ADR-0038 (API-Doc-Convention) diff --git a/scripts/install-hooks.sh b/scripts/install-hooks.sh new file mode 100644 index 0000000..2ff6e41 --- /dev/null +++ b/scripts/install-hooks.sh @@ -0,0 +1,3 @@ +#!/bin/sh +git config core.hooksPath .githooks +echo "Hooks activated."