Skip to content

Projects, maps, levels, releases

This page answers the question that comes back every week: what compiles the art of a game, where does what live, and what goes to the board?

The vocabulary, in one table

Word What it is
Project the game being written: its .lua files (main.lua, always), its .mask files, its sounds
Library the sprites loaded as they are, in true colour (RGB/RGBA): a single sprite, or one animation row
Model what a level places: a collision box, an anchor, and states (idle, walk, jump…), each on a sprite of the library with its rate, its loop and its effect (alpha, outline, behind the background). The player is a model, a torch too. Shared by every level of the account
Map the drawing: layers of 8×8 tiles, stamps and terrains, decors placed on the layers (tile grids with their own mask), and a collision layer. A map places no object and does not compile on its own
Level a map and what plays on it: the objects (models placed at the pixel), the compile settings and a preview. The level is what compiles: the flattened layers of its map become the background, the sprites of its models are quantised with it, in a single palette. Several levels can share a map
Attachment "this project embeds level N": what compiling does when the project is ticked. A reference, not a copy
Group (console side) what the console sees of a level: N.pal, N.atlas, its indexed PNGs, its variants N.<v>.pal, its background N-fond.map + N-fond-tiles.png
Release the published snapshot: a complete folder, the files of the project plus the pieces of its attached levels, frozen, identified by the fingerprint of its manifest

So: a level of the App = a group on the console, named after the level: gfx.background("level2-fond"), phy.anim_load("level2/torch_4"), gfx.id("level2/torch_4_0"). This name follows the rule of groups: lowercase, digits, underscore, no hyphen (the hyphen separates the level from its background). The name of a map does not reach the console: only the level's does.

The firmware says "the game folder" for the whole that gfx.bundle() compiles in one go, all the levels. That is the same object as the release here.

A game is a flat folder

A game is a flat folder of .lua, of .mask, of sounds and of the pieces of its levels, nothing else. Flat because require() is flat on the console: the same rules on both sides, so that a project that works online works on the board.

It is identified by (account, name) and never by the nickname, which can change.

Bytes are addressed by their fingerprint

A file is stored under the fingerprint of its content, never under a project path. Two identical files, the normal case from the first clone on, therefore share a single object: cloning a game copies no bytes, only lines.

What goes where, and why

  • .lua and .mask in the project. Compiling a level, project ticked, writes two files there: <level>.mask, the collision geometry, one cell per tile, from its map's collision layer and the masks of the decors placed on it; and <level>.lua, the data and factories of the level (dimensions, background, models, objects, charger(), creer(), instancier(), decharger()), which the game loads with require("<level>"). This .lua carries a marker on its first line; a .lua of the same name written by hand is never overwritten: the compilation says so and moves on. The .mask has no marker: it is rewritten at every compilation.
  • The background comes from the map. The drawn layers are flattened, quantised with the sprites, then re-tiled for the console (distinct tiles by frequency, mirrors merged, 704 at most, clustering by similarity beyond). It comes out as two pieces, <level>-fond.map and <level>-fond-tiles.png. The map stays the source; the .map is a product.
  • A level compiled against an older map is stale. Retouching a map changes nothing in the games until its levels are recompiled; the workshop marks them recompile, and publishing a game is refused while one of its levels is stale, so that the game and its published levels show the same drawing.
  • One palette per level, computed over the background and every placed sprite. An indexed pixel only makes sense in the palette that produced it: that is why a sprite is never quantised alone, and why the console only displays sprites of one group at a time (a single active palette). A sprite only enters a game if a model that carries it is placed in a level, even invisible.
  • Two levels on one map are two groups. Each brings its own palette and its own background, and both count in the console's bounds: sharing a map saves drawing, not memory on the board.
  • An effect is a property of the bitmap. Additive, translucent, outline, behind the background: these are palette rows. They are set per model state, hold for every level, and one sprite cannot carry two effects in one level.
  • Publishing materialises. A release must sit as it is on a micro-SD card; it therefore embeds the pieces of the attached levels. The .zip export of the draft does the same. The project, for its part, keeps only the reference: recompiling a level changes the next publication, not the previous one.
  • What is used cannot be deleted. A sprite placed by a compiled level, a model placed in a level, a map that levels use, a level attached to a project: the App refuses, because otherwise a game would stop loading far from the cause. Remove the object and recompile; delete or move the levels first; detach the level from the project.

The console's bounds, counted per project

The console holds, per game, 12 palettes (levels + variants), 16 backgrounds (a level brings one), 2 MiB of graphic pieces, 1 024 sprites and 64 animations. In the workshop, the project's sheet (What the console allows) and the Budget panel count the first three; compiling a level refuses to attach it to a project it would overflow, and says so. The 64 animations count per level loaded: they are the states of the models a level places, and compiling refuses a level that has more; M.decharger() frees them before the next level.

Names, and their bound

A full name, <level>/<region> for gfx.id(), <level>-fond for gfx.background(), has to fit in the 48-byte buffers of the console and of the simulator: 47 characters at most. The sum is checked at compilation, with a refusal that names the culprit. Each half keeps its own rule (60 at most), and no pair that fits is forbidden.

Licences and credits

Everything that comes into the workshop says where it comes from: a sprite, a sound, a tileset or a decor image imported into a map. Three answers: your own work; CC0 from elsewhere (public domain; a credit is welcome); CC BY 4.0 from elsewhere, with its credit (the author, and where it comes from), which is then required. An item imported without an answer is not declared: usable, but nothing that contains it can be published until it is declared.

A map keeps the list of its Sources: what its imports brought from elsewhere. Its tiles mix, so the credit follows the map, not each tile. A map made before sources were tracked is not reviewed until its author reviews it.

What contains CC BY is published under CC BY, with every credit gathered, your pseudonym first: a public level, a published game, which then carries a credits.txt next to its files. Otherwise you choose CC0 or CC BY. Publishing always asks you to tick a box: a false credit gets what is public withdrawn at once, together with every public level and game that embeds it; the copies others made are flagged, not deleted, until their owner corrects the credit and lifts the flag.