# Provenance of the tutorial assets Everything under `docs/tutorials/assets/` is **CC0 1.0** or was written for this repository. Nothing here carries an attribution requirement, a share-alike clause, or a field-of-use restriction, which is the whole point: a reader who copies a tutorial into their own game inherits whatever obligation these assets carry, and CC0 carries none. "Free to download" is not the same thing and was not accepted -- each pack's licence was read on its Kenney page *and* in the `License.txt` it ships, and `scripts/fetch_tutorial_assets.sh` re-checks both on every refresh and refuses to vendor a pack that fails either. `LICENSE` is the CC0 1.0 legal code. The three `LICENSE.kenney_*.txt` files are the licence statements the packs themselves ship, kept beside the art in the same way `tests/assets/akgl_test_mono.LICENSE.txt` sits beside its font. The two tables below account for all 56 asset files: 14 carry upstream bytes and 42 were written here. This file and `README.md` are the remaining two, and are documentation of the same kind as the rest of `docs/`. ## Upstream packs | Pack | URL | Licence | Licence file in this tree | |-----------------------|------------------------------------------------|---------|--------------------------------------------| | Pixel Line Platformer | https://kenney.nl/assets/pixel-line-platformer | CC0 1.0 | `LICENSE.kenney_pixel-line-platformer.txt` | | RPG Urban Pack | https://kenney.nl/assets/rpg-urban-pack | CC0 1.0 | `LICENSE.kenney_rpg-urban-pack.txt` | | Music Jingles | https://kenney.nl/assets/music-jingles | CC0 1.0 | `LICENSE.kenney_music-jingles.txt` | Kenney asks for credit and does not require it. Credit is given here and in the tutorial chapters. ## Files carrying upstream bytes | File | Pack | Source URL | Licence | Cropped / repacked into | |--------------------------------------------|-----------------------|-----------------------------------------------------------------|---------|----------------------------------------------------------------------------------------------------| | `LICENSE` | creativecommons.org | https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt | CC0 1.0 | The CC0 1.0 Universal legal code, fetched verbatim | | `LICENSE.kenney_pixel-line-platformer.txt` | Pixel Line Platformer | https://kenney.nl/assets/pixel-line-platformer | CC0 1.0 | The pack's own `License.txt`, copied verbatim | | `LICENSE.kenney_rpg-urban-pack.txt` | RPG Urban Pack | https://kenney.nl/assets/rpg-urban-pack | CC0 1.0 | The pack's own `License.txt`, copied verbatim | | `LICENSE.kenney_music-jingles.txt` | Music Jingles | https://kenney.nl/assets/music-jingles | CC0 1.0 | The pack's own `License.txt`, copied verbatim | | `sidescroller/tiles.png` | Pixel Line Platformer | https://kenney.nl/assets/pixel-line-platformer | CC0 1.0 | `Tilemap/tilemap_packed.png` copied verbatim: 160x96, 10x6 tiles of 16x16, no spacing, no margin | | `sidescroller/player.png` | Pixel Line Platformer | https://kenney.nl/assets/pixel-line-platformer | CC0 1.0 | Tiles 40, 41, 42 and their horizontal mirrors, bottom-centred in six 32x32 cells | | `sidescroller/coin.png` | Pixel Line Platformer | https://kenney.nl/assets/pixel-line-platformer | CC0 1.0 | Tile 44, bottom-centred in one 32x32 cell | | `sidescroller/hazard.png` | Pixel Line Platformer | https://kenney.nl/assets/pixel-line-platformer | CC0 1.0 | Tiles 55, 56, 51, 52, bottom-centred in four 32x32 cells | | `sidescroller/jingle_start.ogg` | Music Jingles | https://kenney.nl/assets/music-jingles | CC0 1.0 | `Audio/8-Bit jingles/jingles_NES00.ogg`, copied verbatim (1.8 s) | | `jrpg/tiles.png` | RPG Urban Pack | https://kenney.nl/assets/rpg-urban-pack | CC0 1.0 | `Tilemap/tilemap_packed.png` copied verbatim: 432x288, 27x18 tiles of 16x16, no spacing, no margin | | `jrpg/player.png` | RPG Urban Pack | https://kenney.nl/assets/rpg-urban-pack | CC0 1.0 | Character block 0 (tileset columns 23-26, rows 0-2) regrouped into twelve 32x32 cells | | `jrpg/npc_shopkeeper.png` | RPG Urban Pack | https://kenney.nl/assets/rpg-urban-pack | CC0 1.0 | Character block 3 (tileset columns 23-26, rows 9-11) regrouped into twelve 32x32 cells | | `jrpg/npc_elder.png` | RPG Urban Pack | https://kenney.nl/assets/rpg-urban-pack | CC0 1.0 | Character block 2 (tileset columns 23-26, rows 6-8) regrouped into twelve 32x32 cells | | `jrpg/jingle_start.ogg` | Music Jingles | https://kenney.nl/assets/music-jingles | CC0 1.0 | `Audio/Pizzicato jingles/jingles_PIZZI07.ogg`, copied verbatim (1.3 s) | ## Files written for this repository These contain no upstream content. They are libakgl source in JSON form: they name the art above, they do not embed it. | File | Kind | What it is | |-------------------------------------------------|-----------|---------------------------------------------------------------------------------| | `jrpg/character_jrpg_elder.json` | character | State-name to sprite-name bindings; contains no upstream content | | `jrpg/character_jrpg_player.json` | character | State-name to sprite-name bindings; contains no upstream content | | `jrpg/character_jrpg_shopkeeper.json` | character | State-name to sprite-name bindings; contains no upstream content | | `jrpg/sprite_jrpg_elder_idle_down.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_elder_idle_left.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_elder_idle_right.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_elder_idle_up.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_elder_walk_down.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_elder_walk_left.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_elder_walk_right.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_elder_walk_up.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_player_idle_down.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_player_idle_left.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_player_idle_right.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_player_idle_up.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_player_walk_down.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_player_walk_left.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_player_walk_right.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_player_walk_up.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_shopkeeper_idle_down.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_shopkeeper_idle_left.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_shopkeeper_idle_right.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_shopkeeper_idle_up.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_shopkeeper_walk_down.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_shopkeeper_walk_left.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_shopkeeper_walk_right.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/sprite_jrpg_shopkeeper_walk_up.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `jrpg/town.tmj` | map | Tiled 1.8 TMJ, tileset **embedded**. 30x20 cells, ground + decoration + actors | | `sidescroller/character_ss_coin.json` | character | State-name to sprite-name bindings; contains no upstream content | | `sidescroller/character_ss_hazard_blob.json` | character | State-name to sprite-name bindings; contains no upstream content | | `sidescroller/character_ss_hazard_moth.json` | character | State-name to sprite-name bindings; contains no upstream content | | `sidescroller/character_ss_player.json` | character | State-name to sprite-name bindings; contains no upstream content | | `sidescroller/level1.tmj` | map | Tiled 1.8 TMJ, tileset **embedded**. 40x15 cells, background + terrain + actors | | `sidescroller/sprite_ss_coin.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `sidescroller/sprite_ss_hazard_blob.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `sidescroller/sprite_ss_hazard_moth.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `sidescroller/sprite_ss_player_idle_left.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `sidescroller/sprite_ss_player_idle_right.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `sidescroller/sprite_ss_player_jump_left.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `sidescroller/sprite_ss_player_jump_right.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `sidescroller/sprite_ss_player_run_left.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | | `sidescroller/sprite_ss_player_run_right.json` | sprite | Frame list into the sheet named by its own `spritesheet.filename` | ## How the repacking works, and why **Tilesets are copied verbatim.** Both packs already ship a 16x16 grid with zero spacing and zero margin, which is the only geometry `akgl_tilemap_compute_tileset_offsets` gets right -- it adds `spacing` to the tile pitch but sets the *first* row's y offset to `spacing` rather than zero, and it ignores `margin` completely. A packed sheet with a 1 px gutter, which is what most of Kenney's older packs ship, would render every tile in row 0 one pixel low. That ruled several otherwise-suitable packs out. **Character sheets are repacked into one row of 32x32 cells.** The asset contract for these tutorials is 16x16 tiles and 32x32 character frames, and neither pack ships 32x32 art, so each 16x16 source tile is composited at (8, 16) inside its cell -- horizontally centred, sitting on the cell's bottom edge. The art is not resampled: a 2x upscale would put 2x2 pixel blocks next to 1x1 tiles, and every sprite in the repository would stop matching the source pixel for pixel. The cost is that three quarters of each cell is empty, and the benefit is one rule the tutorials can rely on: **an actor drawn into a 32x32 destination rectangle has its feet on the bottom edge of that rectangle and its body centred**, whichever sheet it came from. **Left-facing frames are stored, not mirrored at draw time.** `akgl_actor_render` calls `draw_texture` with `SDL_FLIP_NONE` hard-coded (`src/actor.c`), so there is no way to ask for a mirrored blit. The sidescroller sheet therefore carries frames 0-2 facing right and frames 3-5 as their horizontal mirrors. The JRPG sheets need no mirroring: the RPG Urban Pack draws both side views. **One frame is used twice.** Pixel Line Platformer has no dedicated jump pose. Frame 1 is the passing position of the run cycle, which is the airborne one, and `ss_player_jump_right` reuses it. Said here rather than left for a reader to notice. **The coin does not animate.** Pixel Line Platformer ships one gold pickup tile and no rotation frames, so `sprite_ss_coin.json` is a single frame. Nothing was invented to pad it out. ## Reproducing this ```sh scripts/fetch_tutorial_assets.sh ``` Fetches all three packs, checks each page and each shipped `License.txt` says CC0, verifies the source images are the size the frame arithmetic assumes, repacks into a temporary directory, checks every staged file's dimensions, and only then moves anything into place. A failure at any step leaves the tracked copies untouched and exits non-zero. The output is byte-for-byte reproducible -- ImageMagick's wall-clock `date:create` and `date:modify` chunks are stripped, so a refresh that changes nothing produces no diff. The script does not touch the JSON or the TMJ maps. Those are hand-maintained.