Pack Layout and Conventions¶
Every rule on this page applies to all file formats. Read it once before the per-format pages.
Source folder structure¶
Editor/<Mod Name>/
config.json
thumbnail.png
Entities/
<Entity Name>/
spriteSettings.json
animations.json
logic.json
dependencies.json
0.png, 1.png, 2.png, ...
portrait.png
preview.png
Palettes/
palette 0.png, palette 1.png, ...
Entity Replacements/
Classic/
<Classic Entity Name>/ same files as an entity
Levels/
level 1.json
level 1 - art.json
level 1 - cutscenes.json
title.json
presentation.json
ending.json
Art/
<any image files>
Level Replacements/
Classic/
level 1 - art.json
level 1 - cutscenes.json
title.json
presentation.json
ending.json
Palettes/
<Target Pack>/
<Entity Name>/
palette 0.png, palette 1.png, ...
Sounds/
<any audio files>
Musics/
<any audio files>
<track name>.json loop points
Audio Replacements/
<Target Pack>/
music-replacements.json
sound-replacements.json
Scripts/
<any .lua files>
Only the folders your mod uses need to exist. Missing folders and missing files are not errors; the loader treats them as "this mod provides nothing here".
The folder name under Editor/ is the pack name. Renaming the folder renames the pack, which changes its hash and breaks every packName reference pointing at the old name.
The three kinds of content¶
Understanding which of these you are authoring decides where the files go.
Own content lives under Entities/ and Levels/. It belongs to your pack and is addressed as (your pack name, entity or level name).
Replacements live under Entity Replacements/<Target Pack>/ and Level Replacements/<Target Pack>/. They substitute content of another pack, in place, keeping that pack's identity. This is how a reskin works: your Entity Replacements/Classic/Axel/ becomes what the game shows when it draws Classic's Axel. The folder name under the replacements folder must match an entity or file name that exists in the target pack.
Contributions live under Palettes/<Target Pack>/ and Audio Replacements/<Target Pack>/. They add to or redirect part of another pack without replacing whole entities.
<Target Pack> is almost always Classic, the built-in pack extracted from the original ROM.
How names resolve¶
Almost every cross-reference in these files is a (packName, name) pair of plain strings.
- An empty or omitted
packNamemeans the Classic pack in loadcues and dependencies, and the current pack in editor-written sound and script references. The per-format pages state which applies. - Names are matched by folder or file name exactly as written, including spaces and capitalisation.
- Internally, both names are reduced to a 32-bit hash. You never need to compute this except for
allowedCharactersinconfig.json; see Enums and Identifiers.
A reference to a pack that is not loaded is not a load-time error. It fails at the moment the game tries to use it, which usually looks like a missing sprite or a silent sound. List every pack you reference in config.json dependencies.
File naming rules¶
These are matched by prefix and suffix in code, so they are exact.
| Pattern | Meaning |
|---|---|
Levels/level <n>.json |
A level's gameplay data. The prefix is level with a trailing space. |
Levels/level <n> - art.json |
That level's visuals. The suffix is - art.json. |
Levels/level <n> - cutscenes.json |
That level's cutscenes. The suffix is - cutscenes.json. |
Levels/title.json, presentation.json, ending.json |
Fixed names for the non-level screens. |
Entities/<name>/<i>.png |
Own sprite frame i, zero-based, no padding. |
Palettes/.../palette <n>.png |
Palette n. Sorted numerically by the trailing digits, not alphabetically. |
Musics/<track>.json |
Loop points for Musics/<track>.<audio extension>. |
Level discovery scans Levels/ for files starting with level and not ending in the art or cutscenes suffix, so a stray level notes.json would be picked up as a level. Keep the folder tidy.
Palette sorting deserves a note: files are ordered by the digits at the end of the base name, so palette 2.png comes before palette 10.png. Plain alphabetical order would get this wrong, and everything downstream — exporting, deleting, the paletteIndex a loadcue refers to — assumes the numeric order.
Assets¶
| Asset | Format | Notes |
|---|---|---|
| Sprite frames | PNG | One image per frame. Transparency preserved. Frame count comes from spriteSettings.json pivots, not from how many PNGs exist. |
portrait.png, preview.png |
PNG | Optional. Character select portrait and preview. |
thumbnail.png |
PNG | Optional. Shown in mod listings. |
| Level art | PNG or JPG | Anything the engine's texture loader accepts. Referenced by file name including extension. |
| Palettes | PNG | See Palette Files. |
| Sounds and music | .wav, .mp3, .ogg |
Referenced by base name without extension. |
Level art images are drawn with point filtering by default, which keeps pixel art sharp. An image used only at non-integer scales is switched to bilinear filtering automatically. If the same image is used at both an integer and a non-integer scale anywhere in the pack, point filtering wins for all of its uses.
Editing safely¶
- Never reorder an indexed array.
animations,animationsLogic, andanimationsDefinitionare addressed by position. Inserting an entry in the middle silently repoints every reference past it. - Keep parallel arrays aligned.
animations.jsonanimations[i]andlogic.jsonanimationsLogic[i]describe the same animation. Either may be shorter, and either entry may benull. - Prefer omitting a field to writing a zero, especially on an entity that sets
referencePackName. On a referencing entity,0is indistinguishable from "not set" and falls through to the reference. See Entity Files. - Close the editor before hand-editing. The Godot editors hold the loaded model in memory and rewrite the whole file on save, so a save after your edit will discard it.