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.
EWRAMvariables 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
- The majority of the styleguide was written by garakmon as part of their PR to pokefirered.