EmeraldPartyRandomizer
Description
EmeraldPartyRandomizer is a ROM hack for Pokemon Emerald - if you're unfamiliar, a "ROM hack" is just what mods are called for cartridge games. The hack randomizes the player's party before every battle while keeping stats and XP. I wrote it for a Twitch streamer and YouTuber named SmallAnt. He requested it on stream one day, so I took it upon myself to write it and managed to get it done before he finished streaming that day.
A few days later, after we were sure it worked well, he started playing it on stream. The stream was no more popular than most of his streams, but the videos about it he posted quickly became some of his most popular - and remains that way today. Here it is:
More Information
Technical Details
When I decided to make the hack, my first step was to try and figure out how you even make a ROM hack. Unfortunately Google was largely unhelpful and mostly linked to people doing things like sprite swaps (skins/texture packs) and modifying maps. What I wanted to do was modify the code.
Luckily (extremely luckily), there's a project called pokeemerald that's a decompilation of the assembly into C. It took a few hours to set up the compiler toolchain (thankfully WSL exists), but once I got it to compile it was pretty simple to get the hack working.
I wanted the party to be randomized every time a battle started, so the first thing to do was figure out where the code was to start a battle.
Thankfully the pokeemerald folks did a good job of naming battle_controllers.c
, so it didn't take me too long to figure out.
A bit less aptly named, but clear from its body, the function I wanted is SetUpBattleVarsAndBirchZigzagoon
:
void SetUpBattleVarsAndBirchZigzagoon(void)
{
s32 i;
gBattleMainFunc = BeginBattleIntroDummy;
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
{
gBattlerControllerFuncs[i] = BattleControllerDummy;
gBattlerPositions[i] = 0xFF;
gActionSelectionCursor[i] = 0;
gMoveSelectionCursor[i] = 0;
}
HandleLinkBattleSetup();
gBattleControllerExecFlags = 0;
ClearBattleAnimationVars();
ClearBattleMonForms();
BattleAI_HandleItemUseBeforeAISetup(0xF);
if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE)
{
ZeroEnemyPartyMons();
CreateMon(&gEnemyParty[0], SPECIES_ZIGZAGOON, 2, USE_RANDOM_IVS, 0, 0, OT_ID_PLAYER_ID, 0);
i = 0;
SetMonData(&gEnemyParty[0], MON_DATA_HELD_ITEM, &i);
}
// Below are never read
gUnusedFirstBattleVar1 = 0;
gUnusedFirstBattleVar2 = 0;
}
Again, good naming comes to the rescue with the check against BATTLE_TYPE_FIRST_BATTLE
.
All we need to do is add an else
branch and call a function that randomizes the party:
if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE)
{
ZeroEnemyPartyMons();
CreateMon(&gEnemyParty[0], SPECIES_ZIGZAGOON, 2, USE_RANDOM_IVS, 0, 0, OT_ID_PLAYER_ID, 0);
i = 0;
SetMonData(&gEnemyParty[0], MON_DATA_HELD_ITEM, &i);
} else {
rndParty();
}
We don't need any arguments because this is a GameBoy game, so pretty much everything is global variables. Yucky, but helpful in this case. As far as the actual randomization, most of the code is handling minor details like copying EVs, nicknames, personality, etc. Here's some pseudo-C describing the algorithm:
void rndParty(void) {
for (int i = 0; i < NUM_POKEMON_IN_PARTY; i++) {
rndPkmn(&party[i]);
addPkmnToPokedex(&party[i]);
}
}
void rndPkmn(struct Pokemon* mon) {
//the actualy code copies out all the values we need seperately, but for clarity
//we'll just copy the whole struct
struct Pokemon backupMon = *mon;
//generate a random number for the new species
short newPokemonId = Random() % (NUM_SPECIES - 1) + 1;
//some species numbers are invalid.
//the real code attempts another random run before giving up and choosing a constant
if (IsSpeciesInvalid(newPokemonId)) newPokemonId = 100;
//set up the new pokemon
CreateMon(mon, newPokemonId, mon->level);
//copy across all the data we want to stay the same
copyPkmnData(mon, &backupMon);
}
If you want to see the actual code you can check the GitHub, but like I said, most of it is copying values and checking edge cases.
The last detail worth mentioning is that I have a weird set up for the way the code is hosted.
I didn't want to copy over the entire pokeemerald repo for ~100 lines of code, so I only host a separate .c
file containing everything except the else
branch we added earlier.
So to compile the hack, you have to #include
the .c
file and add the else
branch.
More complicated than I'd like, but better than hosting the entire other repo.
I also had issues fighting the compiler on includes and code generation since it's a bit old, so I had to copy over a few functions verbatim.
They're the ones that come before rndParty()
.