Compiling Pokémon Red (pokered) using Docker and adding 'Super B' button behaviour
Over the Christmas break I found myself on a bit of a nostalgic gaming journey. Along with building a RetroPie, I dusted off my old Game Boy and decided to ‘catch them all’ one more time on Pokémon Red. Whilst playing, the developer in me started to contrive how a game like this was created, and better still could be changed. Enter pokered, a disassembly of Pokémon Red/Blue which has been organised so that an assembly code noice like myself can understand it. In this post I will go through compiling the ASM, tweaking the game to add ‘Super B’ button behaviour, and then running the compiled ROM on an actual handheld!
Note: I don’t condone piracy and personally own both Pokémon Red/Blue game cartridges that this project is based on.
Compiling pokered using Docker
The pokered Git repository is well documented, including the steps required to compile the given assembly code for the desired platform.
However, being an advocate of Docker I wished to take it a step further an Dockerise this process.
To do this I created the Dockerfile
below, which uses a small Debian base image to install all the required dependencies with.
FROM bitnami/minideb:stretch
RUN install_packages make git gcc byacc flex pkg-config libpng-dev ca-certificates
RUN git clone https://github.com/rednex/rgbds /opt/rgbds && make -C /opt/rgbds install && rm -fr /opt/rgbds
RUN mkdir /opt/src
WORKDIR /opt/src
CMD ["make", "all"]
From here I then compiled and pushed this image to Docker Hub and created a small executable docker-make
script within the root of the repository.
#!/bin/bash
docker run -v $(pwd):/opt/src eddmann/pokered make $@
This allowed me to seamlessly run all provided make
targets within the Docker container like so ./docker-make all
.
Once complete, two new build artifacts were now present in the root of the repository - pokered.gbc
and pokeblue.gbc
.
Both these ROMs were now playable using a desktop/web Game Boy emulator like wasmboy. 🎉
Adding the ‘Super B’ button behaviour
Now that I could now compile the game into a working ROM, my mind began thinking of all the small tweaks to the game I would like to experiment with. One aspect of the original that always felt time consuming was when trying to get to a destination in a hurry and be caught out by many wild Pokémon (without Repel active) and trainer-battle encounters. I started to review the code-base to see if there was a way of taking advantage of when the B button was pressed, possibly temporarily preventing these two activities.
Prevent Wild Pokémon Encounters
It turns out that the game had an unused debug mode that was left in, which provided one part of this behaviour.
As such I was able to tweak engine/battle/core.asm
to prevent wild Pokémon encounters if the B button was pressed.
DetermineWildOpponent:
- ld a, [wd732]
- bit 1, a
- jr z, .asm_3ef2f
ld a, [hJoyHeld]
- bit 1, a ; B button pressed?
+ and B_BUTTON
ret nz
This tweak simply removes the check for if the debug mode is enabled and short-circuits the action if the B button is pressed.
Prevent Trainer Battle Encounters
Following this, I now hoped to follow a similar pattern in temporarily preventing trainer battle encounters.
I was able to achieve this by making some minor modifications to home.asm
.
CheckFightingMapTrainers::
+ ld a, [hJoyHeld]
+ and B_BUTTON
+ jr nz, .noFight
call CheckForEngagingTrainers
ld a, [wSpriteIndex]
cp $ff
jr nz, .trainerEngaging
+ .noFight
xor a
ld [wSpriteIndex], a
ld [wTrainerHeaderFlagBit], a
ret
In a similar manor to how we prevented wild Pokémon encounters, if the B button is pressed we jump to .noFight
, skipping the CheckForEngagingTrainers
call.
Once compiled, I was able to experiment with these two changes and found them to be very interesting additions to game play.
Walking Through Walls
I then thought wouldn’t it be cool to accommodate for a similar ‘walk through walls’ glitch behaviour that you see in certain speed runs.
As such I delved back into the code-base and made a small modification to home/overworld.asm
which provided me with this capability.
.noDirectionChange
ld a, [wPlayerDirection] ; current direction
ld [wPlayerMovingDirection], a ; save direction
call UpdateSprites
+ ld a, [hJoyHeld]
+ and B_BUTTON
+ jr nz, .noCollision
ld a, [wWalkBikeSurfState]
cp $02 ; surfing
jr z, .surfing
After another compile I was now able to not only prevent wild Pokémon and trainer battle encounters, but also ‘walk through walls’ when desired. You can see a small clip below of how game play is altered when the ‘Super B’ button is pressed.
Due to the effort put into making the pokered project so readable, I found it very easy to work my way through the code-base and locate the areas of interest to experiment with. The people behind this disassembly have also worked on doing the same for many other Pokémon releases.
Playing on a Handheld
With this modifications in place I ideally wished to now use this new behaviour on a handled console, as opposed to just through a desktop emulator. To achieve this I purchased an EZ-Flash Omega cartridge for the GBA which allowed me to transfer these ROMs onto a micro-SD card. Now I could play these games on a backlit screen using my Nintendo DS Lite. 😎