Files
libakgl/tests/docs_setups/tilemap.sh

86 lines
2.9 KiB
Bash
Raw Normal View History

Compile, link and run every example in the documentation libakgl exports 157 functions and the only user-facing documentation was a FAQ in README.md whose examples did not compile: two unbalanced `PASS()` calls, a `sprite->frameids = [0, 1, 2, 3];` that is not C in any dialect, a stray `9` inside a bitmask expression, an `int screenwidth = NULL`, and -- in the first snippet a reader ever saw -- the exact `strncpy` call AGENTS.md forbids. That is not a reader routing around typos. It is what happens to samples that nothing executes. This is akbasic's documentation harness with the interpreter taken out and the ability to link and run put in. The contract is a set of fence info strings, so an example is ordinary markdown that still highlights on the forge: ```c compiled with -fsyntax-only -Wall -Werror ```c wrap=NAME the same, wrapped in tests/docs_preludes/NAME.pre/.post ```c run=NAME linked against akgl and run headless ```c excerpt=PATH must still appear verbatim in PATH ```c screenshot=NAME also the source of docs/images/NAME.png ```json kind=KIND loaded through the real akgl_*_load_json ```output the exact stdout of the runnable block above it `run=` is why this links at all: -fsyntax-only proves a call typechecks, not that the startup order works or that an ATTEMPT block gives back what it took. `json kind=` exists because the asset formats are documented in prose and read by four loaders with nothing tying the two together -- util/assets/littleguy.json is already invalid against the loader it ships with. A fence with no info string is a hard error, and so is an unknown one. The failure mode this exists to prevent is passing because it quietly ran nothing, so an unannotated block is a missing decision rather than a default. Exit status is the number of failed examples; 2 for a usage error, which is a different thing and has to be distinguishable. Proven to fail on all ten of its failure modes -- untagged fence, non-compiling snippet, stale excerpt, orphan output block, unknown info string, run= output mismatch, invalid JSON, missing figure, a -Werror warning, and a dangling preload= -- because a check that has never failed has not been tested. `-Werror` here although AKGL_WERROR stays off for the library: that option is off so a new compiler's diagnostic cannot break a consumer's build, and a doc snippet is not a consumer. A sample that warns is a sample that teaches the warning. Registered as `docs_examples` and `docs_screenshots`. No docs-path filter in CI, deliberately: documentation goes stale because the code moved, not because somebody edited a chapter. Co-Authored-By: Claude Code <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 20:58:11 -04:00
#!/bin/bash
#
# Stage everything a `json kind=tilemap` block needs around it.
#
# A tilemap is the least self-contained of the four formats. Loading one really
# opens the tileset image, and an actor object really resolves its `character`
# property against AKGL_REGISTRY_CHARACTER -- which in turn needs the character's
# sprites registered. So this stages three things, and
# tools/docs_checkjson.c's `tilemap` row loads ./sprites and then ./characters
# before the map itself.
#
# **The tileset image is staged as `assets/tileset.png`**, and a chapter's map
# has to name that path. It is a copy of the sprite sheet, and that is fine: the
# loader does not check the image against the tileset's declared geometry, it
# only has to be a readable PNG.
#
# The obvious alternative was `assets/World_A1.png`, which is what
# tests/assets/testmap.tmj names. That file carries the default filename of
# RPG Maker's bundled art and ships here with no license file beside it, which
# is a finding this project is recording rather than spreading into the manual.
# A tutorial a reader copies must not inherit an obligation nobody has checked.
#
# **The character is named `testcharacter`**, which is a contract with any
# chapter whose map has an actor object. Change it here and change the chapter.
#
# Embedded tilesets only. `"source"` appears nowhere in src/tilemap.c;
# akgl_tilemap_load_tilesets_each reads `columns`, `firstgid`, `tilecount` and
# `image` straight off each element of the map's own `tilesets` array. An
# external tileset reference -- the form Tiled writes by default -- does not
# load. Every object in an object group also needs a `type` string, plain
# rectangles included.
#
# $1 is the repository root. The caller runs this with the sandbox as the
# working directory.
set -u
ROOT="${1:-}"
if [ -z "${ROOT}" ]; then
echo "usage: tilemap.sh <repository-root>" >&2
exit 2
fi
mkdir -p assets sprites characters || exit 1
cp "${ROOT}/tests/assets/spritesheet.png" assets/tileset.png || exit 1
cp "${ROOT}/tests/assets/spritesheet.png" sprites/spritesheet.png || exit 1
cat > sprites/testactor_standing.json <<'JSON'
{
"spritesheet": {
"filename": "spritesheet.png",
"frame_width": 48,
"frame_height": 48
},
"name": "testactor standing",
"width": 48,
"height": 48,
"speed": 200,
"loop": true,
"loopReverse": false,
"frames": [0]
}
JSON
# AKGL_ACTOR_STATE_ALIVE is 1 << 4 and AKGL_ACTOR_STATE_FACE_DOWN is 1 << 0, so
# this mapping is the state 17 a map's actor object usually carries.
cat > characters/testcharacter.json <<'JSON'
{
"name": "testcharacter",
"speedtime": 100,
"speed_x": 0.20,
"speed_y": 0.20,
"acceleration_x": 0.20,
"acceleration_y": 0.20,
"sprite_mappings": [
{
"state": [
"AKGL_ACTOR_STATE_ALIVE",
"AKGL_ACTOR_STATE_FACE_DOWN"
],
"sprite": "testactor standing"
}
]
}
JSON