413 lines
9.6 KiB
Markdown
413 lines
9.6 KiB
Markdown
# Styleguide and Principles
|
|
|
|
## Naming Conventions
|
|
|
|
Function names and struct names should be formatted in `PascalCase`.
|
|
|
|
```c
|
|
void ThisIsCorrect(void);
|
|
|
|
struct MyStruct
|
|
{
|
|
u8 firstField;
|
|
u16 secondField;
|
|
...
|
|
};
|
|
```
|
|
|
|
Variables and struct fields should be formatted in `camelCase`.
|
|
|
|
```c
|
|
int thisIsCorrect = 0;
|
|
```
|
|
|
|
Global variables should be prefixed with `g`, and static variables should be
|
|
prefixed with `s`.
|
|
|
|
```c
|
|
extern s32 gMyGlobalVariable;
|
|
|
|
static u8 sMyStaticVariable = 0;
|
|
```
|
|
|
|
Macros and constants should use `CAPS_WITH_UNDERSCORES`.
|
|
|
|
```c
|
|
#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.
|
|
|
|
```c
|
|
/*
|
|
* 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 `//`.
|
|
|
|
```c
|
|
// 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.
|
|
```c
|
|
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 `=`.
|
|
|
|
```c
|
|
int i = 0; // correct
|
|
int i=0; // incorrect
|
|
|
|
a > b // correct
|
|
a>b // incorrect
|
|
```
|
|
|
|
The incrementor and decrementor operators should NOT have a space.
|
|
|
|
```c
|
|
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.
|
|
|
|
```c
|
|
for (...)
|
|
{
|
|
// correct
|
|
}
|
|
|
|
for(...) {
|
|
// incorrect
|
|
}
|
|
```
|
|
|
|
A `switch` statement's cases should left-align with the `switch`'s block.
|
|
|
|
```c
|
|
switch (foo)
|
|
{
|
|
case 0: // correct
|
|
...
|
|
break;
|
|
}
|
|
|
|
switch (foo)
|
|
{
|
|
case 0: // incorrect
|
|
...
|
|
break;
|
|
}
|
|
```
|
|
|
|
A single empty line should follow a block.
|
|
|
|
```c
|
|
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.
|
|
|
|
```c
|
|
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.
|
|
|
|
```c
|
|
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.
|
|
|
|
```c
|
|
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.
|
|
```c
|
|
void SetCurrentDifficultyLevel(enum DifficultyLevel desiredDifficulty)
|
|
{
|
|
#ifdef B_VAR_DIFFICULTY
|
|
return; // Incorrect
|
|
#endif
|
|
|
|
if (desiredDifficulty > DIFFICULTY_MAX)
|
|
desiredDifficulty = DIFFICULTY_MAX;
|
|
|
|
VarSet(B_VAR_DIFFICULTY, desiredDifficulty);
|
|
}
|
|
```
|
|
```c
|
|
void SetCurrentDifficultyLevel(enum DifficultyLevel desiredDifficulty)
|
|
{
|
|
if (!B_VAR_DIFFICULTY) // Correct
|
|
return;
|
|
|
|
if (desiredDifficulty > DIFFICULTY_MAX)
|
|
desiredDifficulty = DIFFICULTY_MAX;
|
|
|
|
VarSet(B_VAR_DIFFICULTY, desiredDifficulty);
|
|
}
|
|
```
|
|
```c
|
|
[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.
|
|
```C
|
|
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.
|
|
|
|
```c
|
|
// Incorrect
|
|
if (gimmick == 5 && mon->teraType != 0)
|
|
return TRUE;
|
|
if (gimmick == 4 && mon->shouldUseDynamax)
|
|
return TRUE;
|
|
```
|
|
|
|
```c
|
|
// 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.
|
|
|
|
```c
|
|
//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.
|
|
|
|
```c
|
|
// 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);
|
|
}
|
|
```
|
|
|
|
```c
|
|
//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`](https://patch-diff.githubusercontent.com/raw/rh-hideout/pokeemerald-expansion/pull/5337.diff) 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.
|
|
|
|
```c
|
|
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](https://discord.com/channels/419213663107416084/1108733346864963746) 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](https://discord.com/channels/419213663107416084/1202774957776441427) together as possible, and increment the major version number of the project.
|
|
|
|
# Attribution
|
|
* The majority of the styleguide was written by [garakmon](https://github.com/garakmon) as part of their [PR to pokefirered](<https://github.com/pret/pokefirered/pull/63>).
|