pokeemmo/docs/STYLEGUIDE.md

404 lines
9.4 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,
},
```
## 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>).