-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Adding a Pokémon Type & Disabling the Mystery Type
In this tutorial, we will cover how to add a new Pokémon Type to pokeemerald, as well as how to disable the ??? Type used by Curse. This tutorial does overlap with the tutorial on adding the Physical/Special split from Generation IV due to the nature of how the game determines that split.
For this tutorial, we will be adding the Fairy type from Generation VI, and for example purposes, this tutorial will use the Fairy icon from the pokeemerald-expansion build. For reference, the Type icons used in the game are 32x16 pixels in png format, with a color index of 8 colors.
To start, place your new Type icon inside the "/graphics/types" folder. Open up your command terminal (what you used to install your build onto your computer), and use the command touch followed by the exact path to that Type icon (e.g. /mnt/c/Users/[You]/Desktop/pokeemerald/graphics/types/Fairy.png). This will update the timestamp on the file, which will have the pokeemerald makefile re-implement the image into the game when the make command is run. You should run the touch command each time you make an edit to the icon, to ensure it's updated. If it isn't, run the make clean command.
Before moving on, open the file "graphics_file_rules.mk", and scroll down to line 24, which reads:
types := normal fight flying poison ground rock bug ghost steel mystery fire water grass electric psychic ice dragon dark
This file determines what graphics the makefile will recognise when the make command is run, while this line determines which Type icons are made. So we'll add our Type onto the end so the file will make a place.
+ types := normal fight flying poison ground rock bug ghost steel mystery fire water grass electric psychic ice dragon dark fairy
We start in "/include/constants/pokemon.h", at the list starting on line 4. This list defines every Type in the game, so we're going to add our Type to the list (and make sure that NUMBER_OF_MON_TYPES is increased by the number of Types added, in this case 1). NOTE: If you won't be implementing the Physical/Special split code to your build, it is important to place your Type before TYPE_MYSTERY if that Type should be comprised of Physical moves, or after if should be Special. For this tutorial, we'll just place Fairy at the end of the list, ignoring Physical and Special status.
// Pokemon types
#define TYPE_NONE 255
#define TYPE_NORMAL 0
#define TYPE_FIGHTING 1
#define TYPE_FLYING 2
...
#define TYPE_DARK 17
+#define TYPE_FAIRY 18
-#define NUMBER_OF_MON_TYPES 18
+#define NUMBER_OF_MON_TYPES 19
We need to add our new Type to all the locations that the game accesses the Type table for, so we'll start inside "/src/battle_main.c" at the list that starts at line 435. This list shows the Type's name as it appears in-game, so we'll append our Type to the end.
NOTE: Make sure that the number of characters in the name is 6 characters or less!
...
const u8 gTypeNames[NUMBER_OF_MON_TYPES][TYPE_NAME_LENGTH + 1] =
{
[TYPE_NORMAL] = _("NORMAL"),
[TYPE_FIGHTING] = _("FIGHT"),
[TYPE_FLYING] = _("FLYING"),
...
[TYPE_DRAGON] = _("DRAGON"),
[TYPE_DARK] = _("DARK"),
+ [TYPE_FAIRY] = _("FAIRY"),
Next, we move to "src/pokedex.c", where we'll append our Type to the two lists that start at lines 1376 and 1411. This will allow the player to choose to sort their Pokédex by our new Type.
static const struct SearchOptionText sDexSearchTypeOptions[NUMBER_OF_MON_TYPES + 1] = // + 2 for "None" and terminator, - 1 for Mystery
{
{gText_DexEmptyString, gText_DexSearchTypeNone},
{gText_DexEmptyString, gTypeNames[TYPE_NORMAL]},
{gText_DexEmptyString, gTypeNames[TYPE_FIGHTING]},
{gText_DexEmptyString, gTypeNames[TYPE_FLYING]},
...
{gText_DexEmptyString, gTypeNames[TYPE_DRAGON]},
{gText_DexEmptyString, gTypeNames[TYPE_DARK]},
+ {gText_DexEmptyString, gTypeNames[TYPE_FAIRY]},
{},
};
static const u8 sDexSearchTypeIds[NUMBER_OF_MON_TYPES] =
{
TYPE_NONE,
TYPE_NORMAL,
TYPE_FIGHTING,
TYPE_FLYING,
...
TYPE_ICE,
TYPE_DRAGON,
TYPE_DARK,
+ TYPE_FAIRY,
};
And another addition to the list at line 1327 in "src/battle_messages.c" - NOTE: The number at the top of this code has increased by 1 here in comparison to the base code. This number must increase by however many lines are added.
static const u8 sATypeMove_Table[NUMBER_OF_MON_TYPES][18] =
{
[TYPE_NORMAL] = _("a NORMAL move"),
[TYPE_FIGHTING] = _("a FIGHTING move"),
[TYPE_FLYING] = _("a FLYING move"),
...
[TYPE_ICE] = _("an ICE move"),
[TYPE_DRAGON] = _("a DRAGON move"),
[TYPE_DARK] = _("a DARK move")
+ [TYPE_FAIRY] = _("a FAIRY move")
};
Finally, we'll add our type to "src/pokemon_summary_screen", so that our Type icon appears on the summary screen. These lists start from lines 750, 846, and 889.
static const union AnimCmd sSpriteAnim_TypeNormal[] = {
ANIMCMD_FRAME(TYPE_NORMAL * 8, 0, FALSE, FALSE),
ANIMCMD_END
};
static const union AnimCmd sSpriteAnim_TypeFighting[] = {
ANIMCMD_FRAME(TYPE_FIGHTING * 8, 0, FALSE, FALSE),
ANIMCMD_END
};
...
static const union AnimCmd sSpriteAnim_TypeDark[] = {
ANIMCMD_FRAME(TYPE_DARK * 8, 0, FALSE, FALSE),
ANIMCMD_END
};
+static const union AnimCmd sSpriteAnim_TypeFairy[] = {
+ ANIMCMD_FRAME(TYPE_FAIRY * 8, 0, FALSE, FALSE),
+ ANIMCMD_END
};
};
static const union AnimCmd *const sSpriteAnimTable_MoveTypes[NUMBER_OF_MON_TYPES + CONTEST_CATEGORIES_COUNT] = {
sSpriteAnim_TypeNormal,
sSpriteAnim_TypeFighting,
sSpriteAnim_TypeFlying,
...
sSpriteAnim_TypeDragon,
sSpriteAnim_TypeDark,
+ sSpriteAnim_TypeFairy,
sSpriteAnim_CategoryCool,
sSpriteAnim_CategoryBeauty,
...
static const u8 sMoveTypeToOamPaletteNum[NUMBER_OF_MON_TYPES + CONTEST_CATEGORIES_COUNT] =
{
[TYPE_NORMAL] = 13,
[TYPE_FIGHTING] = 13,
[TYPE_FLYING] = 14,
...
[TYPE_DRAGON] = 15,
[TYPE_DARK] = 13,
+ [TYPE_FAIRY] = 14,
[NUMBER_OF_MON_TYPES + CONTEST_CATEGORY_COOL] = 13,
[NUMBER_OF_MON_TYPES + CONTEST_CATEGORY_BEAUTY] = 14,
...
So, now that we've dealt with defining the Type, it needs actual data to be used in battle. First, we'll add some Type match-ups, located at "/src/battle_main.c" starting at line 318.
const u8 gTypeEffectiveness[339] =
{
TYPE_NORMAL, TYPE_ROCK, TYPE_MUL_NOT_EFFECTIVE,
TYPE_NORMAL, TYPE_STEEL, TYPE_MUL_NOT_EFFECTIVE,
TYPE_FIRE, TYPE_FIRE, TYPE_MUL_NOT_EFFECTIVE,
TYPE_FIRE, TYPE_WATER, TYPE_MUL_NOT_EFFECTIVE,
TYPE_FIRE, TYPE_GRASS, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_FIRE, TYPE_ICE, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_FIRE, TYPE_BUG, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_FIRE, TYPE_ROCK, TYPE_MUL_NOT_EFFECTIVE,
Each line is a separate Type match-up that has 3 values, written in the format "[Attacking Move Type], [Defending Pokemon Type], [Effectiveness]". So from the first line, we see that an attacking Normal move against a defending Rock Pokémon will not be effective. For our Type, we'll add that Fairy moves are Super Effective against Dragon Pokémon, which we'll append near the end of the list.
TYPE_STEEL, TYPE_FIRE, TYPE_MUL_NOT_EFFECTIVE,
TYPE_STEEL, TYPE_WATER, TYPE_MUL_NOT_EFFECTIVE,
TYPE_STEEL, TYPE_ELECTRIC, TYPE_MUL_NOT_EFFECTIVE,
TYPE_STEEL, TYPE_ICE, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_STEEL, TYPE_ROCK, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_STEEL, TYPE_STEEL, TYPE_MUL_NOT_EFFECTIVE,
+ TYPE_FAIRY, TYPE_DRAGON, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_FORESIGHT, TYPE_FORESIGHT, TYPE_MUL_NO_EFFECT,
TYPE_NORMAL, TYPE_GHOST, TYPE_MUL_NO_EFFECT,
TYPE_FIGHTING, TYPE_GHOST, TYPE_MUL_NO_EFFECT,
TYPE_ENDTABLE, TYPE_ENDTABLE, TYPE_MUL_NO_EFFECT
};
NOTE: Whenever Type match-ups are edited, the number at the top of this list (see below)
const u8 gTypeEffectiveness[336] =
needs to be updated to reflect the additional values added to the list. Each line has 3 values, so this number in essence needs to be the number of lines in the list x 3. We've added 1 line, so we'll increase this number by 3.
+ const u8 gTypeEffectiveness[339] =
We'll make this same change to line 90 of "/include/battle_main.h". This line defines the list we just edited in the game's code, so this number needs to be the same or else the code won't run.
+ extern const u8 gTypeEffectiveness[339];
With that, a new Type has been successfully added to the game. This Type can now be added to moves and Pokémon via "/src/data/battle_moves.h" and "/src/data/pokemon/base_stats.h" as desired. For this tutorial, we'll make Treecko a Fairy/Grass-Type Pokémon, Zigzagoon a Dragon-Type Pokémon, and make Pound a Fairy move.
[SPECIES_TREECKO] =
{
.baseHP = 40,
.baseAttack = 45,
.baseDefense = 35,
.baseSpeed = 70,
.baseSpAttack = 65,
.baseSpDefense = 55,
.type1 = TYPE_GRASS,
- .type2 = TYPE_GRASS,
+ .type2 = TYPE_FAIRY,
.catchRate = 45,
.expYield = 65,
.evYield_HP = 0,
.evYield_Attack = 0,
.evYield_Defense = 0,
.evYield_Speed = 1,
.evYield_SpAttack = 0,
.evYield_SpDefense = 0,
.itemCommon = ITEM_NONE,
.itemRare = ITEM_NONE,
.genderRatio = PERCENT_FEMALE(12.5),
.eggCycles = 20,
.friendship = 70,
.growthRate = GROWTH_MEDIUM_SLOW,
.eggGroup1 = EGG_GROUP_MONSTER,
.eggGroup2 = EGG_GROUP_DRAGON,
.abilities = {ABILITY_OVERGROW, ABILITY_NONE},
.safariZoneFleeRate = 0,
.bodyColor = BODY_COLOR_GREEN,
.noFlip = FALSE,
},
[MOVE_POUND] =
{
.effect = EFFECT_HIT,
.power = 40,
- .type = TYPE_NORMAL,
+ .type = TYPE_FAIRY,
.accuracy = 100,
.pp = 35,
.secondaryEffectChance = 0,
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.flags = FLAG_MAKES_CONTACT | FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED,
},
The ??? Type, referenced in the code as TYPE_MYSTERY, was a Pokémon Type that existed in the games up until Generation V. Disabling it in Generation III is a simple task, but how to do so is up to you.
The first step starts in "/include/constants/pokemon.h", and there are two choices:
// Pokemon types
#define TYPE_NONE 255
#define TYPE_NORMAL 0
#define TYPE_FIGHTING 1
#define TYPE_FLYING 2
#define TYPE_POISON 3
#define TYPE_GROUND 4
#define TYPE_ROCK 5
#define TYPE_BUG 6
#define TYPE_GHOST 7
#define TYPE_STEEL 8
-#define TYPE_MYSTERY 9
+#define TYPE_FIRE 9
+#define TYPE_WATER 10
+#define TYPE_GRASS 11
+#define TYPE_ELECTRIC 12
+#define TYPE_PSYCHIC 13
+#define TYPE_ICE 14
+#define TYPE_DRAGON 15
+#define TYPE_DARK 16
+#define NUMBER_OF_MON_TYPES 17
- Removing the Type entirely, in which case NUMBER_OF_MON_TYPES should be reduced appropriately.
// Pokemon types
#define TYPE_NONE 255
#define TYPE_NORMAL 0
#define TYPE_FIGHTING 1
#define TYPE_FLYING 2
#define TYPE_POISON 3
#define TYPE_GROUND 4
#define TYPE_ROCK 5
#define TYPE_BUG 6
#define TYPE_GHOST 7
#define TYPE_STEEL 8
-#define TYPE_MYSTERY 9
+#define TYPE_FIRE 9
+#define TYPE_WATER 10
+#define TYPE_GRASS 11
+#define TYPE_ELECTRIC 12
+#define TYPE_PSYCHIC 13
+#define TYPE_ICE 14
+#define TYPE_DRAGON 15
+#define TYPE_DARK 16
+#define TYPE_FAIRY 17
+#define NUMBER_OF_MON_TYPES 18
Or 2) replace TYPE_MYSTERY with a new Type entirely and keeping NUMBER_OF_MON_TYPES the same, in which case this part of the tutorial can be used in tandem with the first part.
#define IS_TYPE_PHYSICAL(moveType)(moveType < TYPE_MYSTERY)
#define IS_TYPE_SPECIAL(moveType)(moveType > TYPE_MYSTERY)
However, the distinction between Physical and Special moves in this generation depends on TYPE_MYSTERY (line 56 of "/include/battle.h"). More specifically, by the above code, the game checks to see whether the Type of a move is before or after the index number of TYPE_MYSTERY in the list of Types. Because we've disabled it, the game currently can't make moves Physical or Special. This can be fixed by adding the Physical/Special split, but if you aren't adding that code, you can instead change the goalposts.
#define IS_TYPE_PHYSICAL(moveType)(moveType < TYPE_FIRE)
#define IS_TYPE_SPECIAL(moveType)(moveType > TYPE_STEEL)
Now the game defines anything before TYPE_FIRE as Physical, and anything after TYPE_STEEL as Special, which accomplishes the same goal.
Now that we've undefined TYPE_MYSTERY, we just need to remove the instances where it appears in the game's code. First, we'll edit the move Curse so that it's always a Ghost-Type move, as it became from Generation V onwards.
[MOVE_CURSE] =
{
.effect = EFFECT_CURSE,
.power = 0,
- .type = TYPE_MYSTERY,
+ .type = TYPE_GHOST,
.accuracy = 0,
.pp = 10,
.secondaryEffectChance = 0,
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.flags = 0,
},
Next, the following references can be outright deleted from the following locations:
- [TYPE_MYSTERY] = _("???"),
The list starting at line 434 of "/src/battle_main.c" (can be replaced with another Type if desired)
+static const u8 sATypeMove_Table[NUMBER_OF_MON_TYPES][16] =
{
...
- [TYPE_MYSTERY] = _("a ??? move"),
The list starting at line 1327 of "/src/battle_message.c" (can be replaced with another Type if desired) - NOTE: The number at the top has been decreased by 1 compared to the base code. This number must be decreased by 1 if you remove TYPE_MYSTERY, or replace it with another Type.
-static const union AnimCmd sSpriteAnim_TypeMystery[] = {
- ANIMCMD_FRAME(TYPE_MYSTERY * 8, 0, FALSE, FALSE),
- ANIMCMD_END
};
- sSpriteAnim_TypeMystery,
- [TYPE_MYSTERY] = 15,
The three lists starting at lines 750, 842, and 884 of "/src/battle_main.c" (can be replaced with another Type if desired)
if (summary->isEgg)
{
- SetTypeSpritePosAndPal(TYPE_MYSTERY, 120, 48, SPRITE_ARR_ID_TYPE);
SetSpriteInvisibility(SPRITE_ARR_ID_TYPE + 1, TRUE);
}
Line 3786 of "src/pokemon_summary_screen.c", which prints the ??? icon from appearing on the Egg summary screen. Alternatively, you can replace TYPE_MYSTERY with TYPE_NORMAL to have Eggs be shown as Normal type.
if (moveType == TYPE_MYSTERY)
{
if (IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST))
moveType = TYPE_GHOST;
else
moveType = TYPE_NORMAL;
}
And lastly, two instances of this snippet of code at lines 7318 and 7345 in "/src/battle_script_commands.c". This code was used to change the Type of the move Curse depending on whether the user's Type is Ghost Pokémon, but now since Curse is always a Ghost move, it is no longer needed.
static void Cmd_hiddenpowercalc(void)
{
...
gBattleStruct->dynamicMoveType = ((NUMBER_OF_MON_TYPES - 3) * typeBits) / 63 + 1;
if (gBattleStruct->dynamicMoveType >= TYPE_MYSTERY)
gBattleStruct->dynamicMoveType++;
gBattleStruct->dynamicMoveType |= F_DYNAMIC_TYPE_1 | F_DYNAMIC_TYPE_2;
gBattlescriptCurrInstr++;
}
Finally, there is one more code that relies on TYPE_MYSTERY, being the code that determines the base power and Type of the move Hidden Power, at line 8775 of "src/battle_script_commands.c". Hidden Power is currently programmed to never be Normal or ??? Type. To reduce the scope so that it only excludes Normal, we'll change this code:
- gBattleStruct->dynamicMoveType = ((NUMBER_OF_MON_TYPES - 3) * typeBits) / 63 + 1;
- if (gBattleStruct->dynamicMoveType >= TYPE_MYSTERY)
- gBattleStruct->dynamicMoveType++;
+ gBattleStruct->dynamicMoveType = ((NUMBER_OF_MON_TYPES - 2) * typeBits) / 63 + 1;
gBattleStruct->dynamicMoveType |= F_DYNAMIC_TYPE_1 | F_DYNAMIC_TYPE_2;
gBattlescriptCurrInstr++;
And that's the end of the tutorial. Now you can apply this to add whatever Types to the game you want. (NOTE: As mentioned above, Zigzagoon was changed to a Dragon-Type Pokémon for demonstration purposes.)