November 23, 2015 · code

On the Topic of Structure, or Making a Pokémon

In this entry, we're going to be making a Pokémon from scratch.

Mewtwo refrains from striking back That's me right there. The evil-looking guy with the impressive hairdo.

It's... not quite as dramatic as the hit '98 animated film Pokémon: The First Movie: Mewtwo Strikes Back makes it out to be, but we are indeed going to be creating a Mewtwo, using some of the code written for Lanette's time capsule so far. Before we do that, however, I'd like to talk a bit about how the code is structured.

Being a programmer, one of the most difficult things is structure. Just throwing things together is fine at first, but once you want to join several parts, or want to maintain a project with new features or bugfixes, a lack of structure will quickly make you cry tears of blood. Already, in the post on making a breakout board, you've seen a bit of the structure of the hardware side of Lanette's time capsule, and so in this devlog entry I'm going to talk a bit more about what the software side of things looks like, what it will eventually look like, and which distinct parts there are to it.

Boiling Lanette's time capsule down to its shiny temporal core, it really is about one thing and one thing only: translating one form of data to another. Pokémon in Generation II are represented in bytes in one way, and Pokémon in Generation III in another. In its very essence, the Lanette capsule is about taking one of these and translating it to the other. If you were to rush into this without thinking, you might do something like grab the relevant bytes directly from the raw data in one generation, and mash it into correct positions and shapes in the data for the other. However, that would be messy, hard to read, and hard to use in other contexts than sending to the other game - comparing stats, for example, or showing the name of a Pokémon on a screen.

This is why I decided to parse Pokémon data into what's called an "intermediary representation", which is exactly what it sounds like: a way to represent data in between two others. There is one intermediary representation for Generation II Pokémon (with the catchy name Gen2Pokemon), and one for Generation III Pokémon (called Gen3Pokemon - don't tell Gen2Pokemon, but I think I like that name better). Now here's the cool part: the translation is done not directly between the data formats, but between the intermediary representations, and then the data are constructed from that. Translation code without this approach would mix loading data with converting it, all in the same place, which would look a bit like the following.

// Species index - bytes 32-34 in Gen 3, byte 0 in Gen 2.
littleEndian_insertShort(gen3Data, 32, gen2Data[0])

// Trainer ID - little-endian int at bytes 4-8 in gen 3,
// big-endian short at bytes 6-8 in Gen 2.
unsigned short trainerId = bigEndian_getShort(gen2Data, 6);  
unsigned int doubleTrainerId = (unsigned int) trainerId << 16 | trainerId;  
littleEndian_insertInt(gen3Data, 4, doubleTrainerId);

// Experience points - bytes 36-40 in Gen 3, bytes 8-10 in Gen 2.
unsigned int gen2Exp = gen2Data[8] << 16;  
gen2Exp |= bigEndian_getShort(gen2Data[9]);  
littleEndian_insertInt(gen3Data, 36, gen2Exp);  

With the aforementioned structure, however, the same translation code instead looks more like these few lines:

gen3Pokemon.speciesIndex = gen2Pokemon.speciesIndex;  
gen3Pokemon.trainerId = (unsigned int) gen2Pokemon.trainerId << 16 | gen2Pokemon.trainerId;  
gen3Pokemon.experiencePoints = gen2Pokemon.experiencePoints;  

Even without understanding everything that's going on here, it's quite plain to see that the second version is much easier to read. Because of this, separating parsing code and translation code is a fantastic idea. Plus, you don't want to have to think about exactly where things are in the data every time you want to do things with them.

Putting these thoughts on structure together, we've got a modular design for the code which mainly looks like this:

As it so happens, both the Gen II and III data modules are done. That does mean there's no translation yet - but loading and building Pokémon data are both in full order. Those two parts have in fact even been done for a few weeks prior to this entry.

The question now is if science has gone too far, and the answer is not yet. So... let's get to creating a Pokémon using these modules, shall we? This code right here does the trick.

#include "generations/gen3.h" // The Gen III parsing/building module

int main()  
{
    Gen3Pokemon mewtwo;

    mewtwo.speciesIndex = 150; // Mewtwo is #150.
    mewtwo.personality = 539895; // "MEWTWO" on a keypad.
    mewtwo.friendship = 0; // Mewtwo ain't friendly.
    mewtwo.item = 179; // BrightPowder, Mewtwo's signature item in Gen I.

    mewtwo.move1 = 94; // Psychic
    mewtwo.move2 = 165; // Struggle
    mewtwo.move3 = 150; // Splash
    mewtwo.move4 = 0;

    ...

    char data[100];
    buildGen3PokemonData(data, mewtwo);
}

The full file is available to download and read as you please. If you would like a glimpse at which stats need to be set in order to make a complete Pokémon, I'd recommend taking a look. Compiling this, running it, and inserting the data into the game, we're finally presented with the fruits of our labor: a typically surly Mewtwo.

Mewtwo's stats, part 1 Mewtwo's stats, part 2 Creating life itself? No big deal.

But, as Mewtwo himself puts it, this creature was "not born of Pokémon", it was created by human hands. And what we're going for isn't hacked Pokémon - rather, we're transferring legitimate Pokémon from one generation to another. Which means that, although this is good progress, there is still a long way to go. In addition, we haven't even touched on the code required to interface with the link cables in the entries so far, so there's a lot to look forward to!

If there's anything you'd like to talk about - perhaps you have a question, or you'd just like to tell me what you think of the project - you can write @obskyr on Twitter, or you can e-mail me. You can also comment on this devlog entry. In any case, I'll do my best to answer. Hope you've enjoyed this devlog entry - I'll see you in the next one!

  • Reddit
  • Tumblr
  • Pocket
Comments powered by Disqus