5.0 KiB
Building a Blue Version of the port
pokered builds Red and Blue from one source tree: the Makefile
assembles everything twice with rgbasm -D _RED or -D _BLUE, and
every in-game difference sits in an IF DEF(_RED) / IF DEF(_BLUE)
block. Our extraction pipeline resolves those conditionals the same
way (tools/extract/util.py ASM_DEFINES), so most of a Blue build
falls out of re-running the extractors. Only three things are
hand-ported on the Lua side and need swapping by hand.
What differs between Red and Blue
| Where (pokered) | What |
|---|---|
data/wild/maps/*.asm (34 files) |
Version-exclusive encounters (Red: Ekans, Oddish, Growlithe, Mankey, Scyther, Electabuzz, Blue: Sandshrew, Bellsprout, Vulpix, Meowth, Pinsir, Magmar) |
data/pokemon/title_mons.asm |
The 16 title-screen Pokémon |
engine/movie/title.asm + gfx/version.asm |
The "Red Version" / "Blue Version" ribbon |
constants/player_constants.asm |
Preset names (Red: RED/ASH/JACK + BLUE/GARY/JOHN) |
data/sgb/sgb_palettes.asm, sgb_border.asm |
Super Game Boy palettes and border |
data/events/prizes.asm, prize_mon_levels.asm |
Game Corner prize mons, costs and levels |
engine/movie/intro.asm, data/credits/credits_text.asm, engine/slots/slot_machine.asm, engine/battle/animations.asm, audio/sfx/save_3.asm |
Small gated tweaks (credits say "BLUE VERSION STAFF", etc.) |
Step 1, flip the extraction define
tools/extract/util.py:
ASM_DEFINES = {"_RED"} # -> {"_BLUE"}
Then regenerate everything (same as scripts/setup.sh does):
cd tools
../.venv/bin/python3 build_data.py \
--pokered /Users/bryanbassett/Documents/development/pokered \
--out ../data/generated --assets ../assets/generated
This alone switches the wild encounters, preset names, SGB palettes
and credits text. Caveat: parse_preset_names sanity-checks in
tools/extract/field.py:1193 expect "RED" in the player presets,
relax that check for a Blue build (Blue's presets are BLUE/GARY/JOHN
for the player and RED/ASH/JACK for the rival).
tools/extract/palettes.py uses its own raw reader with hardcoded
IF DEF(_BLUE) skipping (around line 44), invert that too, or port
it to read_asm so ASM_DEFINES covers it.
Step 2, title screen (hand-ported)
src/ui/TitleState.lua:
-
Ribbon art. The extractor writes
assets/generated/title/red_version.png; addblue_version.pngtogfx.extract_titleintools/extract/gfx.py(source:gfx/title/blue_version.png, 64×8). InTitleState:draw(), the Red strip needs two quads (tiles 0–1 "Red", skip, tiles 5–9 "Version",title.asmVersionOnTitleScreenText). Blue's strip prints its tiles contiguously (db $61..$68), so draw the whole 64×8 image at px (56, 64) and verify with the title driver screenshot. -
Title mons. Replace
CYCLE_SPECIESwith Blue's list fromdata/pokemon/title_mons.asm:local CYCLE_SPECIES = { "SQUIRTLE", "CHARMANDER", "BULBASAUR", "MANKEY", "HITMONLEE", "VULPIX", "CHANSEY", "AERODACTYL", "JOLTEON", "SNORLAX", "GLOOM", "POLIWAG", "DODUO", "PORYGON", "GENGAR", "RAICHU", }
Step 3, Game Corner prizes (hand-ported)
data/scripts/story3.lua (~line 209) carries the Red prize tables.
Blue's values (prizes.asm + prize_mon_levels.asm):
| Prize | Cost | Level |
|---|---|---|
| ABRA | 120 | 6 |
| CLEFAIRY | 750 | 12 |
| NIDORINO | 1200 | 17 |
| PINSIR | 2500 | 20 |
| DRATINI | 4600 | 24 |
| PORYGON | 6500 | 18 |
(TM prizes are identical in both versions.)
Step 4, verify
luajit tests/run_tests.lua
Plus two spot checks:
# every grass table must have exactly 10 slots (a conditional-parsing
# regression shows up as 19–20 slots)
luajit -e 'local e=dofile("data/generated/encounters.lua")
for m,d in pairs(e) do if type(d)=="table" and d.grass and #d.grass.slots>0
and #d.grass.slots~=10 then print("BAD",m,#d.grass.slots) end end'
# a Blue exclusive should now appear (and Growlithe should not)
grep -c VULPIX data/generated/encounters.lua
grep -c GROWLITHE data/generated/encounters.lua
Title screenshot: run the driver in
tests/drivers/ style (SHOT_DIR=... POKEPORT_DRIVER=... love .) and
eyeball the ribbon, title mon, and copyright row.
What you get for free / what to skip
- Free after re-extraction: encounters (incl. Super Rod groups), SGB palettes, preset names, credits text, the gated sfx/animation tweaks.
- Trades, gift Pokémon, story scripts, gym data: identical in Western Red/Blue, nothing to touch.
- Save files: a Red save loads fine, but dex AREA nests and new encounters will be Blue's. Trainer parties are identical.
Making it a runtime toggle instead
If you want one build with both versions, extraction would need to
emit both branches keyed by version (e.g. slots / slotsBlue) and
the three hand-ported spots would read a save.version or
conf.lua flag. That is a bigger change than the rebuild above,
the flip-and-regenerate route needs no engine changes at all.