GET_BASE_SPECIES_ID -> GetBaseSpeciesId

This commit is contained in:
Eduardo Quezada 2026-03-21 09:40:48 -03:00
parent 93314a0918
commit 64ade164bd
14 changed files with 94 additions and 70 deletions

View File

@ -14,30 +14,42 @@ The main things that the Expansion changes are listed here.
* Most tables that use `SPECIES_x` as indexes have been moved to `gSpeciesInfo`.
# Content
* [Useful resources](#useful-resources)
* [The Data - Part 1](#the-data---part-1)
* [1. Declare a species constant](#1-Declare-a-species-constant)
* [2. `SpeciesInfo`'s structure](#2-speciesinfos-structure)
* [3. Define its basic species information](#3-define-its-basic-species-information)
* [4. Species Name](#4-species-name)
* [5. Define its cry](#5-define-its-cry)
* [6. Define its Pokédex entry](#6-define-its-pokédex-entry)
* [The Graphics](#the-graphics)
* [1. Edit the sprites](#1-edit-the-sprites)
* [2. Add the sprites to the rom](#2-add-the-sprites-to-the-rom)
* [3. Add the animations to the rom](#3-add-the-animations-to-the-rom)
* [4. Linking graphic information to our Pokémon](#4-linking-graphic-information-to-our-pokémon)
* [The Data - Part 2](#the-data---part-2)
* [1. Species Flags](#1-species-flags)
* [2. Delimit the moveset](#2-delimit-the-moveset)
* [3. Define the Evolutions](#3-define-the-evolutions)
* [4. Make it appear!](#4-make-it-appear)
* [Optional data](#optional-data)
* [1. Form tables](#1-form-tables)
* [2. Form change tables](#2-form-change-tables)
* [3. Gender differences](#3-gender-differences)
* [4. Overworld Data (v1.9 onwards)](#4-overworld-data-v19-onwards)
* [5. In-battle shadows (v1.10 onwards)](#5-in-battle-shadows-v110-onwards)
- [Changes compared to vanilla](#changes-compared-to-vanilla)
- [Content](#content)
- [Useful resources](#useful-resources)
- [The Data - Part 1](#the-data---part-1)
- [1. Declare a species constant](#1-declare-a-species-constant)
- [2. `SpeciesInfo`'s structure](#2-speciesinfos-structure)
- [3. Define its basic species information](#3-define-its-basic-species-information)
- [4. Species Name](#4-species-name)
- [5. Define its cry](#5-define-its-cry)
- [6. Define its Pokédex entry](#6-define-its-pokédex-entry)
- [The Graphics](#the-graphics)
- [1. Edit the sprites](#1-edit-the-sprites)
- [2. Add the sprites to the rom](#2-add-the-sprites-to-the-rom)
- [3. Add the animations to the rom](#3-add-the-animations-to-the-rom)
- [Version 1.11.0 or later](#version-1110-or-later)
- [Version 1.10.3 or earlier](#version-1103-or-earlier)
- [4. Linking graphic information to our Pokémon](#4-linking-graphic-information-to-our-pokémon)
- [The Data - Part 2](#the-data---part-2)
- [1. Species Flags](#1-species-flags)
- [2. Delimit the moveset](#2-delimit-the-moveset)
- [3. Define the Evolutions](#3-define-the-evolutions)
- [4. Make it appear!](#4-make-it-appear)
- [Optional data](#optional-data)
- [1. Form tables](#1-form-tables)
- [2. Form change tables](#2-form-change-tables)
- [3. Gender differences](#3-gender-differences)
- [4. Overworld Data (v1.9 onwards)](#4-overworld-data-v19-onwards)
- [Sprite Size](#sprite-size)
- [Shadows](#shadows)
- [Tracks](#tracks)
- [Asymmetric sprites (Version 1.10.0 onwards)](#asymmetric-sprites-version-1100-onwards)
- [Version 1.11.0 onwards](#version-1110-onwards)
- [Version 1.10.x](#version-110x)
- [How to add the Pokémon Object Events to map](#how-to-add-the-pokémon-object-events-to-map)
- [5. In-battle shadows (v1.10 onwards)](#5-in-battle-shadows-v110-onwards)
- [6. Limiting species allowed as followers](#6-limiting-species-allowed-as-followers)
# Useful resources
You can open a sprite debug menu by pressing `Select` in a Pokémon's summary screen outside of battle.
@ -1003,10 +1015,10 @@ For example, in the HGSS dex, it lets us browse between the entries of every for
![hgssdex1](img/add_pokemon/hgssdex1.png) ![image](img/add_pokemon/hgssdex2.png)
In addition, we have the `GET_BASE_SPECIES_ID` macro, which returns the first entry of the table (or return the species itself if it doesn't have a table registered). With this, you can check if a Pokémon is any form of a species. For example, making it so that the Light Ball affects all Pikachu forms:
In addition, we have the `GetBaseSpeciesId` function, which returns the first entry of the table (or return the species itself if it doesn't have a table registered). With this, you can check if a Pokémon is any form of a species. For example, making it so that the Light Ball affects all Pikachu forms:
```c
case HOLD_EFFECT_LIGHT_BALL:
if (GET_BASE_SPECIES_ID(gBattleMons[battlerAtk].species) == SPECIES_PIKACHU && IS_MOVE_SPECIAL(move))
if (GetBaseSpeciesId(gBattleMons[battlerAtk].species) == SPECIES_PIKACHU && IS_MOVE_SPECIAL(move))
modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0));
break;
```

View File

@ -423,5 +423,6 @@ void SetOrClearRageVolatile(void);
enum BattlerId GetTargetBySlot(enum BattlerId battlerAtk, enum BattlerId battlerDef);
bool32 IsNaturalEnemy(enum Species speciesAttacker, enum Species speciesTarget);
enum Stat GetDownloadStat(enum BattlerId battler);
enum Species GetBattlerBaseSpecies(enum BattlerId battler);
#endif // GUARD_BATTLE_UTIL_H

View File

@ -16,7 +16,6 @@
#include "contest_effect.h"
#include "constants/trainers.h"
#define GET_BASE_SPECIES_ID(speciesId) (GetFormSpeciesId(speciesId, 0))
#define FORM_SPECIES_END (0xffff)
// Property labels for Get(Box)MonData / Set(Box)MonData
@ -733,7 +732,6 @@ bool8 HasTwoFramesAnimation(enum Species species);
struct MonSpritesGfxManager *CreateMonSpritesGfxManager(u8 managerId, u8 mode);
void DestroyMonSpritesGfxManager(u8 managerId);
u8 *MonSpritesGfxManager_GetSpritePtr(u8 managerId, u8 spriteNum);
enum Species GetFormSpeciesId(enum Species speciesId, u8 formId);
u8 GetFormIdFromFormSpeciesId(enum Species formSpeciesId);
enum Species GetFormChangeTargetSpecies_Internal(struct FormChangeContext ctx);
bool32 DoesSpeciesHaveFormChangeMethod(enum Species species, enum FormChanges method);

View File

@ -786,6 +786,20 @@ static inline const enum Species *GetSpeciesFormTable(enum Species species)
return formTable;
}
static inline enum Species GetFormSpeciesId(enum Species species, u8 formId)
{
const enum Species *formTable = GetSpeciesFormTable(species);
if (formTable != NULL)
return formTable[formId];
else
return species;
}
static inline enum Species GetBaseSpeciesId(enum Species species)
{
return GetFormSpeciesId(species, 0);
}
static inline const struct FormChange *GetSpeciesFormChanges(enum Species species)
{
const struct FormChange *formChanges = gSpeciesInfo[SanitizeSpeciesId(species)].formChangeTable;

View File

@ -90,9 +90,10 @@ bool32 CanDynamax(enum BattlerId battler)
}
// Check if species isn't allowed to Dynamax.
if (GET_BASE_SPECIES_ID(species) == SPECIES_ZACIAN
|| GET_BASE_SPECIES_ID(species) == SPECIES_ZAMAZENTA
|| GET_BASE_SPECIES_ID(species) == SPECIES_ETERNATUS)
enum Species baseSpecies = GetBaseSpeciesId(species);
if (baseSpecies == SPECIES_ZACIAN
|| baseSpecies == SPECIES_ZAMAZENTA
|| baseSpecies == SPECIES_ETERNATUS)
return FALSE;
// Check if Trainer has already Dynamaxed.

View File

@ -670,7 +670,7 @@ void BattleLoadMonSpriteGfx(struct Pokemon *mon, enum BattlerId battler)
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX)
{
// Calyrex and its forms have a blue dynamax aura instead of red.
if (GET_BASE_SPECIES_ID(species) == SPECIES_CALYREX)
if (GetBaseSpeciesId(species) == SPECIES_CALYREX)
BlendPalette(paletteOffset, 16, 4, RGB(12, 0, 31));
else
BlendPalette(paletteOffset, 16, 4, RGB(31, 0, 12));
@ -989,7 +989,7 @@ void HandleSpeciesGfxDataChange(enum BattlerId battlerAtk, enum BattlerId battle
if (GetActiveGimmick(battlerAtk) == GIMMICK_DYNAMAX)
{
// Calyrex and its forms have a blue dynamax aura instead of red.
if (GET_BASE_SPECIES_ID(targetSpecies) == SPECIES_CALYREX)
if (GetBaseSpeciesId(targetSpecies) == SPECIES_CALYREX)
BlendPalette(paletteOffset, 16, 4, RGB(12, 0, 31));
else
BlendPalette(paletteOffset, 16, 4, RGB(31, 0, 12));

View File

@ -1425,7 +1425,7 @@ void GenerateBattlePyramidWildMon(void)
species = Random() % NUM_SPECIES;
// check if base species
if (GET_BASE_SPECIES_ID(species) != species)
if (GetBaseSpeciesId(species) != species)
continue;
// check type

View File

@ -12274,7 +12274,7 @@ void BS_JumpIfTerrainAffected(void)
void BS_TryReflectType(void)
{
NATIVE_ARGS(const u8 *failInstr);
enum Species targetBaseSpecies = GET_BASE_SPECIES_ID(gBattleMons[gBattlerTarget].species);
enum Species targetBaseSpecies = GetBattlerBaseSpecies(gBattlerTarget);
enum Type targetTypes[3];
GetBattlerTypes(gBattlerTarget, FALSE, targetTypes);

View File

@ -63,7 +63,7 @@ bool32 CanTerastallize(enum BattlerId battler)
{
enum HoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler);
if (gBattleMons[battler].volatiles.transformed && GET_BASE_SPECIES_ID(gBattleMons[battler].species) == SPECIES_TERAPAGOS)
if (gBattleMons[battler].volatiles.transformed && GetBattlerBaseSpecies(battler) == SPECIES_TERAPAGOS)
return FALSE;
// Prevents Zigzagoon from terastalizing in vanilla.

View File

@ -4656,7 +4656,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
&& gBattleStruct->battlerState[partner].commanderSpecies == SPECIES_NONE
&& gBattleMons[partner].species == SPECIES_DONDOZO
&& (gChosenActionByBattler[partner] != B_ACTION_SWITCH || HasBattlerActedThisTurn(partner))
&& GET_BASE_SPECIES_ID(GetMonData(GetBattlerMon(battler), MON_DATA_SPECIES)) == SPECIES_TATSUGIRI)
&& GetBaseSpeciesId(GetMonData(GetBattlerMon(battler), MON_DATA_SPECIES)) == SPECIES_TATSUGIRI)
{
SaveBattlerAttacker(gBattlerAttacker);
gBattlerAttacker = partner;
@ -6703,15 +6703,15 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct BattleContext *ctx)
modifier = uq4_12_multiply(modifier, uq4_12_add(UQ_4_12(1.0), PercentToUQ4_12_Floored(holdEffectParamAtk)));
break;
case HOLD_EFFECT_LUSTROUS_ORB:
if (GET_BASE_SPECIES_ID(gBattleMons[battlerAtk].species) == SPECIES_PALKIA && (moveType == TYPE_WATER || moveType == TYPE_DRAGON))
if (GetBattlerBaseSpecies(battlerAtk) == SPECIES_PALKIA && (moveType == TYPE_WATER || moveType == TYPE_DRAGON))
modifier = uq4_12_multiply(modifier, holdEffectModifier);
break;
case HOLD_EFFECT_ADAMANT_ORB:
if (GET_BASE_SPECIES_ID(gBattleMons[battlerAtk].species) == SPECIES_DIALGA && (moveType == TYPE_STEEL || moveType == TYPE_DRAGON))
if (GetBattlerBaseSpecies(battlerAtk) == SPECIES_DIALGA && (moveType == TYPE_STEEL || moveType == TYPE_DRAGON))
modifier = uq4_12_multiply(modifier, holdEffectModifier);
break;
case HOLD_EFFECT_GRISEOUS_ORB:
if (GET_BASE_SPECIES_ID(gBattleMons[battlerAtk].species) == SPECIES_GIRATINA && (moveType == TYPE_GHOST || moveType == TYPE_DRAGON))
if (GetBattlerBaseSpecies(battlerAtk) == SPECIES_GIRATINA && (moveType == TYPE_GHOST || moveType == TYPE_DRAGON))
modifier = uq4_12_multiply(modifier, holdEffectModifier);
break;
case HOLD_EFFECT_SOUL_DEW:
@ -6730,7 +6730,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct BattleContext *ctx)
modifier = uq4_12_multiply(modifier, UQ_4_12(1.1));
break;
case HOLD_EFFECT_OGERPON_MASK:
if (GET_BASE_SPECIES_ID(gBattleMons[battlerAtk].species) == SPECIES_OGERPON)
if (GetBattlerBaseSpecies(battlerAtk) == SPECIES_OGERPON)
modifier = uq4_12_multiply(modifier, UQ_4_12(1.2));
break;
default:
@ -6805,7 +6805,7 @@ static inline u32 CalcAttackStat(struct BattleContext *ctx)
enum Type moveType = ctx->moveType;
enum BattleMoveEffects moveEffect = GetMoveEffect(move);
atkBaseSpeciesId = GET_BASE_SPECIES_ID(gBattleMons[battlerAtk].species);
atkBaseSpeciesId = GetBattlerBaseSpecies(battlerAtk);
if (moveEffect == EFFECT_FOUL_PLAY)
{
@ -7877,7 +7877,7 @@ static inline u32 IsBattlerLeekAffected(enum BattlerId battler, enum HoldEffect
{
if (holdEffect == HOLD_EFFECT_LEEK)
{
return GET_BASE_SPECIES_ID(gBattleMons[battler].species) == SPECIES_FARFETCHD
return GetBattlerBaseSpecies(battler) == SPECIES_FARFETCHD
|| gBattleMons[battler].species == SPECIES_SIRFETCHD;
}
return FALSE;
@ -8801,7 +8801,7 @@ bool32 CanBattlerGetOrLoseItem(enum BattlerId fromBattler, enum BattlerId battle
else if (holdEffect == HOLD_EFFECT_BOOSTER_ENERGY
&& (IsSpeciesParadox(fromSpecies) || IsSpeciesParadox(otherSpecies)))
return FALSE;
else if (holdEffect == HOLD_EFFECT_OGERPON_MASK && GET_BASE_SPECIES_ID(fromSpecies) == SPECIES_OGERPON)
else if (holdEffect == HOLD_EFFECT_OGERPON_MASK && GetBaseSpeciesId(fromSpecies) == SPECIES_OGERPON)
return FALSE;
else
return TRUE;
@ -9078,7 +9078,7 @@ void SetDynamicMoveCategory(enum BattlerId battlerAtk, enum BattlerId battlerDef
gBattleStruct->swapDamageCategory = GetCategoryBasedOnStats(battlerAtk) == DAMAGE_CATEGORY_PHYSICAL;
break;
case EFFECT_TERA_STARSTORM:
if (GetActiveGimmick(battlerAtk) == GIMMICK_TERA && GET_BASE_SPECIES_ID(GetMonData(GetBattlerMon(battlerAtk), MON_DATA_SPECIES)) == SPECIES_TERAPAGOS)
if (GetActiveGimmick(battlerAtk) == GIMMICK_TERA && GetBaseSpeciesId(GetMonData(GetBattlerMon(battlerAtk), MON_DATA_SPECIES)) == SPECIES_TERAPAGOS)
gBattleStruct->swapDamageCategory = GetCategoryBasedOnStats(battlerAtk) == DAMAGE_CATEGORY_PHYSICAL;
break;
default:
@ -11002,3 +11002,8 @@ enum Stat GetDownloadStat(enum BattlerId battler)
else
return STAT_SPATK;
}
enum Species GetBattlerBaseSpecies(enum BattlerId battler)
{
return GetBaseSpeciesId(gBattleMons[battler].species);
}

View File

@ -227,7 +227,7 @@ static void TransferEggMoves(void)
continue;
// Check if you can inherit from them
if (GET_BASE_SPECIES_ID(moveTeacherSpecies) != GET_BASE_SPECIES_ID(moveLearnerSpecies)
if (GetBaseSpeciesId(moveTeacherSpecies) != GetBaseSpeciesId(moveLearnerSpecies)
&& (P_EGG_MOVE_TRANSFER < GEN_9 || GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[i].mon, MON_DATA_HELD_ITEM) != ITEM_MIRROR_HERB)
)
continue;
@ -725,7 +725,7 @@ static void InheritPokeball(struct Pokemon *egg, struct BoxPokemon *father, stru
if (P_BALL_INHERITING >= GEN_7)
{
if (GET_BASE_SPECIES_ID(fatherSpecies) == GET_BASE_SPECIES_ID(motherSpecies))
if (GetBaseSpeciesId(fatherSpecies) == GetBaseSpeciesId(motherSpecies))
inheritBall = (Random() % 2 == 0 ? motherBall : fatherBall);
else if (motherSpecies != SPECIES_DITTO)
inheritBall = motherBall;
@ -1046,11 +1046,11 @@ static enum Species DetermineEggSpeciesAndParentSlots(struct DayCare *daycare, u
eggSpecies = SPECIES_ILLUMISE;
else if (eggSpecies == SPECIES_MANAPHY)
eggSpecies = SPECIES_PHIONE;
else if (GET_BASE_SPECIES_ID(eggSpecies) == SPECIES_ROTOM)
else if (GetBaseSpeciesId(eggSpecies) == SPECIES_ROTOM)
eggSpecies = SPECIES_ROTOM;
else if (GET_BASE_SPECIES_ID(eggSpecies) == SPECIES_SCATTERBUG)
else if (GetBaseSpeciesId(eggSpecies) == SPECIES_SCATTERBUG)
eggSpecies = P_SCATTERBUG_LINE_FORM_BREED;
else if (GET_BASE_SPECIES_ID(eggSpecies) == SPECIES_FURFROU)
else if (GetBaseSpeciesId(eggSpecies) == SPECIES_FURFROU)
eggSpecies = SPECIES_FURFROU;
else if (eggSpecies == SPECIES_SINISTEA_ANTIQUE)
eggSpecies = SPECIES_SINISTEA_PHONY;

View File

@ -2145,7 +2145,7 @@ static void CheckPartyIneligibility(void)
{
if (!IsSpeciesEnabled(i))
continue;
baseSpecies = GET_BASE_SPECIES_ID(i);
baseSpecies = GetBaseSpeciesId(i);
if (baseSpecies == i && IsSpeciesFrontierBanned(baseSpecies))
{
if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(baseSpecies), FLAG_GET_CAUGHT))
@ -2158,7 +2158,7 @@ static void CheckPartyIneligibility(void)
enum Species species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG);
if (species == SPECIES_EGG || species == SPECIES_NONE)
continue;
if (IsSpeciesFrontierBanned(GET_BASE_SPECIES_ID(species)))
if (IsSpeciesFrontierBanned(GetBaseSpeciesId(species)))
{
bool32 addToList = TRUE;
for (j = 0; j < totalPartyBanned; j++)
@ -3355,7 +3355,7 @@ static u16 *MakeCaughtBannesSpeciesList(u32 totalBannedSpecies)
if (!IsSpeciesEnabled(i))
continue;
enum Species baseSpecies = GET_BASE_SPECIES_ID(i);
enum Species baseSpecies = GetBaseSpeciesId(i);
if (baseSpecies == i && IsSpeciesFrontierBanned(baseSpecies))
{
if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(baseSpecies), FLAG_GET_CAUGHT))

View File

@ -5040,7 +5040,7 @@ static bool8 CalculateMoves(void)
// Mega and Gmax Pokémon don't have distinct learnsets from their base form; so use base species for calculation
if (IsSpeciesMegaEvolution(species) || IsSpeciesGigantamax(species))
species = GetFormSpeciesId(species, 0);
species = GetBaseSpeciesId(species);
// Egg moves
if (HGSS_SHOW_EGG_MOVES_FOR_EVOS)
@ -6250,7 +6250,7 @@ static u8 PrintPreEvolutions(u8 taskId, enum Species species)
sPokedexView->sEvoScreenData.isMega = FALSE;
//Check if it's a mega
baseFormSpecies = GetFormSpeciesId(species, 0);
baseFormSpecies = GetBaseSpeciesId(species);
if (baseFormSpecies != species)
{
const struct FormChange *formChanges = GetSpeciesFormChanges(baseFormSpecies);
@ -6397,7 +6397,7 @@ u32 GetSpeciesNameWidthInChars(const u8 *speciesName)
bool32 IsSpeciesAlcremie(enum Species targetSpecies)
{
return GET_BASE_SPECIES_ID(targetSpecies) == SPECIES_ALCREMIE;
return GetBaseSpeciesId(targetSpecies) == SPECIES_ALCREMIE;
}
bool32 IsItemSweet(enum Item item)
@ -7035,7 +7035,7 @@ static void Task_HandleFormsScreenInput(u8 taskId)
if (sPokedexView->isSearchResults && sPokedexView->originalSearchSelectionNum == 0)
sPokedexView->originalSearchSelectionNum = sPokedexListItem->dexNum;
if (formSpecies == GetFormSpeciesId(formSpecies, 0))
if (formSpecies == GetBaseSpeciesId(formSpecies))
sPokedexView->formSpecies = 0;
else
sPokedexView->formSpecies = formSpecies;
@ -7073,7 +7073,7 @@ static void PrintForms(u8 taskId, enum Species species)
if (species == SPECIES_UNOWN)
y_offset_icons = 8;
if (GetFormSpeciesId(species, 0) == SPECIES_UNOWN)
if (GetBaseSpeciesId(species) == SPECIES_UNOWN)
y_offset_icons = 8;
StringCopy(gStringVar1, GetSpeciesName(species));

View File

@ -5034,7 +5034,7 @@ enum Species NationalPokedexNumToSpecies(enum NationalDexOrder nationalNum)
if (species == NUM_SPECIES)
return SPECIES_NONE;
return GET_BASE_SPECIES_ID(species);
return GetBaseSpeciesId(species);
}
u32 NationalToRegionalOrder(enum NationalDexOrder nationalNum)
@ -6339,23 +6339,16 @@ u8 *MonSpritesGfxManager_GetSpritePtr(u8 managerId, u8 spriteNum)
}
}
enum Species GetFormSpeciesId(enum Species speciesId, u8 formId)
{
if (GetSpeciesFormTable(speciesId) != NULL)
return GetSpeciesFormTable(speciesId)[formId];
else
return speciesId;
}
u8 GetFormIdFromFormSpeciesId(enum Species formSpeciesId)
{
const enum Species *formTable = GetSpeciesFormTable(formSpeciesId);
u8 targetFormId = 0;
if (GetSpeciesFormTable(formSpeciesId) != NULL)
if (formTable != NULL)
{
for (targetFormId = 0; GetSpeciesFormTable(formSpeciesId)[targetFormId] != FORM_SPECIES_END; targetFormId++)
for (targetFormId = 0; formTable[targetFormId] != FORM_SPECIES_END; targetFormId++)
{
if (formSpeciesId == GetSpeciesFormTable(formSpeciesId)[targetFormId])
if (formSpeciesId == formTable[targetFormId])
break;
}
}