Renumber chapters 15 to 21 up by one, to open a slot for collision
A pure rename plus link rewrite and nothing else. Collision is a pluggable subsystem with two implementations, shapes, a response hook and a tile binding; that is a chapter, and burying it inside the physics chapter is the shape this manual otherwise avoids -- rendering and drawing are 8 and 9, spritesheets and characters and actors are 10, 11 and 12. Kept separate from writing the chapter because **nothing validates a cross-reference target.** The example harness reads fenced blocks; it does not follow links. A rename mixed into five hundred lines of new prose is not reviewable, and a broken link would land silently. As its own commit it is reviewable as a rename, and a grep for dangling `](NN-` targets is clean. Co-Authored-By: Claude Code <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -70,7 +70,7 @@ hit it, and an entry in `TODO.md`.
|
||||
| Friction / deceleration | Releasing a direction zeroes thrust and stops the actor dead. Right for Zelda, wrong for Mario | [14](14-physics.md) |
|
||||
| Savegames | `akgl_game_save` writes the name tables but not the objects, so a file is not yet enough to restore a session | [07](07-the-game-and-the-frame.md) |
|
||||
| Mesh drawing | `akgl_render_2d_draw_mesh` raises `AKERR_API`; the hook is reserved for a 3D backend | [08](08-rendering.md) |
|
||||
| A text cache | Every `akgl_text_rendertextat` rasterizes, uploads, blits and destroys | [16](16-text-and-fonts.md) |
|
||||
| A text cache | Every `akgl_text_rendertextat` rasterizes, uploads, blits and destroys | [16](17-text-and-fonts.md) |
|
||||
|
||||
## Who owns which documentation
|
||||
|
||||
@@ -86,12 +86,12 @@ caller actually write — and links out for the rest.
|
||||
| `aksl_strncpy`, `aksl_fclose`, `aksl_fgetc`, `aksl_snprintf` | libakstdlib (`deps/libakstdlib`) | Which ones libakgl requires you to use, and why |
|
||||
| `SDL_Renderer`, `SDL_Texture`, events, `SDL_PropertiesID` | [SDL3](https://wiki.libsdl.org/SDL3/) | The backend vtable, the frame contract, what libakgl does to the renderer's state |
|
||||
| Image decoding | [SDL3_image](https://wiki.libsdl.org/SDL3_image/) | Which formats reach a spritesheet, and when they are decoded |
|
||||
| Audio decoding, `MIX_Audio`, mixers and tracks | [SDL3_mixer](https://wiki.libsdl.org/SDL3_mixer/) | `akgl_load_start_bgm` and the track table — [Chapter 17](17-audio.md) |
|
||||
| TTF rasterizing and metrics | [SDL3_ttf](https://wiki.libsdl.org/SDL3_ttf/) | The font registry, the teardown ordering trap, the per-call cost — [Chapter 16](16-text-and-fonts.md) |
|
||||
| `json_t`, `json_decref`, the parser | [jansson](https://jansson.readthedocs.io/) | `akgl_get_json_*` status semantics and the borrowed-reference rule — [Chapter 18](18-utilities.md) |
|
||||
| Audio decoding, `MIX_Audio`, mixers and tracks | [SDL3_mixer](https://wiki.libsdl.org/SDL3_mixer/) | `akgl_load_start_bgm` and the track table — [Chapter 18](18-audio.md) |
|
||||
| TTF rasterizing and metrics | [SDL3_ttf](https://wiki.libsdl.org/SDL3_ttf/) | The font registry, the teardown ordering trap, the per-call cost — [Chapter 17](17-text-and-fonts.md) |
|
||||
| `json_t`, `json_decref`, the parser | [jansson](https://jansson.readthedocs.io/) | `akgl_get_json_*` status semantics and the borrowed-reference rule — [Chapter 19](19-utilities.md) |
|
||||
| The TMJ map format, layers, tilesets, custom properties | [Tiled](https://doc.mapeditor.org/en/stable/reference/json-map-format/) | libakgl's extensions and limits — [Chapter 13](13-tilemaps.md) |
|
||||
|
||||
The three-voice synthesizer in [Chapter 17](17-audio.md) is the one audio subsystem that is
|
||||
The three-voice synthesizer in [Chapter 18](18-audio.md) is the one audio subsystem that is
|
||||
libakgl's own, and it is documented here in full. It has nothing to do with SDL3_mixer.
|
||||
|
||||
## Where the per-function reference lives
|
||||
@@ -122,5 +122,5 @@ can read that return value. After that, [Chapter 3](03-getting-started.md) gets
|
||||
the screen, and the subsystem chapters can be read in any order.
|
||||
|
||||
If you would rather start by building something, the two tutorials —
|
||||
[Chapter 19](19-tutorial-sidescroller.md) and [Chapter 20](20-tutorial-jrpg.md) — are
|
||||
[Chapter 20](20-tutorial-sidescroller.md) and [Chapter 21](21-tutorial-jrpg.md) — are
|
||||
complete programs under `examples/`, built by default and smoke-run in CI.
|
||||
|
||||
@@ -243,7 +243,7 @@ starts at 256, so `exit(AKGL_ERR_SDL)` is a wait status of 0 and the shell sees
|
||||
**`akgl_text_unloadallfonts()` before `SDL_Quit()`.** `SDL_Quit` destroys the property
|
||||
registry the fonts live in and takes the last reference to every one of them with it. This
|
||||
program loads no fonts, and the call is in the shutdown anyway because that is where it
|
||||
belongs the moment one is loaded — [Chapter 16](16-text-and-fonts.md).
|
||||
belongs the moment one is loaded — [Chapter 17](17-text-and-fonts.md).
|
||||
|
||||
**Sixty `akgl_game_update` calls log `Low FPS! 0` sixty times.** `akgl_game.fps` is a
|
||||
completed-second average, so it reads 0 for the first second of the process, which is under
|
||||
@@ -285,4 +285,4 @@ assertions in `tests/` work.
|
||||
|
||||
[Chapter 5](05-the-heap.md) for the pools you just claimed a string from,
|
||||
[Chapter 7](07-the-game-and-the-frame.md) for what `akgl_game_update` actually does, or
|
||||
[Chapter 19](19-tutorial-sidescroller.md) to start building a game.
|
||||
[Chapter 20](20-tutorial-sidescroller.md) to start building a game.
|
||||
|
||||
@@ -97,7 +97,7 @@ Statuses raised by the libraries underneath also reach you unchanged. `aksl_fope
|
||||
`akgl_error_init` can raise libakerror's `AKERR_STATUS_RANGE_OVERLAP`,
|
||||
`AKERR_STATUS_RANGE_FULL` or `AKERR_STATUS_NAME_FULL`.
|
||||
|
||||
[Chapter 21](21-appendix-limits.md) has the per-function cross-reference.
|
||||
[Chapter 22](22-appendix-limits.md) has the per-function cross-reference.
|
||||
|
||||
## Table 3 — the exit status trap
|
||||
|
||||
@@ -264,6 +264,6 @@ Four things in there are libakgl-specific and worth naming:
|
||||
release.
|
||||
- [Chapter 6](06-the-registry.md) — the name lookups behind `AKERR_KEY` and
|
||||
`AKERR_NULLPOINTER`.
|
||||
- [Chapter 21](21-appendix-limits.md) — which function raises what.
|
||||
- [Chapter 22](22-appendix-limits.md) — which function raises what.
|
||||
- `deps/libakerror/README.md` — the protocol itself, and the exit-status discussion.
|
||||
- The generated Doxygen reference — every function's own `@throws` list.
|
||||
|
||||
@@ -344,4 +344,4 @@ all visible in `src/heap.c`.
|
||||
|
||||
- [Chapter 4](04-errors.md) — `AKGL_ERR_HEAP` in the status tables.
|
||||
- [Chapter 6](06-the-registry.md) — the registries the releases clear entries from.
|
||||
- [Chapter 21](21-appendix-limits.md) — every `AKGL_MAX_*` in one place.
|
||||
- [Chapter 22](22-appendix-limits.md) — every `AKGL_MAX_*` in one place.
|
||||
|
||||
@@ -224,5 +224,5 @@ registry's.
|
||||
- [Chapter 7](07-the-game-and-the-frame.md) — where in startup the registries are created,
|
||||
and where configuration has to be in place by.
|
||||
- [Chapter 14](14-physics.md) — choosing a backend, and what the `physics.*` properties do.
|
||||
- [Chapter 21](21-appendix-limits.md) — the configuration table again, alongside every
|
||||
- [Chapter 22](22-appendix-limits.md) — the configuration table again, alongside every
|
||||
`AKGL_MAX_*`.
|
||||
|
||||
@@ -367,4 +367,4 @@ error-reporting path. See [Chapter 4](04-errors.md).
|
||||
what `draw_world` does with the layers.
|
||||
- [Chapter 14](14-physics.md) — what `simulate` does with the `dt` this chapter does not
|
||||
pace.
|
||||
- [Chapter 21](21-appendix-limits.md) — `AKGL_GAME_*` and the rest of the constants.
|
||||
- [Chapter 22](22-appendix-limits.md) — `AKGL_GAME_*` and the rest of the constants.
|
||||
|
||||
@@ -326,7 +326,7 @@ colour it found, which is also what makes it testable without a world.
|
||||
|
||||
- [Chapter 08](08-rendering.md) — the backend these all take, and the frame the
|
||||
calls sit inside.
|
||||
- [Chapter 16](16-text-and-fonts.md) — text, which is textures rather than
|
||||
- [Chapter 17](17-text-and-fonts.md) — text, which is textures rather than
|
||||
primitives.
|
||||
- [Chapter 05](05-the-heap.md) — the pools, and why `copy_region` returning owned
|
||||
memory is the exception rather than the rule.
|
||||
|
||||
@@ -295,7 +295,7 @@ The `akgl_actor_cmhf_*` functions ("control map handler function") are what
|
||||
`akgl_controller_default` binds the arrow keys and the D-pad to, and what a game's
|
||||
own `akgl_Control` bindings can point at. They take the control map's **target
|
||||
actor**, not a global "player", so they work for any number of locally controlled
|
||||
actors. See [Chapter 15](15-input.md).
|
||||
actors. See [Chapter 16](16-input.md).
|
||||
|
||||
| Handler | Sets | Clears first | Writes |
|
||||
|---|---|---|---|
|
||||
@@ -441,7 +441,7 @@ for `akgl_Actor`.
|
||||
|
||||
- [Chapter 11](11-characters.md) — the template, and the state-to-sprite map.
|
||||
- [Chapter 14](14-physics.md) — what happens to `t`, `e`, `v` and `a` each step.
|
||||
- [Chapter 15](15-input.md) — control maps, and binding the handlers above.
|
||||
- [Chapter 16](16-input.md) — control maps, and binding the handlers above.
|
||||
- [Chapter 08](08-rendering.md) — where `renderfunc` gets called from.
|
||||
- [Chapter 04](04-errors.md) — `AKGL_ERR_LOGICINTERRUPT` and the rest of the
|
||||
status tables.
|
||||
|
||||
@@ -37,7 +37,7 @@ why the library's own map, `akgl_default_gamemap`, is a file-scope object in
|
||||
The bounds are fixed at compile time, so the size is fixed too. Every field is present
|
||||
whether the map uses it or not: a 20x15 map with one tileset and two layers costs the same
|
||||
25.2 MiB as a 511x511 one. Raising any of the constants below raises this number and
|
||||
changes the ABI — see [Chapter 21](21-appendix-limits.md).
|
||||
changes the ABI — see [Chapter 22](22-appendix-limits.md).
|
||||
|
||||
## Limits
|
||||
|
||||
|
||||
@@ -333,8 +333,8 @@ contract is "nothing collides", which is an answer. The arcade one means "nobody
|
||||
written this yet", and a caller who reaches it should find out.
|
||||
|
||||
Until it exists, collision belongs in your `movementlogicfunc`, as in the example above.
|
||||
The sidescroller tutorial in [Chapter 19](19-tutorial-sidescroller.md) does exactly that and
|
||||
says why. `akgl_collide_*` in [Chapter 18](18-utilities.md) gives you the rectangle tests.
|
||||
The sidescroller tutorial in [Chapter 20](20-tutorial-sidescroller.md) does exactly that and
|
||||
says why. `akgl_collide_*` in [Chapter 19](19-utilities.md) gives you the rectangle tests.
|
||||
|
||||
### Four gaps in the feel
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 15. Input
|
||||
# 16. Input
|
||||
|
||||
libakgl does not have an input system so much as a *binding* system. SDL3 owns events,
|
||||
keycodes, keymods, gamepad enumeration and text composition, and it documents all of them
|
||||
@@ -138,7 +138,7 @@ With XInput2 the events carry the physical slave device's `sourceid` and
|
||||
`SDL_GetKeyboards()[0]` may be the master. Binding `SDL_GetKeyboards()[0]` therefore gives
|
||||
you a map that matches nothing at all, and controls that silently do nothing.
|
||||
|
||||
That is not hypothetical — the sidescroller in [Chapter 19](19-tutorial-sidescroller.md)
|
||||
That is not hypothetical — the sidescroller in [Chapter 20](20-tutorial-sidescroller.md)
|
||||
shipped exactly that bug, and its smoke test passed anyway because the test called the
|
||||
handlers instead of dispatching events.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 16. Text and fonts
|
||||
# 17. Text and fonts
|
||||
|
||||
**SDL3_ttf owns fonts.** It opens the file, rasterizes the glyphs, and computes the metrics;
|
||||
its API and its behaviour are documented in the
|
||||
@@ -1,4 +1,4 @@
|
||||
# 17. Audio
|
||||
# 18. Audio
|
||||
|
||||
**There are two audio subsystems in libakgl and they have nothing to do with each other.**
|
||||
Not two layers, not two APIs over one engine — two unrelated things that happen to make
|
||||
@@ -1,4 +1,4 @@
|
||||
# 18. Utilities
|
||||
# 19. Utilities
|
||||
|
||||
`util.h`, `json_helpers.h` and `staticstring.h` hold the pieces that belong to no subsystem:
|
||||
rectangle overlap, asset path resolution, typed JSON accessors, and the pooled string type
|
||||
@@ -1,4 +1,4 @@
|
||||
# 19. Tutorial: a 2D sidescroller
|
||||
# 20. Tutorial: a 2D sidescroller
|
||||
|
||||
A complete game: a Tiled level, a player who runs and jumps, platforms that hold him up,
|
||||
four coins to collect, a blob that patrols and a moth that flies. It is about nine hundred
|
||||
@@ -94,7 +94,7 @@ libakgl has exactly one startup order that works and it is written down at the t
|
||||
`akgl_game.name`, `.version` and `.uri` are required and have no defaults —
|
||||
`akgl_game_init` refuses to run without all three, because the window title, SDL's
|
||||
application metadata and the savegame compatibility check are built from them. `aksl_strncpy`
|
||||
rather than `strncpy`, per [Chapter 18](18-utilities.md) and `AGENTS.md`: these are
|
||||
rather than `strncpy`, per [Chapter 19](19-utilities.md) and `AGENTS.md`: these are
|
||||
fixed-width fields that end up as registry keys.
|
||||
|
||||
```c excerpt=examples/sidescroller/main.c
|
||||
@@ -469,7 +469,7 @@ actually arrives.
|
||||
|
||||
The `_on` handlers are the library's unchanged. `akgl_actor_cmhf_right_on` clears
|
||||
`FACE_ALL | MOVING_ALL`, sets `MOVING_RIGHT | FACE_RIGHT` and signs `ax` from the character
|
||||
— exactly right. Only the release half needed replacing. [Chapter 15](15-input.md) covers
|
||||
— exactly right. Only the release half needed replacing. [Chapter 16](16-input.md) covers
|
||||
building a control map; the whole of this game's is six bindings, three keyboard and three
|
||||
gamepad.
|
||||
|
||||
@@ -603,7 +603,7 @@ memory at runtime is one fewer failure mode. See [Chapter 5](05-the-heap.md).
|
||||
|
||||
## Picking things up
|
||||
|
||||
Collecting a coin is a rectangle test from [Chapter 18](18-utilities.md) and a pool release:
|
||||
Collecting a coin is a rectangle test from [Chapter 19](19-utilities.md) and a pool release:
|
||||
|
||||
```c excerpt=examples/sidescroller/player.c
|
||||
PASS(errctx, hitbox(ss_game.coins[i], 8.0f, &other));
|
||||
@@ -693,7 +693,7 @@ program's `CLEANUP` block, which is reached whether startup succeeded or not —
|
||||
**There is no `akgl_game_shutdown`.** Teardown belongs to the application, and the order
|
||||
matters in one place: `akgl_text_unloadallfonts` has to run before `TTF_Quit` or `SDL_Quit`,
|
||||
because those destroy the fonts underneath the registry that still points at them
|
||||
([Chapter 16](16-text-and-fonts.md)). This game loads no fonts and calls it anyway, because
|
||||
([Chapter 17](17-text-and-fonts.md)). This game loads no fonts and calls it anyway, because
|
||||
the ordering is the thing worth copying.
|
||||
|
||||
What this does **not** do is unwind the sprite, spritesheet and character pools. They are
|
||||
@@ -806,7 +806,7 @@ rather than a score — an assertion on the numbers would be a test of the sched
|
||||
- [Chapter 12](12-actors.md) — the state mask, the six hooks, and the control handlers.
|
||||
- [Chapter 13](13-tilemaps.md) — what the map loader accepts, and the three extensions to
|
||||
Tiled this level uses.
|
||||
- [Chapter 15](15-input.md) — control maps, and why a binding that never fires is usually
|
||||
- [Chapter 16](16-input.md) — control maps, and why a binding that never fires is usually
|
||||
the wrong device id.
|
||||
- [Chapter 20](20-tutorial-jrpg.md) — the same library from the other end: a top-down game
|
||||
- [Chapter 21](21-tutorial-jrpg.md) — the same library from the other end: a top-down game
|
||||
with no gravity, where the content pipeline is the interesting part.
|
||||
@@ -1,4 +1,4 @@
|
||||
# 20. Tutorial: a top-down JRPG
|
||||
# 21. Tutorial: a top-down JRPG
|
||||
|
||||
A town, three people standing in it, a party member who follows you around, and
|
||||
a box that tells you what the elder thinks of the road north. About six hundred
|
||||
@@ -7,7 +7,7 @@ lines of C, in `examples/jrpg/`, built by `all` and run headless by `ctest`.
|
||||
This is the **content-pipeline** tutorial. Its subject is the road from a
|
||||
directory of JSON and PNG to a world with people in it: twenty-four sprites, three
|
||||
characters, one Tiled map that spawns its own actors, four-way per-facing
|
||||
animation, and text on top. [Chapter 19](19-tutorial-sidescroller.md) is the
|
||||
animation, and text on top. [Chapter 20](20-tutorial-sidescroller.md) is the
|
||||
physics one — gravity, a jump, a coin — and the two are deliberately
|
||||
complementary. If you want to know how `ey` accumulates, read that one.
|
||||
|
||||
@@ -130,7 +130,7 @@ There is no diagonal row and there does not need to be one. The default arrow
|
||||
bindings clear *every* facing and movement bit before setting their own
|
||||
(`akgl_actor_cmhf_left_on` and its five siblings all do), so holding two
|
||||
directions moves in whichever was pressed last rather than diagonally. That is
|
||||
documented in [Chapter 15](15-input.md), and it is why an eight-way character
|
||||
documented in [Chapter 16](16-input.md), and it is why an eight-way character
|
||||
sheet would be wasted on the default bindings.
|
||||
|
||||
## The map spawns the actors
|
||||
@@ -424,7 +424,7 @@ and a decision about which reading is canonical.
|
||||
Text in libakgl is immediate mode. Every `akgl_text_rendertextat` call
|
||||
rasterizes the string through SDL_ttf, uploads it to a texture, blits it, and
|
||||
destroys the texture and the surface again — there is no glyph atlas and no text
|
||||
object to keep. [Chapter 16](16-text-and-fonts.md) says plainly that this is
|
||||
object to keep. [Chapter 17](17-text-and-fonts.md) says plainly that this is
|
||||
wrong for a page of static prose redrawn sixty times a second, and it is exactly
|
||||
right for a line of dialogue that is on screen only while somebody is talking.
|
||||
|
||||
@@ -565,7 +565,7 @@ the third thing here is a deliberate omission:
|
||||
- **`akgl_text_unloadallfonts` must run before `SDL_Quit`.** Fonts live in an SDL
|
||||
property registry, and `SDL_Quit` destroys the registry — taking the last
|
||||
reference to every font still in it, with no way left to close them.
|
||||
[Chapter 16](16-text-and-fonts.md) covers the ordering.
|
||||
[Chapter 17](17-text-and-fonts.md) covers the ordering.
|
||||
- **`akgl_tilemap_release` is not called**, and that is the fourth workaround.
|
||||
Its layer loop destroys `tilesets[i].texture` rather than `layers[i].texture`,
|
||||
so it double-frees every tileset texture and never frees an image layer's, and
|
||||
@@ -663,13 +663,13 @@ failing a frame. [Chapter 4](04-errors.md) covers installing your own
|
||||
|
||||
## Where to look next
|
||||
|
||||
- [Chapter 19](19-tutorial-sidescroller.md) — the same shape of program with
|
||||
- [Chapter 20](20-tutorial-sidescroller.md) — the same shape of program with
|
||||
gravity, a jump and platforms, which is where the physics model earns its keep.
|
||||
- [Chapter 13](13-tilemaps.md) — the map format, the three libakgl extensions to
|
||||
Tiled, and the four constraints the loader really enforces.
|
||||
- [Chapter 11](11-characters.md) and [Chapter 12](12-actors.md) — the template
|
||||
and the instance, and why the state word is the whole key.
|
||||
- [Chapter 15](15-input.md) — control maps, the default bindings, and the
|
||||
- [Chapter 16](16-input.md) — control maps, the default bindings, and the
|
||||
keystroke ring this game does not use.
|
||||
- [Chapter 21](21-appendix-limits.md) — every compile-time ceiling in one place,
|
||||
- [Chapter 22](22-appendix-limits.md) — every compile-time ceiling in one place,
|
||||
including the 64-actor pool this town uses four of.
|
||||
@@ -1,4 +1,4 @@
|
||||
# 21. Appendix: limits, statuses and properties
|
||||
# 22. Appendix: limits, statuses and properties
|
||||
|
||||
Three reference tables that no single chapter owns: which function raises which status,
|
||||
every compile-time bound, and every configuration property the library reads.
|
||||
@@ -27,13 +27,13 @@ per-function reference, build the Doxygen output with `doxygen Doxyfile`.
|
||||
| **[12. Actors](12-actors.md)** | The state bitmask, the six behaviour hooks, parents and children, layers |
|
||||
| **[13. Tilemaps](13-tilemaps.md)** | Tiled TMJ, libakgl's three extensions, and the limits that bind a map |
|
||||
| **[14. Physics](14-physics.md)** | Thrust, environment and velocity; the `null` and `arcade` backends; what is not implemented |
|
||||
| **[15. Input](15-input.md)** | Control maps, push-not-poll dispatch, the keystroke ring, gamepads |
|
||||
| **[16. Text and fonts](16-text-and-fonts.md)** | Loading, drawing, measuring, and the teardown order that matters |
|
||||
| **[17. Audio](17-audio.md)** | The three-voice synthesizer, and the separate background-music path |
|
||||
| **[18. Utilities](18-utilities.md)** | Collision helpers, path resolution, the JSON accessors, static strings |
|
||||
| **[19. Tutorial: a 2D sidescroller](19-tutorial-sidescroller.md)** | Gravity, a jump, collision you write yourself, coins and hazards |
|
||||
| **[20. Tutorial: a top-down JRPG](20-tutorial-jrpg.md)** | A town map, NPCs spawned from map objects, four-way animation, a text box, a follower |
|
||||
| **[21. Appendix](21-appendix-limits.md)** | Every limit, every status, every configuration property |
|
||||
| **[15. Input](16-input.md)** | Control maps, push-not-poll dispatch, the keystroke ring, gamepads |
|
||||
| **[16. Text and fonts](17-text-and-fonts.md)** | Loading, drawing, measuring, and the teardown order that matters |
|
||||
| **[17. Audio](18-audio.md)** | The three-voice synthesizer, and the separate background-music path |
|
||||
| **[18. Utilities](19-utilities.md)** | Collision helpers, path resolution, the JSON accessors, static strings |
|
||||
| **[19. Tutorial: a 2D sidescroller](20-tutorial-sidescroller.md)** | Gravity, a jump, collision you write yourself, coins and hazards |
|
||||
| **[20. Tutorial: a top-down JRPG](21-tutorial-jrpg.md)** | A town map, NPCs spawned from map objects, four-way animation, a text box, a follower |
|
||||
| **[21. Appendix](22-appendix-limits.md)** | Every limit, every status, every configuration property |
|
||||
|
||||
Both tutorials are complete programs under [`examples/`](../examples). They build with the
|
||||
library and run headless in CI, and the chapters quote them rather than restating them — so
|
||||
|
||||
Reference in New Issue
Block a user