Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Styleguide and Principles

Naming Conventions

Function names and struct names should be formatted in PascalCase.

void ThisIsCorrect(void);

struct MyStruct
{
    u8 firstField;
    u16 secondField;
    ...
};

Variables and struct fields should be formatted in camelCase.

int thisIsCorrect = 0;

Global variables should be prefixed with g, and static variables should be prefixed with s.

extern s32 gMyGlobalVariable;

static u8 sMyStaticVariable = 0;

Macros and constants should use CAPS_WITH_UNDERSCORES.

#define MAX_LEVEL 100

enum
{
    COLOR_RED,
    COLOR_BLUE,
    COLOR_GREEN,
};

#define ADD_FIVE(x) ((x) + 5)

Coding Style

Comments

Ideally, contributions have descriptive variable, function and constant names so as to explain functionality without comments. When a comment is used, the content of the comment should explain WHY a specific system or component works the way it does.

When describing a system/component in-depth, use block comment syntax.

/*
 * This is an in-depth description of the save block format. Its format is as follows:
 *
 * Sectors  0 - 13: Save Slot 1
 * Sectors 14 - 27: Save Slot 2
 * ...
 */

When briefly describing a function or block of code, use a single-line comments placed on its own line. There should be a single space directly to the right of //.

// This is supplemental information for the function. If there is a bunch of info, it should
// carry on to the next line.
void ProcessSingleTask(void)
{
   // Short comment describing some noteworthy aspect of the code immediately following.
   ...
   // Comments should be capitalized and end in a period.
}

When tagging a data structure that corresponds to an enum or some noteworthy value, place the comment on the same line as the code.

const u8 gPlantlikeMons[] =
{
    FALSE, // SPECIES_BULBASAUR
    FALSE, // SPECIES_IVYSAUR
    TRUE,  // SPECIES_VENUSAUR
    FALSE, // SPECIES_CHARMANDER
    ...
};

Whitespace

All .c and .h files should use 4 spaces–not tabs. Assembler files (.s) use tabs. Script files (.inc) use tabs.

Operators

Assignments and comparison operators should have one space on both sides of =.

int i = 0; // correct
int i=0;   // incorrect

a > b // correct
a>b   // incorrect

The incrementor and decrementor operators should NOT have a space.

i++;  // correct
i ++; // incorrect

A control statement should have a space between them and their expressions, and the opening bracket should be on the next line.

for (...)
{
    // correct
}

for(...) {
    // incorrect
}

A switch statement’s cases should left-align with the switch’s block.

switch (foo)
{
case 0: // correct
    ...
    break;
}

switch (foo)
{
    case 0: // incorrect
        ...
        break;
}

A single empty line should follow a block.

int MyFunction(int bar)
{
    int foo = 0;
    if (bar)
        foo++;

    return foo; // correct
}

int MyFunction(int bar)
{
    int foo = 0;
    if (bar)
        foo++;
    return foo; // incorrect
}

A chain of if-else statements in which any block is more than one line of code should use braces. If all blocks are single-line, then no braces are necessary.

if (foo) // correct
{
    return 1;
}
else
{
    MyFunction();
    return 0;
}

if (foo) // incorrect
    return 1;
else
{
    MyFunction();
    return 0;
}

Control Structures

When comparing whether or not a value equals 0, don’t be explicit unless the situation calls for it.

if (runTasks) // correct
    RunTasks();

if (runTasks != 0) // incorrect
    RunTasks();

if (!PlayerIsOutside()) // correct
    RemoveSunglasses();

if (PlayerIsOutside() == 0) // incorrect
    RemoveSunglasses();

When writing a for or while loop with no body, use a semicolon ; on the same line, rather than empty braces.

for (i = 0; gParty[i].species != SPECIES_NONE; i++); // correct

for (i = 0; gParty[i].species != SPECIES_NONE; i++) // incorrect
{ }

Inline Configs

When adding functionality that is controlled by a config, defines should be checked within the normal control flow of the function unless a data structure requires a change at runtime.

void SetCurrentDifficultyLevel(enum DifficultyLevel desiredDifficulty)
{
#ifdef B_VAR_DIFFICULTY
    return; // Incorrect
#endif

    if (desiredDifficulty > DIFFICULTY_MAX)
        desiredDifficulty = DIFFICULTY_MAX;

    VarSet(B_VAR_DIFFICULTY, desiredDifficulty);
}
void SetCurrentDifficultyLevel(enum DifficultyLevel desiredDifficulty)
{
    if (!B_VAR_DIFFICULTY) // Correct
        return;

    if (desiredDifficulty > DIFFICULTY_MAX)
        desiredDifficulty = DIFFICULTY_MAX;

    VarSet(B_VAR_DIFFICULTY, desiredDifficulty);
}
    [MOVE_VINE_WHIP] =
    {
        .name = COMPOUND_STRING("Vine Whip"),
        .description = COMPOUND_STRING(
            "Strikes the foe with\n"
            "slender, whiplike vines."),
        #if B_UPDATED_MOVE_DATA >= GEN_6 // Correct
            .pp = 25,
        #elif B_UPDATED_MOVE_DATA >= GEN_4
            .pp = 15,
        #else
            .pp = 10,
        #endif
        .effect = EFFECT_HIT,
        .power = B_UPDATED_MOVE_DATA >= GEN_6 ? 45 : 35,
    },

