Files
2026-07-28 06:16:21 -04:00
..
2026-07-19 16:18:18 -04:00
2026-07-19 16:18:18 -04:00
2026-07-19 16:18:18 -04:00
2026-07-19 16:18:18 -04:00
2026-07-25 12:36:53 -04:00
2026-07-19 16:18:18 -04:00
2026-07-19 16:18:18 -04:00

Shiny Palette Example

Recolors the player's overworld sheets to teal and repaints Pallet Town — and ships no ROM-derived pixels to do it.

Persona: the Artist. This is the canonical answer to "how do I ship a recolor legally": you ship the transform, not the image.

Try it

python3 tools/modkit.py validate mods/examples/example_shiny_palette --base imported
python3 tools/modkit.py lint mods/examples/example_shiny_palette
luajit mods/examples/example_shiny_palette/tests/example_shiny_palette_test.lua

Enable it (example_shiny_palette = true under mods in options.lua, or the F10 manager) and start the game. On first load the transform runs once and writes save/mod-derived/example_shiny_palette/sprites/*.png. Delete that directory to force a re-run.

What it demonstrates

Seam Where
assets_transforms manifest.json + transforms.lua — the recipe that derives art
content.palettes:register main.lua — the v2 named-record palette shape
content.palettes:override main.lua — the vanilla four-triple shape
content.sprites:patch main.luatrueColor opt-out, nothing else touched
events:on("assets.transformed") main.lua — the empty-state warning

transforms.lua runs inside a restricted context with exactly two filesystem roots: read assets/generated/** (the player's own imported cache) and write save/mod-derived/example_shiny_palette/**. There is no require, no love, no io, no os. The only way data leaves the sandbox is the ctx table.

Because the derived file keeps the same relative name as the cache file it came from, the asset resolver finds it automatically:

assets/generated/sprites/red.png          <- the player's import
save/mod-derived/example_shiny_palette/sprites/red.png   <- this mod's recolor

Every consumer of the first path transparently gets the second. No sprites:override, no path string in main.lua — and modkit lint can prove the repo carries no cache-derived bytes, because it carries no bytes at all.

The one thing that does need a registry entry is the 4-shade contract. The renderer normally re-shades an overworld sheet into the current palette's four grays, which would throw the teal away. trueColor = true opts out:

mod.content.sprites:patch("SPRITE_RED", { trueColor = true })

patch, not override: image, frames and walker stay whatever the merged view already holds, so the derived sheet keeps supplying the pixels.

Empty state

No ROM imported yet? ctx.exists(rel) is false, the transform writes nothing, the mod still loads, and main.lua logs a remediation line naming the directory to delete once you have imported. It never errors.

Original assets

assets/accent_sparkle.png is a 16x16 four-shade sparkle drawn for this example. It is the only image in the directory and it is original work.

Credits

  • pret/pokered — the overworld sheet layout the transform recolors.