5 Guide Total Conversions
bryanthaboi edited this page 2026-10-20 17:01:40 -04:00
This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Total conversions

Building a new game on the engine (new region, new dex, new story, new branding) without touching engine source. The capstone tutorial (Tutorial 12) walks a small one end-to-end; this page is the map of the territory.

Start with a small playable result

This is an advanced authoring path. Start with a scaffolded mod, an imported game, and the Modding Basics and first change pages if you are new to Lua. Run the scaffold command from the engine repository root, the directory containing tools/modkit.py.

Build in milestones and boot the game after each one:

  1. Scaffold the conversion and confirm it loads as a mod.
  2. Replace one map and tileset, then walk through it.
  3. Add one encounter, character, and text change.
  4. Add the story, roster, rules, art, and audio in separate steps.
  5. Add save state, package the result, and test from a clean install.

Keep a short DIFFERENCES.md as you go. At each milestone, record the visible result and the file or registry that produced it. If the game does not boot after a change, disable the last change, read the manager error feed, and use Troubleshooting before adding more surfaces.

The profile

This is a complete minimal manifest for the scaffolded conversion; add only fields your project actually uses.

{
  "id": "my_conversion",
  "version": "1.0.0",
  "api": 2,
  "profile": "total_conversion",
  "games": ["red"],
  "description": "A small replacement game"
}

profile = "total_conversion" labels the mod in the manager and marks it as content that needs deliberate link review. Link play uses the launcher’s approved vanilla or sealed custom-cart flow; the manifest field below is not a player permission for loose-mod linking. The scaffold wires the profile:

python3 tools/modkit.py scaffold my_conversion --profile total_conversion

What a conversion replaces (all data)

Surface Mechanism
The world maps / tilesets / encounters registries; field (ledges, hidden items, fly network, town map, badge gates)
The boot flow field.boot: spawn, names, money, naming presets, screens = { splash, title, newGame } resolved through the screens registry. To keep OakSpeech but add questions / sprite beats / answers, wrap intro.oak_speech.build instead of replacing newGame
The roster pokemon / moves / type_chart / statuses / move_effects / growth_rates / evolution_methods
The people trainers / ai_classes; NPCs as map objects + map_scripts
The story map_scripts (compose; an override chain excludes the vanilla contribution per map), commands, tokens, text, text_pointers
The rules constants: dex size, badges (override for a whole new set), bag/party/box caps, level cap, HM list
The sound music / sfx / cries / map_songs; ChipAsm for authentic tracks
The look palettes / font / sprites / transitions; asset transforms and overrides/ (Art Pipeline)
The screens screens registry + the ui.* menu hooks
The saves save.new_game hook when field.boot is not enough; mod.save + mod.migrations for your own state

The deep registries take override as the total-conversion verb: it replaces a whole key where patch folds into it.

One generation at a time

A total conversion is a Gen 1 project today. profile is a label (the manager shows it and the mod index flags anything but content); it unlocks nothing per generation, and the scaffold writes "games": ["gen1"]. The surfaces the table above leans on hardest are Gen 1 surfaces:

Surface Gen 2 (Gold, Silver, Crystal) Gen 3 (FRLG, RSE)
field boot only: startMap/startX/startY/startFacing, startMoney, rivalName, namePresets. No boot.screens, title, starterScript no target
maps, tilesets, encounters, trainers, text Gen 2 tables, Gen 2 record shapes maps by native map id; encounters, trainers, text own tables; tilesets no target
map_scripts Gold's bytecode VM scripts (data.gen2Scripts) native script ops (data.gen3Scripts)
constants Gold's ordered ROM name lists no target
rulesets, text_pointers no target no target
transitions Gen 1 target no target

A write with no target is taken, dropped and reported, so there is no sanctioned way to replace a Gen 2 or Gen 3 boot flow or title, and Gen 2 and Gen 3 story work means native-shape map_scripts, not the Gen 1 row lists (Data Model). Keep the conversion on "games": ["gen1"] (or omit games, which means Gen 1 only) rather than half-loading on later games. Smaller cross-game pieces (a species, a move, an item) can ship as a separate api 3 mod; see Portable Mods across Gen 1, 2 and 3 and Preparing Your Mod for Gen 2.

What a conversion must not replace

  • The ROM import. It stays mandatory: it supplies fonts, UI plumbing and audio infrastructure, and it is the legal foundation: the player's own verified ROM is the only game-content input. Conversions overlay the imported world; they never substitute for it.
  • Engine source. Everything you need is a registry, hook, or event; a conversion that requires private modules is unsupported and must declare permissions = ["engine_internals"].
  • Someone else's content. Porting an existing ROM hack means rebuilding it natively: data re-expressed as registry records, mechanics rewritten against the mod API, art regenerated via transforms. The loader never executes IPS/BPS/UPS patches.

Coverage discipline

A conversion is done when every surface the player meets is deliberate. Iterate the merged registries (mod.content.<name>:each()) in a dev build to hunt vanilla leftovers: a Kanto map song on your route, an unreplaced trainer party, a dex entry you forgot. Keep a DIFFERENCES.md in the mod enumerating what diverges from vanilla; the base game's own ledgers stay clean because your changes vanish with the mod (Compatibility).

Shipping

modkit pack gates distribution on the no-ROM-content lint. To link two players, include the conversion in a sealed custom cart and test that cart through the launcher; an ordinary conversion package is not a link cart (Link Compatibility, Publishing).

Check and recover

At each milestone, the expected result is a bootable game with the one visible change just added. If the boot fails, disable the last surface, read the manager error feed, and return to the last working milestone before adding another registry or hook.