Variable Declarations

Loop iterators should be declared as part of the loop unless there’s a very good reason not to.

for (u32 i = 0; i < LOOP_ITERATIONS; i++)
{
    dst1[i] = i;
    dst2[i] = i;
}

Data Type Sizes

When a variable number is used, the data type should generally u32 (unsigned) or s32 (signed). There are a few exceptions to this rule, such as:

  • Values stored in the saveblock should use the smallest data type possible.
  • EWRAM variables should use the smallest data type possible.
  • Global variables / global struct members use the smallest data type possible.

Constants, Enums and Type Checking

Avoid using magic numbers when possible - constants help to make clear why a specific value is used.

// Incorrect
        if (gimmick == 5 && mon->teraType != 0)
            return TRUE;
        if (gimmick == 4 && mon->shouldUseDynamax)
            return TRUE;
// Correct
#define TYPE_NONE 0
#define GIMMICK_DYNAMAX 4
#define GIMMICK_TERA 5

        if (gimmick == GIMMICK_TERA && mon->teraType != TYPE_NONE)
            return TRUE;
        if (gimmick == GIMMICK_DYNAMAX && mon->shouldUseDynamax)
            return TRUE;

When several numbers in sequence are used AND those values are not utilized in the saveblock, an enum is used instead.

//Correct
enum Gimmick
{
    GIMMICK_NONE,
    GIMMICK_MEGA,
    GIMMICK_ULTRA_BURST,
    GIMMICK_Z_MOVE,
    GIMMICK_DYNAMAX,
    GIMMICK_TERA,
    GIMMICKS_COUNT,
};

        if (gimmick == GIMMICK_TERA && mon->teraType != TYPE_NONE)
            return TRUE;
        if (gimmick == GIMMICK_DYNAMAX && mon->shouldUseDynamax)
            return TRUE;

When an enum is used, the enum type is used instead of a regular number type to prevent incorrectly set values.

// Incorrect
bool32 CanActivateGimmick(u32 battler, u32 gimmick)
{
    return gGimmicksInfo[gimmick].CanActivate != NULL && gGimmicksInfo[gimmick].CanActivate(battler);
}

u32 GetCurrentDifficultyLevel(void)
{
    if (!B_VAR_DIFFICULTY)
        return DIFFICULTY_NORMAL;

    return VarGet(B_VAR_DIFFICULTY);
}
//Correct

bool32 CanActivateGimmick(u32 battler, enum Gimmick gimmick)
{
    return gGimmicksInfo[gimmick].CanActivate != NULL && gGimmicksInfo[gimmick].CanActivate(battler);
}

enum DifficultyLevel GetCurrentDifficultyLevel(void)
{
    if (!B_VAR_DIFFICULTY)
        return DIFFICULTY_NORMAL;

    return VarGet(B_VAR_DIFFICULTY);
}

Data file format

External data files should use JSON.

Principles

Minimally Invasive

New functionality must be as minimally invasive to existing files as possible. When a large amount of new code is introduced, it is best to isolate it in its own file.

The B_VAR_DIFFICULTY pull request is a good example of lots of new code being introduced in minimally invasive ways.

UNUSED

If a function or data is introduced but is never called, it is designated as UNUSED. UNUSED functions should not be introduced unless neccesary.

static void UNUSED PadString(const u8 *src, u8 *dst)
{
    u32 i;

    for (i = 0; i < 17 && src[i] != EOS; i++)
        dst[i] = src[i];

    for (; i < 17; i++)
        dst[i] = CHAR_SPACE;

    dst[i] = EOS;
}

Config Philosophy

If a branch can modifies saves, the functionality that does so must be gated behind a config, and off by default.

If a branch has a config that performs either of the following, it should be on by default:

  • improves the backend / developer quality of life
  • emulates present day, modern day Pokémon

If a branch’s behavior is one that Game Freak does not have a consistent stance on, the default behavior of the config should be disussed by the maintainers.

All other configs should be off.

Save Philosophy

Until save migration is implemented, branches will only merged in if they do not forcefully break existing game saves.

When pokemeerald-expansion gets to a point where new functionality will require that we break saves, we will merge as many save-breaking features together as possible, and increment the major version number of the project.

Attribution