mirror of
https://github.com/pret/pokefirered.git
synced 2026-05-14 00:01:13 -05:00
6406 lines
216 KiB
C
6406 lines
216 KiB
C
#define IS_POKEMON_C
|
|
|
|
#include "global.h"
|
|
#include "gflib.h"
|
|
#include "random.h"
|
|
#include "text.h"
|
|
#include "data.h"
|
|
#include "battle.h"
|
|
#include "battle_anim.h"
|
|
#include "item.h"
|
|
#include "event_data.h"
|
|
#include "util.h"
|
|
#include "pokemon_storage_system.h"
|
|
#include "battle_gfx_sfx_util.h"
|
|
#include "battle_controllers.h"
|
|
#include "evolution_scene.h"
|
|
#include "field_weather.h"
|
|
#include "battle_message.h"
|
|
#include "battle_util.h"
|
|
#include "link.h"
|
|
#include "pokemon_animation.h"
|
|
#include "m4a.h"
|
|
#include "pokedex.h"
|
|
#include "strings.h"
|
|
#include "overworld.h"
|
|
#include "party_menu.h"
|
|
#include "pokemon_icon.h"
|
|
#include "field_specials.h"
|
|
#include "berry.h"
|
|
#include "constants/items.h"
|
|
#include "constants/item_effects.h"
|
|
#include "constants/cries.h"
|
|
#include "constants/form_change_types.h"
|
|
#include "constants/pokemon.h"
|
|
#include "constants/abilities.h"
|
|
#include "constants/moves.h"
|
|
#include "constants/songs.h"
|
|
#include "constants/item_effects.h"
|
|
#include "constants/trainers.h"
|
|
#include "constants/hold_effects.h"
|
|
#include "constants/battle_move_effects.h"
|
|
#include "constants/union_room.h"
|
|
#include "constants/weather.h"
|
|
|
|
#define FRIENDSHIP_EVO_THRESHOLD ((P_FRIENDSHIP_EVO_THRESHOLD >= GEN_9) ? 160 : 220)
|
|
|
|
struct MonSpritesGfxManager
|
|
{
|
|
u8 numSprites:4;
|
|
u8 battlePosition:4;
|
|
u8 numFrames;
|
|
u8 active;
|
|
u8 mode;
|
|
u32 dataSize;
|
|
u8 *spriteBuffer;
|
|
u8 **spritePointers;
|
|
struct SpriteTemplate *templates;
|
|
struct SpriteFrameImage *frameImages;
|
|
};
|
|
|
|
static EWRAM_DATA u8 sLearningMoveTableID = 0;
|
|
EWRAM_DATA u8 gPlayerPartyCount = 0;
|
|
EWRAM_DATA u8 gEnemyPartyCount = 0;
|
|
EWRAM_DATA struct Pokemon gEnemyParty[PARTY_SIZE] = {};
|
|
EWRAM_DATA struct Pokemon gPlayerParty[PARTY_SIZE] = {};
|
|
EWRAM_DATA struct SpriteTemplate gMultiuseSpriteTemplate = {0};
|
|
static EWRAM_DATA struct MonSpritesGfxManager *sMonSpritesGfxManager = NULL;
|
|
|
|
static union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 personality, u8 substructType);
|
|
static u16 GetDeoxysStat(struct Pokemon *mon, s32 statId);
|
|
static bool8 IsShinyOtIdPersonality(u32 otId, u32 personality);
|
|
static u16 ModifyStatByNature(u8 nature, u16 n, u8 statIndex);
|
|
static u8 GetNatureFromPersonality(u32 personality);
|
|
static bool8 PartyMonHasStatus(struct Pokemon *mon, u32 unused, u32 healMask, u8 battleId);
|
|
static bool8 IsPokemonStorageFull(void);
|
|
static u8 SendMonToPC(struct Pokemon* mon);
|
|
static void EncryptBoxMon(struct BoxPokemon *boxMon);
|
|
static void DeleteFirstMoveAndGiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move);
|
|
static void GiveBoxMonInitialMoveset(struct BoxPokemon *boxMon);
|
|
static u16 GiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move);
|
|
static u8 GetLevelFromMonExp(struct Pokemon *mon);
|
|
static u16 CalculateBoxMonChecksum(struct BoxPokemon *boxMon);
|
|
static u16 SpeciesToHoennPokedexNum(u16 species);
|
|
static bool8 ShouldSkipFriendshipChange(void);
|
|
static void RemoveIVIndexFromList(u8 *ivs, u8 selectedIv);
|
|
|
|
#include "data/battle_moves.h"
|
|
#include "data/moves_info.h"
|
|
#include "data/abilities.h"
|
|
|
|
// Used in an unreferenced function in RS.
|
|
// Unreferenced here and in Emerald.
|
|
struct CombinedMove
|
|
{
|
|
u16 move1;
|
|
u16 move2;
|
|
u16 newMove;
|
|
};
|
|
|
|
static const struct CombinedMove sCombinedMoves[2] =
|
|
{
|
|
{MOVE_EMBER, MOVE_GUST, MOVE_HEAT_WAVE},
|
|
{0xFFFF, 0xFFFF, 0xFFFF}
|
|
};
|
|
|
|
// NOTE: The order of the elements in the 3 arrays below is irrelevant.
|
|
// To reorder the pokedex, see the values in include/constants/pokedex.h.
|
|
|
|
// Assigns all species to the Hoenn Dex Index (Summary No. for Hoenn Dex)
|
|
// removed:
|
|
// static const u16 sSpeciesToHoennPokedexNum[NUM_SPECIES - 1] =
|
|
|
|
|
|
// Assigns all species to the National Dex Index (Summary No. for National Dex)
|
|
// removed:
|
|
//static const u16 sSpeciesToNationalPokedexNum[NUM_SPECIES - 1] =
|
|
|
|
#define HOENN_TO_NATIONAL(name) [HOENN_DEX_##name - 1] = NATIONAL_DEX_##name
|
|
#define KANTO_TO_NATIONAL(name) [KANTO_DEX_##name - 1] = NATIONAL_DEX_##name
|
|
|
|
static const u16 sKantoToNationalOrder[NUM_SPECIES - 1] =
|
|
{
|
|
// Kanto
|
|
KANTO_TO_NATIONAL(BULBASAUR),
|
|
KANTO_TO_NATIONAL(IVYSAUR),
|
|
KANTO_TO_NATIONAL(VENUSAUR),
|
|
KANTO_TO_NATIONAL(CHARMANDER),
|
|
KANTO_TO_NATIONAL(CHARMELEON),
|
|
KANTO_TO_NATIONAL(CHARIZARD),
|
|
KANTO_TO_NATIONAL(SQUIRTLE),
|
|
KANTO_TO_NATIONAL(WARTORTLE),
|
|
KANTO_TO_NATIONAL(BLASTOISE),
|
|
KANTO_TO_NATIONAL(CATERPIE),
|
|
KANTO_TO_NATIONAL(METAPOD),
|
|
KANTO_TO_NATIONAL(BUTTERFREE),
|
|
KANTO_TO_NATIONAL(WEEDLE),
|
|
KANTO_TO_NATIONAL(KAKUNA),
|
|
KANTO_TO_NATIONAL(BEEDRILL),
|
|
KANTO_TO_NATIONAL(PIDGEY),
|
|
KANTO_TO_NATIONAL(PIDGEOTTO),
|
|
KANTO_TO_NATIONAL(PIDGEOT),
|
|
KANTO_TO_NATIONAL(RATTATA),
|
|
KANTO_TO_NATIONAL(RATICATE),
|
|
KANTO_TO_NATIONAL(SPEAROW),
|
|
KANTO_TO_NATIONAL(FEAROW),
|
|
KANTO_TO_NATIONAL(EKANS),
|
|
KANTO_TO_NATIONAL(ARBOK),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_2_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(PICHU),
|
|
#endif
|
|
KANTO_TO_NATIONAL(PIKACHU),
|
|
KANTO_TO_NATIONAL(RAICHU),
|
|
KANTO_TO_NATIONAL(SANDSHREW),
|
|
KANTO_TO_NATIONAL(SANDSLASH),
|
|
KANTO_TO_NATIONAL(NIDORAN_F),
|
|
KANTO_TO_NATIONAL(NIDORINA),
|
|
KANTO_TO_NATIONAL(NIDOQUEEN),
|
|
KANTO_TO_NATIONAL(NIDORAN_M),
|
|
KANTO_TO_NATIONAL(NIDORINO),
|
|
KANTO_TO_NATIONAL(NIDOKING),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_2_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(CLEFFA),
|
|
#endif
|
|
KANTO_TO_NATIONAL(CLEFAIRY),
|
|
KANTO_TO_NATIONAL(CLEFABLE),
|
|
KANTO_TO_NATIONAL(VULPIX),
|
|
KANTO_TO_NATIONAL(NINETALES),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_2_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(IGGLYBUFF),
|
|
#endif
|
|
KANTO_TO_NATIONAL(JIGGLYPUFF),
|
|
KANTO_TO_NATIONAL(WIGGLYTUFF),
|
|
KANTO_TO_NATIONAL(ZUBAT),
|
|
KANTO_TO_NATIONAL(GOLBAT),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_2_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(CROBAT),
|
|
#endif
|
|
KANTO_TO_NATIONAL(ODDISH),
|
|
KANTO_TO_NATIONAL(GLOOM),
|
|
KANTO_TO_NATIONAL(VILEPLUME),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_2_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(BELLOSSOM),
|
|
#endif
|
|
KANTO_TO_NATIONAL(PARAS),
|
|
KANTO_TO_NATIONAL(PARASECT),
|
|
KANTO_TO_NATIONAL(VENONAT),
|
|
KANTO_TO_NATIONAL(VENOMOTH),
|
|
KANTO_TO_NATIONAL(DIGLETT),
|
|
KANTO_TO_NATIONAL(DUGTRIO),
|
|
KANTO_TO_NATIONAL(MEOWTH),
|
|
KANTO_TO_NATIONAL(PERSIAN),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GALARIAN_FORMS
|
|
KANTO_TO_NATIONAL(PERRSERKER),
|
|
#endif
|
|
KANTO_TO_NATIONAL(PSYDUCK),
|
|
KANTO_TO_NATIONAL(GOLDUCK),
|
|
KANTO_TO_NATIONAL(MANKEY),
|
|
KANTO_TO_NATIONAL(PRIMEAPE),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_9_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(ANNIHILAPE),
|
|
#endif
|
|
KANTO_TO_NATIONAL(GROWLITHE),
|
|
KANTO_TO_NATIONAL(ARCANINE),
|
|
KANTO_TO_NATIONAL(POLIWAG),
|
|
KANTO_TO_NATIONAL(POLIWHIRL),
|
|
KANTO_TO_NATIONAL(POLIWRATH),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_2_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(POLITOED),
|
|
#endif
|
|
KANTO_TO_NATIONAL(ABRA),
|
|
KANTO_TO_NATIONAL(KADABRA),
|
|
KANTO_TO_NATIONAL(ALAKAZAM),
|
|
KANTO_TO_NATIONAL(MACHOP),
|
|
KANTO_TO_NATIONAL(MACHOKE),
|
|
KANTO_TO_NATIONAL(MACHAMP),
|
|
KANTO_TO_NATIONAL(BELLSPROUT),
|
|
KANTO_TO_NATIONAL(WEEPINBELL),
|
|
KANTO_TO_NATIONAL(VICTREEBEL),
|
|
KANTO_TO_NATIONAL(TENTACOOL),
|
|
KANTO_TO_NATIONAL(TENTACRUEL),
|
|
KANTO_TO_NATIONAL(GEODUDE),
|
|
KANTO_TO_NATIONAL(GRAVELER),
|
|
KANTO_TO_NATIONAL(GOLEM),
|
|
KANTO_TO_NATIONAL(PONYTA),
|
|
KANTO_TO_NATIONAL(RAPIDASH),
|
|
KANTO_TO_NATIONAL(SLOWPOKE),
|
|
KANTO_TO_NATIONAL(SLOWBRO),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_2_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(SLOWKING),
|
|
#endif
|
|
KANTO_TO_NATIONAL(MAGNEMITE),
|
|
KANTO_TO_NATIONAL(MAGNETON),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_4_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(MAGNEZONE),
|
|
#endif
|
|
KANTO_TO_NATIONAL(FARFETCHD),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GALARIAN_FORMS
|
|
KANTO_TO_NATIONAL(SIRFETCHD),
|
|
#endif
|
|
KANTO_TO_NATIONAL(DODUO),
|
|
KANTO_TO_NATIONAL(DODRIO),
|
|
KANTO_TO_NATIONAL(SEEL),
|
|
KANTO_TO_NATIONAL(DEWGONG),
|
|
KANTO_TO_NATIONAL(GRIMER),
|
|
KANTO_TO_NATIONAL(MUK),
|
|
KANTO_TO_NATIONAL(SHELLDER),
|
|
KANTO_TO_NATIONAL(CLOYSTER),
|
|
KANTO_TO_NATIONAL(GASTLY),
|
|
KANTO_TO_NATIONAL(HAUNTER),
|
|
KANTO_TO_NATIONAL(GENGAR),
|
|
KANTO_TO_NATIONAL(ONIX),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_2_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(STEELIX),
|
|
#endif
|
|
KANTO_TO_NATIONAL(DROWZEE),
|
|
KANTO_TO_NATIONAL(HYPNO),
|
|
KANTO_TO_NATIONAL(KRABBY),
|
|
KANTO_TO_NATIONAL(KINGLER),
|
|
KANTO_TO_NATIONAL(VOLTORB),
|
|
KANTO_TO_NATIONAL(ELECTRODE),
|
|
KANTO_TO_NATIONAL(EXEGGCUTE),
|
|
KANTO_TO_NATIONAL(EXEGGUTOR),
|
|
KANTO_TO_NATIONAL(CUBONE),
|
|
KANTO_TO_NATIONAL(MAROWAK),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_2_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(TYROGUE),
|
|
#endif
|
|
KANTO_TO_NATIONAL(HITMONLEE),
|
|
KANTO_TO_NATIONAL(HITMONCHAN),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_2_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(HITMONTOP),
|
|
#endif
|
|
KANTO_TO_NATIONAL(LICKITUNG),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_4_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(LICKILICKY),
|
|
#endif
|
|
KANTO_TO_NATIONAL(KOFFING),
|
|
KANTO_TO_NATIONAL(WEEZING),
|
|
KANTO_TO_NATIONAL(RHYHORN),
|
|
KANTO_TO_NATIONAL(RHYDON),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_4_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(RHYPERIOR),
|
|
KANTO_TO_NATIONAL(HAPPINY),
|
|
#endif
|
|
KANTO_TO_NATIONAL(CHANSEY),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_2_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(BLISSEY),
|
|
#endif
|
|
KANTO_TO_NATIONAL(TANGELA),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_4_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(TANGROWTH),
|
|
#endif
|
|
KANTO_TO_NATIONAL(KANGASKHAN),
|
|
KANTO_TO_NATIONAL(HORSEA),
|
|
KANTO_TO_NATIONAL(SEADRA),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_2_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(KINGDRA),
|
|
#endif
|
|
KANTO_TO_NATIONAL(GOLDEEN),
|
|
KANTO_TO_NATIONAL(SEAKING),
|
|
KANTO_TO_NATIONAL(STARYU),
|
|
KANTO_TO_NATIONAL(STARMIE),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_4_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(MIME_JR),
|
|
#endif
|
|
KANTO_TO_NATIONAL(MR_MIME),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GALARIAN_FORMS
|
|
KANTO_TO_NATIONAL(MR_RIME),
|
|
#endif
|
|
KANTO_TO_NATIONAL(SCYTHER),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_2_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(SCIZOR),
|
|
#endif
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_8_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(KLEAVOR),
|
|
#endif
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_2_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(SMOOCHUM),
|
|
#endif
|
|
KANTO_TO_NATIONAL(JYNX),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_2_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(ELEKID),
|
|
#endif
|
|
KANTO_TO_NATIONAL(ELECTABUZZ),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_4_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(ELECTIVIRE),
|
|
#endif
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_2_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(MAGBY),
|
|
#endif
|
|
KANTO_TO_NATIONAL(MAGMAR),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_4_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(MAGMORTAR),
|
|
#endif
|
|
KANTO_TO_NATIONAL(PINSIR),
|
|
KANTO_TO_NATIONAL(TAUROS),
|
|
KANTO_TO_NATIONAL(MAGIKARP),
|
|
KANTO_TO_NATIONAL(GYARADOS),
|
|
KANTO_TO_NATIONAL(LAPRAS),
|
|
KANTO_TO_NATIONAL(DITTO),
|
|
KANTO_TO_NATIONAL(EEVEE),
|
|
KANTO_TO_NATIONAL(VAPOREON),
|
|
KANTO_TO_NATIONAL(JOLTEON),
|
|
KANTO_TO_NATIONAL(FLAREON),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_2_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(ESPEON),
|
|
KANTO_TO_NATIONAL(UMBREON),
|
|
#endif
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_4_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(LEAFEON),
|
|
KANTO_TO_NATIONAL(GLACEON),
|
|
#endif
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_6_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(SYLVEON),
|
|
#endif
|
|
KANTO_TO_NATIONAL(PORYGON),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_2_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(PORYGON2),
|
|
#endif
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_4_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(PORYGON_Z),
|
|
#endif
|
|
KANTO_TO_NATIONAL(OMANYTE),
|
|
KANTO_TO_NATIONAL(OMASTAR),
|
|
KANTO_TO_NATIONAL(KABUTO),
|
|
KANTO_TO_NATIONAL(KABUTOPS),
|
|
KANTO_TO_NATIONAL(AERODACTYL),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_4_CROSS_EVOS
|
|
KANTO_TO_NATIONAL(MUNCHLAX),
|
|
#endif
|
|
KANTO_TO_NATIONAL(SNORLAX),
|
|
KANTO_TO_NATIONAL(ARTICUNO),
|
|
KANTO_TO_NATIONAL(ZAPDOS),
|
|
KANTO_TO_NATIONAL(MOLTRES),
|
|
KANTO_TO_NATIONAL(DRATINI),
|
|
KANTO_TO_NATIONAL(DRAGONAIR),
|
|
KANTO_TO_NATIONAL(DRAGONITE),
|
|
KANTO_TO_NATIONAL(MEWTWO),
|
|
KANTO_TO_NATIONAL(MEW),
|
|
};
|
|
|
|
// Assigns all Hoenn Dex Indexes to a National Dex Index
|
|
static const u16 sHoennToNationalOrder[NUM_SPECIES - 1] =
|
|
{
|
|
HOENN_TO_NATIONAL(TREECKO),
|
|
HOENN_TO_NATIONAL(GROVYLE),
|
|
HOENN_TO_NATIONAL(SCEPTILE),
|
|
HOENN_TO_NATIONAL(TORCHIC),
|
|
HOENN_TO_NATIONAL(COMBUSKEN),
|
|
HOENN_TO_NATIONAL(BLAZIKEN),
|
|
HOENN_TO_NATIONAL(MUDKIP),
|
|
HOENN_TO_NATIONAL(MARSHTOMP),
|
|
HOENN_TO_NATIONAL(SWAMPERT),
|
|
HOENN_TO_NATIONAL(POOCHYENA),
|
|
HOENN_TO_NATIONAL(MIGHTYENA),
|
|
HOENN_TO_NATIONAL(ZIGZAGOON),
|
|
HOENN_TO_NATIONAL(LINOONE),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GALARIAN_FORMS
|
|
HOENN_TO_NATIONAL(OBSTAGOON),
|
|
#endif
|
|
HOENN_TO_NATIONAL(WURMPLE),
|
|
HOENN_TO_NATIONAL(SILCOON),
|
|
HOENN_TO_NATIONAL(BEAUTIFLY),
|
|
HOENN_TO_NATIONAL(CASCOON),
|
|
HOENN_TO_NATIONAL(DUSTOX),
|
|
HOENN_TO_NATIONAL(LOTAD),
|
|
HOENN_TO_NATIONAL(LOMBRE),
|
|
HOENN_TO_NATIONAL(LUDICOLO),
|
|
HOENN_TO_NATIONAL(SEEDOT),
|
|
HOENN_TO_NATIONAL(NUZLEAF),
|
|
HOENN_TO_NATIONAL(SHIFTRY),
|
|
HOENN_TO_NATIONAL(TAILLOW),
|
|
HOENN_TO_NATIONAL(SWELLOW),
|
|
HOENN_TO_NATIONAL(WINGULL),
|
|
HOENN_TO_NATIONAL(PELIPPER),
|
|
HOENN_TO_NATIONAL(RALTS),
|
|
HOENN_TO_NATIONAL(KIRLIA),
|
|
HOENN_TO_NATIONAL(GARDEVOIR),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_4_CROSS_EVOS
|
|
HOENN_TO_NATIONAL(GALLADE),
|
|
#endif
|
|
HOENN_TO_NATIONAL(SURSKIT),
|
|
HOENN_TO_NATIONAL(MASQUERAIN),
|
|
HOENN_TO_NATIONAL(SHROOMISH),
|
|
HOENN_TO_NATIONAL(BRELOOM),
|
|
HOENN_TO_NATIONAL(SLAKOTH),
|
|
HOENN_TO_NATIONAL(VIGOROTH),
|
|
HOENN_TO_NATIONAL(SLAKING),
|
|
HOENN_TO_NATIONAL(ABRA),
|
|
HOENN_TO_NATIONAL(KADABRA),
|
|
HOENN_TO_NATIONAL(ALAKAZAM),
|
|
HOENN_TO_NATIONAL(NINCADA),
|
|
HOENN_TO_NATIONAL(NINJASK),
|
|
HOENN_TO_NATIONAL(SHEDINJA),
|
|
HOENN_TO_NATIONAL(WHISMUR),
|
|
HOENN_TO_NATIONAL(LOUDRED),
|
|
HOENN_TO_NATIONAL(EXPLOUD),
|
|
HOENN_TO_NATIONAL(MAKUHITA),
|
|
HOENN_TO_NATIONAL(HARIYAMA),
|
|
HOENN_TO_NATIONAL(GOLDEEN),
|
|
HOENN_TO_NATIONAL(SEAKING),
|
|
HOENN_TO_NATIONAL(MAGIKARP),
|
|
HOENN_TO_NATIONAL(GYARADOS),
|
|
HOENN_TO_NATIONAL(AZURILL),
|
|
HOENN_TO_NATIONAL(MARILL),
|
|
HOENN_TO_NATIONAL(AZUMARILL),
|
|
HOENN_TO_NATIONAL(GEODUDE),
|
|
HOENN_TO_NATIONAL(GRAVELER),
|
|
HOENN_TO_NATIONAL(GOLEM),
|
|
HOENN_TO_NATIONAL(NOSEPASS),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_4_CROSS_EVOS
|
|
HOENN_TO_NATIONAL(PROBOPASS),
|
|
#endif
|
|
HOENN_TO_NATIONAL(SKITTY),
|
|
HOENN_TO_NATIONAL(DELCATTY),
|
|
HOENN_TO_NATIONAL(ZUBAT),
|
|
HOENN_TO_NATIONAL(GOLBAT),
|
|
HOENN_TO_NATIONAL(CROBAT),
|
|
HOENN_TO_NATIONAL(TENTACOOL),
|
|
HOENN_TO_NATIONAL(TENTACRUEL),
|
|
HOENN_TO_NATIONAL(SABLEYE),
|
|
HOENN_TO_NATIONAL(MAWILE),
|
|
HOENN_TO_NATIONAL(ARON),
|
|
HOENN_TO_NATIONAL(LAIRON),
|
|
HOENN_TO_NATIONAL(AGGRON),
|
|
HOENN_TO_NATIONAL(MACHOP),
|
|
HOENN_TO_NATIONAL(MACHOKE),
|
|
HOENN_TO_NATIONAL(MACHAMP),
|
|
HOENN_TO_NATIONAL(MEDITITE),
|
|
HOENN_TO_NATIONAL(MEDICHAM),
|
|
HOENN_TO_NATIONAL(ELECTRIKE),
|
|
HOENN_TO_NATIONAL(MANECTRIC),
|
|
HOENN_TO_NATIONAL(PLUSLE),
|
|
HOENN_TO_NATIONAL(MINUN),
|
|
HOENN_TO_NATIONAL(MAGNEMITE),
|
|
HOENN_TO_NATIONAL(MAGNETON),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_4_CROSS_EVOS
|
|
HOENN_TO_NATIONAL(MAGNEZONE),
|
|
#endif
|
|
HOENN_TO_NATIONAL(VOLTORB),
|
|
HOENN_TO_NATIONAL(ELECTRODE),
|
|
HOENN_TO_NATIONAL(VOLBEAT),
|
|
HOENN_TO_NATIONAL(ILLUMISE),
|
|
HOENN_TO_NATIONAL(ODDISH),
|
|
HOENN_TO_NATIONAL(GLOOM),
|
|
HOENN_TO_NATIONAL(VILEPLUME),
|
|
HOENN_TO_NATIONAL(BELLOSSOM),
|
|
HOENN_TO_NATIONAL(DODUO),
|
|
HOENN_TO_NATIONAL(DODRIO),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_4_CROSS_EVOS
|
|
HOENN_TO_NATIONAL(BUDEW),
|
|
HOENN_TO_NATIONAL(ROSELIA),
|
|
HOENN_TO_NATIONAL(ROSERADE),
|
|
#else
|
|
HOENN_TO_NATIONAL(ROSELIA),
|
|
#endif
|
|
HOENN_TO_NATIONAL(GULPIN),
|
|
HOENN_TO_NATIONAL(SWALOT),
|
|
HOENN_TO_NATIONAL(CARVANHA),
|
|
HOENN_TO_NATIONAL(SHARPEDO),
|
|
HOENN_TO_NATIONAL(WAILMER),
|
|
HOENN_TO_NATIONAL(WAILORD),
|
|
HOENN_TO_NATIONAL(NUMEL),
|
|
HOENN_TO_NATIONAL(CAMERUPT),
|
|
HOENN_TO_NATIONAL(SLUGMA),
|
|
HOENN_TO_NATIONAL(MAGCARGO),
|
|
HOENN_TO_NATIONAL(TORKOAL),
|
|
HOENN_TO_NATIONAL(GRIMER),
|
|
HOENN_TO_NATIONAL(MUK),
|
|
HOENN_TO_NATIONAL(KOFFING),
|
|
HOENN_TO_NATIONAL(WEEZING),
|
|
HOENN_TO_NATIONAL(SPOINK),
|
|
HOENN_TO_NATIONAL(GRUMPIG),
|
|
HOENN_TO_NATIONAL(SANDSHREW),
|
|
HOENN_TO_NATIONAL(SANDSLASH),
|
|
HOENN_TO_NATIONAL(SPINDA),
|
|
HOENN_TO_NATIONAL(SKARMORY),
|
|
HOENN_TO_NATIONAL(TRAPINCH),
|
|
HOENN_TO_NATIONAL(VIBRAVA),
|
|
HOENN_TO_NATIONAL(FLYGON),
|
|
HOENN_TO_NATIONAL(CACNEA),
|
|
HOENN_TO_NATIONAL(CACTURNE),
|
|
HOENN_TO_NATIONAL(SWABLU),
|
|
HOENN_TO_NATIONAL(ALTARIA),
|
|
HOENN_TO_NATIONAL(ZANGOOSE),
|
|
HOENN_TO_NATIONAL(SEVIPER),
|
|
HOENN_TO_NATIONAL(LUNATONE),
|
|
HOENN_TO_NATIONAL(SOLROCK),
|
|
HOENN_TO_NATIONAL(BARBOACH),
|
|
HOENN_TO_NATIONAL(WHISCASH),
|
|
HOENN_TO_NATIONAL(CORPHISH),
|
|
HOENN_TO_NATIONAL(CRAWDAUNT),
|
|
HOENN_TO_NATIONAL(BALTOY),
|
|
HOENN_TO_NATIONAL(CLAYDOL),
|
|
HOENN_TO_NATIONAL(LILEEP),
|
|
HOENN_TO_NATIONAL(CRADILY),
|
|
HOENN_TO_NATIONAL(ANORITH),
|
|
HOENN_TO_NATIONAL(ARMALDO),
|
|
HOENN_TO_NATIONAL(IGGLYBUFF),
|
|
HOENN_TO_NATIONAL(JIGGLYPUFF),
|
|
HOENN_TO_NATIONAL(WIGGLYTUFF),
|
|
HOENN_TO_NATIONAL(FEEBAS),
|
|
HOENN_TO_NATIONAL(MILOTIC),
|
|
HOENN_TO_NATIONAL(CASTFORM),
|
|
HOENN_TO_NATIONAL(STARYU),
|
|
HOENN_TO_NATIONAL(STARMIE),
|
|
HOENN_TO_NATIONAL(KECLEON),
|
|
HOENN_TO_NATIONAL(SHUPPET),
|
|
HOENN_TO_NATIONAL(BANETTE),
|
|
HOENN_TO_NATIONAL(DUSKULL),
|
|
HOENN_TO_NATIONAL(DUSCLOPS),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_4_CROSS_EVOS
|
|
HOENN_TO_NATIONAL(DUSKNOIR),
|
|
HOENN_TO_NATIONAL(TROPIUS),
|
|
HOENN_TO_NATIONAL(CHINGLING),
|
|
#else
|
|
HOENN_TO_NATIONAL(TROPIUS),
|
|
#endif
|
|
HOENN_TO_NATIONAL(CHIMECHO),
|
|
HOENN_TO_NATIONAL(ABSOL),
|
|
HOENN_TO_NATIONAL(VULPIX),
|
|
HOENN_TO_NATIONAL(NINETALES),
|
|
HOENN_TO_NATIONAL(PICHU),
|
|
HOENN_TO_NATIONAL(PIKACHU),
|
|
HOENN_TO_NATIONAL(RAICHU),
|
|
HOENN_TO_NATIONAL(PSYDUCK),
|
|
HOENN_TO_NATIONAL(GOLDUCK),
|
|
HOENN_TO_NATIONAL(WYNAUT),
|
|
HOENN_TO_NATIONAL(WOBBUFFET),
|
|
HOENN_TO_NATIONAL(NATU),
|
|
HOENN_TO_NATIONAL(XATU),
|
|
HOENN_TO_NATIONAL(GIRAFARIG),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_9_CROSS_EVOS
|
|
HOENN_TO_NATIONAL(FARIGIRAF),
|
|
#endif
|
|
HOENN_TO_NATIONAL(PHANPY),
|
|
HOENN_TO_NATIONAL(DONPHAN),
|
|
HOENN_TO_NATIONAL(PINSIR),
|
|
HOENN_TO_NATIONAL(HERACROSS),
|
|
HOENN_TO_NATIONAL(RHYHORN),
|
|
HOENN_TO_NATIONAL(RHYDON),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_4_CROSS_EVOS
|
|
HOENN_TO_NATIONAL(RHYPERIOR),
|
|
#endif
|
|
HOENN_TO_NATIONAL(SNORUNT),
|
|
HOENN_TO_NATIONAL(GLALIE),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GEN_4_CROSS_EVOS
|
|
HOENN_TO_NATIONAL(FROSLASS),
|
|
#endif
|
|
HOENN_TO_NATIONAL(SPHEAL),
|
|
HOENN_TO_NATIONAL(SEALEO),
|
|
HOENN_TO_NATIONAL(WALREIN),
|
|
HOENN_TO_NATIONAL(CLAMPERL),
|
|
HOENN_TO_NATIONAL(HUNTAIL),
|
|
HOENN_TO_NATIONAL(GOREBYSS),
|
|
HOENN_TO_NATIONAL(RELICANTH),
|
|
HOENN_TO_NATIONAL(CORSOLA),
|
|
#if P_NEW_EVOS_IN_REGIONAL_DEX && P_GALARIAN_FORMS
|
|
HOENN_TO_NATIONAL(CURSOLA),
|
|
#endif
|
|
HOENN_TO_NATIONAL(CHINCHOU),
|
|
HOENN_TO_NATIONAL(LANTURN),
|
|
HOENN_TO_NATIONAL(LUVDISC),
|
|
HOENN_TO_NATIONAL(HORSEA),
|
|
HOENN_TO_NATIONAL(SEADRA),
|
|
HOENN_TO_NATIONAL(KINGDRA),
|
|
HOENN_TO_NATIONAL(BAGON),
|
|
HOENN_TO_NATIONAL(SHELGON),
|
|
HOENN_TO_NATIONAL(SALAMENCE),
|
|
HOENN_TO_NATIONAL(BELDUM),
|
|
HOENN_TO_NATIONAL(METANG),
|
|
HOENN_TO_NATIONAL(METAGROSS),
|
|
HOENN_TO_NATIONAL(REGIROCK),
|
|
HOENN_TO_NATIONAL(REGICE),
|
|
HOENN_TO_NATIONAL(REGISTEEL),
|
|
HOENN_TO_NATIONAL(LATIAS),
|
|
HOENN_TO_NATIONAL(LATIOS),
|
|
HOENN_TO_NATIONAL(KYOGRE),
|
|
HOENN_TO_NATIONAL(GROUDON),
|
|
HOENN_TO_NATIONAL(RAYQUAZA),
|
|
HOENN_TO_NATIONAL(JIRACHI),
|
|
HOENN_TO_NATIONAL(DEOXYS),
|
|
};
|
|
|
|
static const struct SpindaSpot sSpindaSpotGraphics[] =
|
|
{
|
|
{.x = 16, .y = 7, .image = INCBIN_U16("graphics/spinda_spots/spot_0.bin")},
|
|
{.x = 40, .y = 8, .image = INCBIN_U16("graphics/spinda_spots/spot_1.bin")},
|
|
{.x = 22, .y = 25, .image = INCBIN_U16("graphics/spinda_spots/spot_2.bin")},
|
|
{.x = 34, .y = 26, .image = INCBIN_U16("graphics/spinda_spots/spot_3.bin")}
|
|
};
|
|
|
|
// #include "data/pokemon/item_effects.h"
|
|
|
|
static const s8 sNatureStatTable[NUM_NATURES][NUM_NATURE_STATS] =
|
|
{ // Attack Defense Speed Sp.Atk Sp.Def
|
|
[NATURE_HARDY] = { 0, 0, 0, 0, 0 },
|
|
[NATURE_LONELY] = { +1, -1, 0, 0, 0 },
|
|
[NATURE_BRAVE] = { +1, 0, -1, 0, 0 },
|
|
[NATURE_ADAMANT] = { +1, 0, 0, -1, 0 },
|
|
[NATURE_NAUGHTY] = { +1, 0, 0, 0, -1 },
|
|
[NATURE_BOLD] = { -1, +1, 0, 0, 0 },
|
|
[NATURE_DOCILE] = { 0, 0, 0, 0, 0 },
|
|
[NATURE_RELAXED] = { 0, +1, -1, 0, 0 },
|
|
[NATURE_IMPISH] = { 0, +1, 0, -1, 0 },
|
|
[NATURE_LAX] = { 0, +1, 0, 0, -1 },
|
|
[NATURE_TIMID] = { -1, 0, +1, 0, 0 },
|
|
[NATURE_HASTY] = { 0, -1, +1, 0, 0 },
|
|
[NATURE_SERIOUS] = { 0, 0, 0, 0, 0 },
|
|
[NATURE_JOLLY] = { 0, 0, +1, -1, 0 },
|
|
[NATURE_NAIVE] = { 0, 0, +1, 0, -1 },
|
|
[NATURE_MODEST] = { -1, 0, 0, +1, 0 },
|
|
[NATURE_MILD] = { 0, -1, 0, +1, 0 },
|
|
[NATURE_QUIET] = { 0, 0, -1, +1, 0 },
|
|
[NATURE_BASHFUL] = { 0, 0, 0, 0, 0 },
|
|
[NATURE_RASH] = { 0, 0, 0, +1, -1 },
|
|
[NATURE_CALM] = { -1, 0, 0, 0, +1 },
|
|
[NATURE_GENTLE] = { 0, -1, 0, 0, +1 },
|
|
[NATURE_SASSY] = { 0, 0, -1, 0, +1 },
|
|
[NATURE_CAREFUL] = { 0, 0, 0, -1, +1 },
|
|
[NATURE_QUIRKY] = { 0, 0, 0, 0, 0 },
|
|
};
|
|
|
|
|
|
#include "data/graphics/pokemon.h"
|
|
#include "data/pokemon_graphics/front_pic_anims.h"
|
|
|
|
#include "data/pokemon/trainer_class_lookups.h"
|
|
#include "data/pokemon/experience_tables.h"
|
|
#include "data/pokemon/teachable_learnsets.h"
|
|
#include "data/pokemon/level_up_learnsets.h"
|
|
#include "data/pokemon/level_up_learnset_pointers.h"
|
|
#include "data/pokemon/form_species_tables.h"
|
|
#include "data/pokemon/form_change_tables.h"
|
|
#include "data/pokemon/species_info.h"
|
|
|
|
static const s8 sPokeblockFlavorCompatibilityTable[NUM_NATURES * FLAVOR_COUNT] =
|
|
{
|
|
// Cool, Beauty, Cute, Smart, Tough
|
|
0, 0, 0, 0, 0, // Hardy
|
|
1, 0, 0, 0, -1, // Lonely
|
|
1, 0, -1, 0, 0, // Brave
|
|
1, -1, 0, 0, 0, // Adamant
|
|
1, 0, 0, -1, 0, // Naughty
|
|
-1, 0, 0, 0, 1, // Bold
|
|
0, 0, 0, 0, 0, // Docile
|
|
0, 0, -1, 0, 1, // Relaxed
|
|
0, -1, 0, 0, 1, // Impish
|
|
0, 0, 0, -1, 1, // Lax
|
|
-1, 0, 1, 0, 0, // Timid
|
|
0, 0, 1, 0, -1, // Hasty
|
|
0, 0, 0, 0, 0, // Serious
|
|
0, -1, 1, 0, 0, // Jolly
|
|
0, 0, 1, -1, 0, // Naive
|
|
-1, 1, 0, 0, 0, // Modest
|
|
0, 1, 0, 0, -1, // Mild
|
|
0, 1, -1, 0, 0, // Quiet
|
|
0, 0, 0, 0, 0, // Bashful
|
|
0, 1, 0, -1, 0, // Rash
|
|
-1, 0, 0, 1, 0, // Calm
|
|
0, 0, 0, 1, -1, // Gentle
|
|
0, 0, -1, 1, 0, // Sassy
|
|
0, -1, 0, 1, 0, // Careful
|
|
0, 0, 0, 0, 0 // Quirky
|
|
};
|
|
|
|
#define PP_UP_SHIFTS(val) val, (val) << 2, (val) << 4, (val) << 6
|
|
#define PP_UP_SHIFTS_INV(val) (u8)~(val), (u8)~((val) << 2), (u8)~((val) << 4), (u8)~((val) << 6)
|
|
|
|
// PP Up bonuses are stored for a Pokémon as a single byte.
|
|
// There are 2 bits (a value 0-3) for each move slot that
|
|
// represent how many PP Ups have been applied.
|
|
// The following arrays take a move slot id and return:
|
|
// gPPUpGetMask - A mask to get the number of PP Ups applied to that move slot
|
|
// gPPUpClearMask - A mask to clear the number of PP Ups applied to that move slot
|
|
// gPPUpAddValues - A value to add to the PP Bonuses byte to apply 1 PP Up to that move slot
|
|
const u8 gPPUpGetMask[MAX_MON_MOVES] = {PP_UP_SHIFTS(3)};
|
|
const u8 gPPUpClearMask[MAX_MON_MOVES] = {PP_UP_SHIFTS_INV(3)};
|
|
const u8 gPPUpAddValues[MAX_MON_MOVES] = {PP_UP_SHIFTS(1)};
|
|
|
|
const u8 gStatStageRatios[MAX_STAT_STAGE + 1][2] =
|
|
{
|
|
{10, 40}, // -6, MIN_STAT_STAGE
|
|
{10, 35}, // -5
|
|
{10, 30}, // -4
|
|
{10, 25}, // -3
|
|
{10, 20}, // -2
|
|
{10, 15}, // -1
|
|
{10, 10}, // 0, DEFAULT_STAT_STAGE
|
|
{15, 10}, // +1
|
|
{20, 10}, // +2
|
|
{25, 10}, // +3
|
|
{30, 10}, // +4
|
|
{35, 10}, // +5
|
|
{40, 10}, // +6, MAX_STAT_STAGE
|
|
};
|
|
|
|
static const u8 sText_GameFreak[] = _("ゲーフリ");
|
|
|
|
static const u8 sHoldEffectToType[][2] =
|
|
{
|
|
{HOLD_EFFECT_BUG_POWER, TYPE_BUG},
|
|
{HOLD_EFFECT_STEEL_POWER, TYPE_STEEL},
|
|
{HOLD_EFFECT_GROUND_POWER, TYPE_GROUND},
|
|
{HOLD_EFFECT_ROCK_POWER, TYPE_ROCK},
|
|
{HOLD_EFFECT_GRASS_POWER, TYPE_GRASS},
|
|
{HOLD_EFFECT_DARK_POWER, TYPE_DARK},
|
|
{HOLD_EFFECT_FIGHTING_POWER, TYPE_FIGHTING},
|
|
{HOLD_EFFECT_ELECTRIC_POWER, TYPE_ELECTRIC},
|
|
{HOLD_EFFECT_WATER_POWER, TYPE_WATER},
|
|
{HOLD_EFFECT_FLYING_POWER, TYPE_FLYING},
|
|
{HOLD_EFFECT_POISON_POWER, TYPE_POISON},
|
|
{HOLD_EFFECT_ICE_POWER, TYPE_ICE},
|
|
{HOLD_EFFECT_GHOST_POWER, TYPE_GHOST},
|
|
{HOLD_EFFECT_PSYCHIC_POWER, TYPE_PSYCHIC},
|
|
{HOLD_EFFECT_FIRE_POWER, TYPE_FIRE},
|
|
{HOLD_EFFECT_DRAGON_POWER, TYPE_DRAGON},
|
|
{HOLD_EFFECT_NORMAL_POWER, TYPE_NORMAL},
|
|
{HOLD_EFFECT_FAIRY_POWER, TYPE_FAIRY},
|
|
};
|
|
|
|
const struct SpriteTemplate gSpriteTemplates_Battlers[MAX_BATTLERS_COUNT] =
|
|
{
|
|
[B_POSITION_PLAYER_LEFT] = {
|
|
.tileTag = TAG_NONE,
|
|
.paletteTag = 0,
|
|
.oam = &gOamData_BattlerPlayer,
|
|
.anims = NULL,
|
|
.images = gBattlerPicTable_PlayerLeft,
|
|
.affineAnims = gAffineAnims_BattleSpritePlayerSide,
|
|
.callback = SpriteCB_AllyMon,
|
|
},
|
|
[B_POSITION_OPPONENT_LEFT] = {
|
|
.tileTag = TAG_NONE,
|
|
.paletteTag = 0,
|
|
.oam = &gOamData_BattlerOpponent,
|
|
.anims = NULL,
|
|
.images = gBattlerPicTable_OpponentLeft,
|
|
.affineAnims = gAffineAnims_BattleSpriteOpponentSide,
|
|
.callback = SpriteCB_EnemyMon,
|
|
},
|
|
[B_POSITION_PLAYER_RIGHT] = {
|
|
.tileTag = TAG_NONE,
|
|
.paletteTag = 0,
|
|
.oam = &gOamData_BattlerPlayer,
|
|
.anims = NULL,
|
|
.images = gBattlerPicTable_PlayerRight,
|
|
.affineAnims = gAffineAnims_BattleSpritePlayerSide,
|
|
.callback = SpriteCB_AllyMon,
|
|
},
|
|
[B_POSITION_OPPONENT_RIGHT] = {
|
|
.tileTag = TAG_NONE,
|
|
.paletteTag = 0,
|
|
.oam = &gOamData_BattlerOpponent,
|
|
.anims = NULL,
|
|
.images = gBattlerPicTable_OpponentRight,
|
|
.affineAnims = gAffineAnims_BattleSpriteOpponentSide,
|
|
.callback = SpriteCB_EnemyMon,
|
|
},
|
|
};
|
|
|
|
static const struct SpriteTemplate sTrainerBackSpriteTemplates[] =
|
|
{
|
|
[TRAINER_BACK_PIC_RED] = {
|
|
.tileTag = TAG_NONE,
|
|
.paletteTag = 0,
|
|
.oam = &gOamData_BattlerPlayer,
|
|
.anims = NULL,
|
|
.images = gTrainerBackPicTable_Red,
|
|
.affineAnims = gAffineAnims_BattleSpritePlayerSide,
|
|
.callback = SpriteCB_AllyMon,
|
|
},
|
|
[TRAINER_BACK_PIC_LEAF] = {
|
|
.tileTag = TAG_NONE,
|
|
.paletteTag = 0,
|
|
.oam = &gOamData_BattlerPlayer,
|
|
.anims = NULL,
|
|
.images = gTrainerBackPicTable_Leaf,
|
|
.affineAnims = gAffineAnims_BattleSpritePlayerSide,
|
|
.callback = SpriteCB_AllyMon,
|
|
},
|
|
[TRAINER_BACK_PIC_RUBY_SAPPHIRE_BRENDAN] = {
|
|
.tileTag = TAG_NONE,
|
|
.paletteTag = 0,
|
|
.oam = &gOamData_BattlerPlayer,
|
|
.anims = NULL,
|
|
.images = gTrainerBackPicTable_RSBrendan,
|
|
.affineAnims = gAffineAnims_BattleSpritePlayerSide,
|
|
.callback = SpriteCB_AllyMon,
|
|
},
|
|
[TRAINER_BACK_PIC_RUBY_SAPPHIRE_MAY] = {
|
|
.tileTag = TAG_NONE,
|
|
.paletteTag = 0,
|
|
.oam = &gOamData_BattlerPlayer,
|
|
.anims = NULL,
|
|
.images = gTrainerBackPicTable_RSMay,
|
|
.affineAnims = gAffineAnims_BattleSpritePlayerSide,
|
|
.callback = SpriteCB_AllyMon,
|
|
},
|
|
[TRAINER_BACK_PIC_POKEDUDE] = {
|
|
.tileTag = TAG_NONE,
|
|
.paletteTag = 0,
|
|
.oam = &gOamData_BattlerPlayer,
|
|
.anims = NULL,
|
|
.images = gTrainerBackPicTable_Pokedude,
|
|
.affineAnims = gAffineAnims_BattleSpritePlayerSide,
|
|
.callback = SpriteCB_AllyMon,
|
|
},
|
|
[TRAINER_BACK_PIC_OLD_MAN] = {
|
|
.tileTag = TAG_NONE,
|
|
.paletteTag = 0,
|
|
.oam = &gOamData_BattlerPlayer,
|
|
.anims = NULL,
|
|
.images = gTrainerBackPicTable_OldMan,
|
|
.affineAnims = gAffineAnims_BattleSpritePlayerSide,
|
|
.callback = SpriteCB_AllyMon,
|
|
},
|
|
};
|
|
|
|
// Classes dummied out
|
|
#define NUM_SECRET_BASE_CLASSES 5
|
|
static const u8 sSecretBaseFacilityClasses[GENDER_COUNT][NUM_SECRET_BASE_CLASSES] =
|
|
{
|
|
[MALE] = {
|
|
FACILITY_CLASS_YOUNGSTER,
|
|
FACILITY_CLASS_YOUNGSTER,
|
|
FACILITY_CLASS_YOUNGSTER,
|
|
FACILITY_CLASS_YOUNGSTER,
|
|
FACILITY_CLASS_YOUNGSTER
|
|
},
|
|
[FEMALE] = {
|
|
FACILITY_CLASS_YOUNGSTER,
|
|
FACILITY_CLASS_YOUNGSTER,
|
|
FACILITY_CLASS_YOUNGSTER,
|
|
FACILITY_CLASS_YOUNGSTER,
|
|
FACILITY_CLASS_YOUNGSTER
|
|
},
|
|
};
|
|
|
|
static const u8 sGetMonDataEVConstants[] =
|
|
{
|
|
MON_DATA_HP_EV,
|
|
MON_DATA_ATK_EV,
|
|
MON_DATA_DEF_EV,
|
|
MON_DATA_SPEED_EV,
|
|
MON_DATA_SPDEF_EV,
|
|
MON_DATA_SPATK_EV
|
|
};
|
|
|
|
// For stat-raising items
|
|
static const u8 sStatsToRaise[] =
|
|
{
|
|
STAT_ATK, STAT_ATK, STAT_SPEED, STAT_DEF, STAT_SPATK, STAT_ACC
|
|
};
|
|
|
|
// 3 modifiers each for how much to change friendship for different ranges
|
|
// 0-99, 100-199, 200+
|
|
static const s8 sFriendshipEventDeltas[][3] =
|
|
{
|
|
[FRIENDSHIP_EVENT_GROW_LEVEL] = { 5, 3, 2 },
|
|
[FRIENDSHIP_EVENT_VITAMIN] = { 5, 3, 2 },
|
|
[FRIENDSHIP_EVENT_BATTLE_ITEM] = { 1, 1, 0 },
|
|
[FRIENDSHIP_EVENT_LEAGUE_BATTLE] = { 3, 2, 1 },
|
|
[FRIENDSHIP_EVENT_LEARN_TMHM] = { 1, 1, 0 },
|
|
[FRIENDSHIP_EVENT_WALKING] = { 1, 1, 1 },
|
|
[FRIENDSHIP_EVENT_MASSAGE] = { 3, 3, 3 },
|
|
[FRIENDSHIP_EVENT_FAINT_SMALL] = {-1, -1, -1 },
|
|
[FRIENDSHIP_EVENT_FAINT_OUTSIDE_BATTLE] = {-5, -5, -10 },
|
|
[FRIENDSHIP_EVENT_FAINT_LARGE] = {-5, -5, -10 },
|
|
};
|
|
|
|
#define HM_MOVES_END 0xFFFF
|
|
|
|
static const u16 sHMMoves[] =
|
|
{
|
|
MOVE_CUT, MOVE_FLY, MOVE_SURF, MOVE_STRENGTH, MOVE_FLASH,
|
|
MOVE_ROCK_SMASH, MOVE_WATERFALL, MOVE_DIVE, HM_MOVES_END
|
|
};
|
|
|
|
#if defined(FIRERED)
|
|
// Attack forme
|
|
static const u16 sDeoxysBaseStats[] =
|
|
{
|
|
[STAT_HP] = 50,
|
|
[STAT_ATK] = 180,
|
|
[STAT_DEF] = 20,
|
|
[STAT_SPEED] = 150,
|
|
[STAT_SPATK] = 180,
|
|
[STAT_SPDEF] = 20,
|
|
};
|
|
#elif defined(LEAFGREEN)
|
|
// Defense forme
|
|
static const u16 sDeoxysBaseStats[] =
|
|
{
|
|
[STAT_HP] = 50,
|
|
[STAT_ATK] = 70,
|
|
[STAT_DEF] = 160,
|
|
[STAT_SPEED] = 90,
|
|
[STAT_SPATK] = 70,
|
|
[STAT_SPDEF] = 160,
|
|
};
|
|
#endif
|
|
|
|
// The classes used by other players in the Union Room.
|
|
// These should correspond with the overworld graphics in sUnionRoomObjGfxIds
|
|
const u16 gUnionRoomFacilityClasses[NUM_UNION_ROOM_CLASSES * GENDER_COUNT] =
|
|
{
|
|
// Male
|
|
FACILITY_CLASS_COOLTRAINER_M,
|
|
FACILITY_CLASS_BLACK_BELT,
|
|
FACILITY_CLASS_CAMPER,
|
|
FACILITY_CLASS_YOUNGSTER,
|
|
FACILITY_CLASS_PSYCHIC_M,
|
|
FACILITY_CLASS_BUG_CATCHER,
|
|
FACILITY_CLASS_TAMER,
|
|
FACILITY_CLASS_JUGGLER,
|
|
// Female
|
|
FACILITY_CLASS_COOLTRAINER_F,
|
|
FACILITY_CLASS_CHANNELER,
|
|
FACILITY_CLASS_PICNICKER,
|
|
FACILITY_CLASS_LASS,
|
|
FACILITY_CLASS_PSYCHIC_F,
|
|
FACILITY_CLASS_CRUSH_GIRL,
|
|
FACILITY_CLASS_PKMN_BREEDER,
|
|
FACILITY_CLASS_BEAUTY,
|
|
};
|
|
|
|
static const struct OamData sOamData_64x64 =
|
|
{
|
|
.y = 0,
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_NORMAL,
|
|
.mosaic = FALSE,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(64x64),
|
|
.x = 0,
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(64x64),
|
|
.tileNum = 0,
|
|
.priority = 0,
|
|
.paletteNum = 0,
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_64x64 =
|
|
{
|
|
.tileTag = TAG_NONE,
|
|
.paletteTag = TAG_NONE,
|
|
.oam = &sOamData_64x64,
|
|
.anims = gDummySpriteAnimTable,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = SpriteCallbackDummy,
|
|
};
|
|
|
|
// NOTE: Reordering this array will break compatibility with existing
|
|
// saves.
|
|
static const u32 sCompressedStatuses[] =
|
|
{
|
|
STATUS1_NONE,
|
|
STATUS1_SLEEP_TURN(1),
|
|
STATUS1_SLEEP_TURN(2),
|
|
STATUS1_SLEEP_TURN(3),
|
|
STATUS1_SLEEP_TURN(4),
|
|
STATUS1_SLEEP_TURN(5),
|
|
STATUS1_POISON,
|
|
STATUS1_BURN,
|
|
STATUS1_FREEZE,
|
|
STATUS1_PARALYSIS,
|
|
STATUS1_TOXIC_POISON,
|
|
STATUS1_FROSTBITE,
|
|
};
|
|
|
|
// Attempt to detect situations where the BoxPokemon struct is unable to
|
|
// contain all the values.
|
|
// TODO: Is it possible to compute:
|
|
// - The maximum experience.
|
|
// - The maximum PP.
|
|
// - The maximum HP.
|
|
// - The maximum form countdown.
|
|
STATIC_ASSERT(NUM_SPECIES < (1 << 11), PokemonSubstruct0_species_TooSmall);
|
|
STATIC_ASSERT(NUMBER_OF_MON_TYPES + 1 <= (1 << 5), PokemonSubstruct0_teraType_TooSmall);
|
|
STATIC_ASSERT(ITEMS_COUNT < (1 << 10), PokemonSubstruct0_heldItem_TooSmall);
|
|
STATIC_ASSERT(MAX_LEVEL <= 100, PokemonSubstruct0_experience_PotentiallTooSmall); // Maximum of ~2 million exp.
|
|
STATIC_ASSERT(LAST_BALL < (1 << 6), PokemonSubstruct0_pokeball_TooSmall);
|
|
STATIC_ASSERT(MOVES_COUNT_ALL < (1 << 11), PokemonSubstruct1_moves_TooSmall);
|
|
STATIC_ASSERT(ARRAY_COUNT(sCompressedStatuses) <= (1 << 4), PokemonSubstruct3_compressedStatus_TooSmall);
|
|
STATIC_ASSERT(MAX_LEVEL < (1 << 7), PokemonSubstruct3_metLevel_TooSmall);
|
|
STATIC_ASSERT(NUM_VERSIONS < (1 << 4), PokemonSubstruct3_metGame_TooSmall);
|
|
STATIC_ASSERT(MAX_DYNAMAX_LEVEL < (1 << 4), PokemonSubstruct3_dynamaxLevel_TooSmall);
|
|
STATIC_ASSERT(MAX_PER_STAT_IVS < (1 << 5), PokemonSubstruct3_ivs_TooSmall);
|
|
STATIC_ASSERT(NUM_NATURES <= (1 << 5), BoxPokemon_hiddenNatureModifier_TooSmall);
|
|
|
|
static u32 CompressStatus(u32 status)
|
|
{
|
|
s32 i;
|
|
for (i = 0; i < ARRAY_COUNT(sCompressedStatuses); i++)
|
|
{
|
|
if (sCompressedStatuses[i] == status)
|
|
return i;
|
|
}
|
|
return 0; // STATUS1_NONE
|
|
}
|
|
|
|
static u32 UncompressStatus(u32 compressedStatus)
|
|
{
|
|
if (compressedStatus < ARRAY_COUNT(sCompressedStatuses))
|
|
return sCompressedStatuses[compressedStatus];
|
|
else
|
|
return STATUS1_NONE;
|
|
}
|
|
|
|
void ZeroBoxMonData(struct BoxPokemon *boxMon)
|
|
{
|
|
u8 *raw = (u8 *)boxMon;
|
|
u32 i;
|
|
for (i = 0; i < sizeof(struct BoxPokemon); i++)
|
|
raw[i] = 0;
|
|
}
|
|
|
|
void ZeroMonData(struct Pokemon *mon)
|
|
{
|
|
u32 arg;
|
|
ZeroBoxMonData(&mon->box);
|
|
arg = 0;
|
|
SetMonData(mon, MON_DATA_STATUS, &arg);
|
|
SetMonData(mon, MON_DATA_LEVEL, &arg);
|
|
SetMonData(mon, MON_DATA_HP, &arg);
|
|
SetMonData(mon, MON_DATA_MAX_HP, &arg);
|
|
SetMonData(mon, MON_DATA_ATK, &arg);
|
|
SetMonData(mon, MON_DATA_DEF, &arg);
|
|
SetMonData(mon, MON_DATA_SPEED, &arg);
|
|
SetMonData(mon, MON_DATA_SPATK, &arg);
|
|
SetMonData(mon, MON_DATA_SPDEF, &arg);
|
|
arg = MAIL_NONE;
|
|
SetMonData(mon, MON_DATA_MAIL, &arg);
|
|
}
|
|
|
|
void ZeroPlayerPartyMons(void)
|
|
{
|
|
s32 i;
|
|
for (i = 0; i < PARTY_SIZE; i++)
|
|
ZeroMonData(&gPlayerParty[i]);
|
|
}
|
|
|
|
void ZeroEnemyPartyMons(void)
|
|
{
|
|
s32 i;
|
|
for (i = 0; i < PARTY_SIZE; i++)
|
|
ZeroMonData(&gEnemyParty[i]);
|
|
}
|
|
|
|
void CreateMon(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId)
|
|
{
|
|
u32 arg;
|
|
ZeroMonData(mon);
|
|
CreateBoxMon(&mon->box, species, level, fixedIV, hasFixedPersonality, fixedPersonality, otIdType, fixedOtId);
|
|
SetMonData(mon, MON_DATA_LEVEL, &level);
|
|
arg = MAIL_NONE;
|
|
SetMonData(mon, MON_DATA_MAIL, &arg);
|
|
CalculateMonStats(mon);
|
|
}
|
|
|
|
void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId)
|
|
{
|
|
u8 speciesName[POKEMON_NAME_LENGTH + 1];
|
|
u32 personality;
|
|
u32 value;
|
|
u16 checksum;
|
|
u8 i;
|
|
u8 availableIVs[NUM_STATS];
|
|
u8 selectedIvs[LEGENDARY_PERFECT_IV_COUNT];
|
|
bool32 isShiny;
|
|
|
|
ZeroBoxMonData(boxMon);
|
|
|
|
if (hasFixedPersonality)
|
|
personality = fixedPersonality;
|
|
else
|
|
personality = Random32();
|
|
|
|
// Determine original trainer ID
|
|
if (otIdType == OT_ID_RANDOM_NO_SHINY)
|
|
{
|
|
value = Random32();
|
|
isShiny = FALSE;
|
|
}
|
|
else if (otIdType == OT_ID_PRESET)
|
|
{
|
|
value = fixedOtId;
|
|
isShiny = GET_SHINY_VALUE(value, personality) < SHINY_ODDS;
|
|
}
|
|
else // Player is the OT
|
|
{
|
|
value = gSaveBlock2Ptr->playerTrainerId[0]
|
|
| (gSaveBlock2Ptr->playerTrainerId[1] << 8)
|
|
| (gSaveBlock2Ptr->playerTrainerId[2] << 16)
|
|
| (gSaveBlock2Ptr->playerTrainerId[3] << 24);
|
|
|
|
if (P_FLAG_FORCE_NO_SHINY != 0 && FlagGet(P_FLAG_FORCE_NO_SHINY))
|
|
{
|
|
isShiny = FALSE;
|
|
}
|
|
else if (P_FLAG_FORCE_SHINY != 0 && FlagGet(P_FLAG_FORCE_SHINY))
|
|
{
|
|
isShiny = TRUE;
|
|
}
|
|
else
|
|
{
|
|
u32 totalRerolls = 0;
|
|
if (CheckBagHasItem(ITEM_SHINY_CHARM, 1))
|
|
totalRerolls += I_SHINY_CHARM_ADDITIONAL_ROLLS;
|
|
// if (LURE_STEP_COUNT != 0)
|
|
// totalRerolls += 1;
|
|
|
|
while (GET_SHINY_VALUE(value, personality) >= SHINY_ODDS && totalRerolls > 0)
|
|
{
|
|
personality = Random32();
|
|
totalRerolls--;
|
|
}
|
|
|
|
isShiny = GET_SHINY_VALUE(value, personality) < SHINY_ODDS;
|
|
}
|
|
}
|
|
|
|
SetBoxMonData(boxMon, MON_DATA_PERSONALITY, &personality);
|
|
SetBoxMonData(boxMon, MON_DATA_OT_ID, &value);
|
|
|
|
checksum = CalculateBoxMonChecksum(boxMon);
|
|
SetBoxMonData(boxMon, MON_DATA_CHECKSUM, &checksum);
|
|
EncryptBoxMon(boxMon);
|
|
SetBoxMonData(boxMon, MON_DATA_IS_SHINY, &isShiny);
|
|
StringCopy(speciesName, GetSpeciesName(species));
|
|
SetBoxMonData(boxMon, MON_DATA_NICKNAME, speciesName);
|
|
SetBoxMonData(boxMon, MON_DATA_LANGUAGE, &gGameLanguage);
|
|
SetBoxMonData(boxMon, MON_DATA_OT_NAME, gSaveBlock2Ptr->playerName);
|
|
SetBoxMonData(boxMon, MON_DATA_SPECIES, &species);
|
|
SetBoxMonData(boxMon, MON_DATA_EXP, &gExperienceTables[gSpeciesInfo[species].growthRate][level]);
|
|
SetBoxMonData(boxMon, MON_DATA_FRIENDSHIP, &gSpeciesInfo[species].friendship);
|
|
value = GetCurrentRegionMapSectionId();
|
|
SetBoxMonData(boxMon, MON_DATA_MET_LOCATION, &value);
|
|
SetBoxMonData(boxMon, MON_DATA_MET_LEVEL, &level);
|
|
SetBoxMonData(boxMon, MON_DATA_MET_GAME, &gGameVersion);
|
|
value = ITEM_POKE_BALL;
|
|
SetBoxMonData(boxMon, MON_DATA_POKEBALL, &value);
|
|
SetBoxMonData(boxMon, MON_DATA_OT_GENDER, &gSaveBlock2Ptr->playerGender);
|
|
|
|
if (fixedIV < USE_RANDOM_IVS)
|
|
{
|
|
SetBoxMonData(boxMon, MON_DATA_HP_IV, &fixedIV);
|
|
SetBoxMonData(boxMon, MON_DATA_ATK_IV, &fixedIV);
|
|
SetBoxMonData(boxMon, MON_DATA_DEF_IV, &fixedIV);
|
|
SetBoxMonData(boxMon, MON_DATA_SPEED_IV, &fixedIV);
|
|
SetBoxMonData(boxMon, MON_DATA_SPATK_IV, &fixedIV);
|
|
SetBoxMonData(boxMon, MON_DATA_SPDEF_IV, &fixedIV);
|
|
}
|
|
else
|
|
{
|
|
u32 iv;
|
|
value = Random();
|
|
|
|
iv = value & MAX_IV_MASK;
|
|
SetBoxMonData(boxMon, MON_DATA_HP_IV, &iv);
|
|
iv = (value & (MAX_IV_MASK << 5)) >> 5;
|
|
SetBoxMonData(boxMon, MON_DATA_ATK_IV, &iv);
|
|
iv = (value & (MAX_IV_MASK << 10)) >> 10;
|
|
SetBoxMonData(boxMon, MON_DATA_DEF_IV, &iv);
|
|
|
|
value = Random();
|
|
|
|
iv = value & MAX_IV_MASK;
|
|
SetBoxMonData(boxMon, MON_DATA_SPEED_IV, &iv);
|
|
iv = (value & (MAX_IV_MASK << 5)) >> 5;
|
|
SetBoxMonData(boxMon, MON_DATA_SPATK_IV, &iv);
|
|
iv = (value & (MAX_IV_MASK << 10)) >> 10;
|
|
SetBoxMonData(boxMon, MON_DATA_SPDEF_IV, &iv);
|
|
|
|
if (gSpeciesInfo[species].allPerfectIVs)
|
|
{
|
|
iv = MAX_PER_STAT_IVS;
|
|
SetBoxMonData(boxMon, MON_DATA_HP_IV, &iv);
|
|
SetBoxMonData(boxMon, MON_DATA_ATK_IV, &iv);
|
|
SetBoxMonData(boxMon, MON_DATA_DEF_IV, &iv);
|
|
SetBoxMonData(boxMon, MON_DATA_SPEED_IV, &iv);
|
|
SetBoxMonData(boxMon, MON_DATA_SPATK_IV, &iv);
|
|
SetBoxMonData(boxMon, MON_DATA_SPDEF_IV, &iv);
|
|
}
|
|
else if (P_LEGENDARY_PERFECT_IVS >= GEN_6
|
|
&& (gSpeciesInfo[species].isLegendary
|
|
|| gSpeciesInfo[species].isMythical
|
|
|| gSpeciesInfo[species].isUltraBeast)
|
|
/*|| gSpeciesInfo[species].isTotem)*/)
|
|
{
|
|
iv = MAX_PER_STAT_IVS;
|
|
// Initialize a list of IV indices.
|
|
for (i = 0; i < NUM_STATS; i++)
|
|
{
|
|
availableIVs[i] = i;
|
|
}
|
|
|
|
// Select the 3 IVs that will be perfected.
|
|
for (i = 0; i < LEGENDARY_PERFECT_IV_COUNT; i++)
|
|
{
|
|
u8 index = Random() % (NUM_STATS - i);
|
|
selectedIvs[i] = availableIVs[index];
|
|
RemoveIVIndexFromList(availableIVs, index);
|
|
}
|
|
for (i = 0; i < LEGENDARY_PERFECT_IV_COUNT; i++)
|
|
{
|
|
switch (selectedIvs[i])
|
|
{
|
|
case STAT_HP:
|
|
SetBoxMonData(boxMon, MON_DATA_HP_IV, &iv);
|
|
break;
|
|
case STAT_ATK:
|
|
SetBoxMonData(boxMon, MON_DATA_ATK_IV, &iv);
|
|
break;
|
|
case STAT_DEF:
|
|
SetBoxMonData(boxMon, MON_DATA_DEF_IV, &iv);
|
|
break;
|
|
case STAT_SPEED:
|
|
SetBoxMonData(boxMon, MON_DATA_SPEED_IV, &iv);
|
|
break;
|
|
case STAT_SPATK:
|
|
SetBoxMonData(boxMon, MON_DATA_SPATK_IV, &iv);
|
|
break;
|
|
case STAT_SPDEF:
|
|
SetBoxMonData(boxMon, MON_DATA_SPDEF_IV, &iv);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (gSpeciesInfo[species].abilities[1])
|
|
{
|
|
value = personality & 1;
|
|
SetBoxMonData(boxMon, MON_DATA_ABILITY_NUM, &value);
|
|
}
|
|
|
|
GiveBoxMonInitialMoveset(boxMon);
|
|
}
|
|
|
|
void CreateMonWithNature(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 nature)
|
|
{
|
|
u32 personality;
|
|
|
|
do
|
|
{
|
|
personality = Random32();
|
|
}
|
|
while (nature != GetNatureFromPersonality(personality));
|
|
|
|
CreateMon(mon, species, level, fixedIV, TRUE, personality, OT_ID_PLAYER_ID, 0);
|
|
}
|
|
|
|
void CreateMonWithGenderNatureLetter(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 gender, u8 nature, u8 unownLetter)
|
|
{
|
|
u32 personality;
|
|
|
|
if ((u8)(unownLetter - 1) < NUM_UNOWN_FORMS)
|
|
{
|
|
u16 actualLetter;
|
|
|
|
do
|
|
{
|
|
personality = Random32();
|
|
actualLetter = GET_UNOWN_LETTER(personality);
|
|
}
|
|
while (nature != GetNatureFromPersonality(personality)
|
|
|| gender != GetGenderFromSpeciesAndPersonality(species, personality)
|
|
|| actualLetter != unownLetter - 1);
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
personality = Random32();
|
|
}
|
|
while (nature != GetNatureFromPersonality(personality)
|
|
|| gender != GetGenderFromSpeciesAndPersonality(species, personality));
|
|
}
|
|
|
|
CreateMon(mon, species, level, fixedIV, TRUE, personality, OT_ID_PLAYER_ID, 0);
|
|
}
|
|
|
|
// Used to create the Old Man's Weedle?
|
|
void CreateMaleMon(struct Pokemon *mon, u16 species, u8 level)
|
|
{
|
|
u32 personality;
|
|
u32 otId;
|
|
|
|
do
|
|
{
|
|
otId = Random32();
|
|
personality = Random32();
|
|
}
|
|
while (GetGenderFromSpeciesAndPersonality(species, personality) != MON_MALE);
|
|
CreateMon(mon, species, level, USE_RANDOM_IVS, TRUE, personality, OT_ID_PRESET, otId);
|
|
}
|
|
|
|
void CreateMonWithIVsPersonality(struct Pokemon *mon, u16 species, u8 level, u32 ivs, u32 personality)
|
|
{
|
|
CreateMon(mon, species, level, 0, TRUE, personality, OT_ID_PLAYER_ID, 0);
|
|
SetMonData(mon, MON_DATA_IVS, &ivs);
|
|
CalculateMonStats(mon);
|
|
}
|
|
|
|
static void CreateMonWithIVsOTID(struct Pokemon *mon, u16 species, u8 level, u8 *ivs, u32 otId)
|
|
{
|
|
CreateMon(mon, species, level, 0, FALSE, 0, OT_ID_PRESET, otId);
|
|
SetMonData(mon, MON_DATA_HP_IV, &ivs[STAT_HP]);
|
|
SetMonData(mon, MON_DATA_ATK_IV, &ivs[STAT_ATK]);
|
|
SetMonData(mon, MON_DATA_DEF_IV, &ivs[STAT_DEF]);
|
|
SetMonData(mon, MON_DATA_SPEED_IV, &ivs[STAT_SPEED]);
|
|
SetMonData(mon, MON_DATA_SPATK_IV, &ivs[STAT_SPATK]);
|
|
SetMonData(mon, MON_DATA_SPDEF_IV, &ivs[STAT_SPDEF]);
|
|
CalculateMonStats(mon);
|
|
}
|
|
|
|
void CreateMonWithEVSpread(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 evSpread)
|
|
{
|
|
s32 i;
|
|
s32 statCount = 0;
|
|
u16 evAmount;
|
|
u8 evsBits;
|
|
|
|
CreateMon(mon, species, level, fixedIV, FALSE, 0, OT_ID_PLAYER_ID, 0);
|
|
|
|
evsBits = evSpread;
|
|
|
|
for (i = 0; i < NUM_STATS; i++)
|
|
{
|
|
if (evsBits & 1)
|
|
statCount++;
|
|
evsBits >>= 1;
|
|
}
|
|
|
|
evAmount = MAX_TOTAL_EVS / statCount;
|
|
|
|
evsBits = 1;
|
|
|
|
for (i = 0; i < NUM_STATS; i++)
|
|
{
|
|
if (evSpread & evsBits)
|
|
SetMonData(mon, MON_DATA_HP_EV + i, &evAmount);
|
|
evsBits <<= 1;
|
|
}
|
|
|
|
CalculateMonStats(mon);
|
|
}
|
|
|
|
void CreateBattleTowerMon(struct Pokemon *mon, struct BattleTowerPokemon *src)
|
|
{
|
|
s32 i;
|
|
u8 value;
|
|
|
|
CreateMon(mon, src->species, src->level, 0, TRUE, src->personality, OT_ID_PRESET, src->otId);
|
|
|
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
SetMonMoveSlot(mon, src->moves[i], i);
|
|
|
|
SetMonData(mon, MON_DATA_PP_BONUSES, &src->ppBonuses);
|
|
SetMonData(mon, MON_DATA_HELD_ITEM, &src->heldItem);
|
|
|
|
// Why is this commented out in FR/LG?
|
|
/*
|
|
StringCopy(nickname, src->nickname);
|
|
|
|
if (nickname[0] == EXT_CTRL_CODE_BEGIN && nickname[1] == EXT_CTRL_CODE_JPN)
|
|
language = LANGUAGE_JAPANESE;
|
|
else
|
|
language = GAME_LANGUAGE;
|
|
|
|
SetMonData(mon, MON_DATA_LANGUAGE, &language);
|
|
Text_StripExtCtrlCodes(nickname);
|
|
*/
|
|
|
|
SetMonData(mon, MON_DATA_NICKNAME, &src->nickname);
|
|
SetMonData(mon, MON_DATA_FRIENDSHIP, &src->friendship);
|
|
SetMonData(mon, MON_DATA_HP_EV, &src->hpEV);
|
|
SetMonData(mon, MON_DATA_ATK_EV, &src->attackEV);
|
|
SetMonData(mon, MON_DATA_DEF_EV, &src->defenseEV);
|
|
SetMonData(mon, MON_DATA_SPEED_EV, &src->speedEV);
|
|
SetMonData(mon, MON_DATA_SPATK_EV, &src->spAttackEV);
|
|
SetMonData(mon, MON_DATA_SPDEF_EV, &src->spDefenseEV);
|
|
value = src->abilityNum;
|
|
SetMonData(mon, MON_DATA_ABILITY_NUM, &value);
|
|
value = src->hpIV;
|
|
SetMonData(mon, MON_DATA_HP_IV, &value);
|
|
value = src->attackIV;
|
|
SetMonData(mon, MON_DATA_ATK_IV, &value);
|
|
value = src->defenseIV;
|
|
SetMonData(mon, MON_DATA_DEF_IV, &value);
|
|
value = src->speedIV;
|
|
SetMonData(mon, MON_DATA_SPEED_IV, &value);
|
|
value = src->spAttackIV;
|
|
SetMonData(mon, MON_DATA_SPATK_IV, &value);
|
|
value = src->spDefenseIV;
|
|
SetMonData(mon, MON_DATA_SPDEF_IV, &value);
|
|
CalculateMonStats(mon);
|
|
}
|
|
|
|
static void CreateEventMon(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId)
|
|
{
|
|
bool32 isModernFatefulEncounter = TRUE;
|
|
|
|
CreateMon(mon, species, level, fixedIV, hasFixedPersonality, fixedPersonality, otIdType, fixedOtId);
|
|
SetMonData(mon, MON_DATA_MODERN_FATEFUL_ENCOUNTER, &isModernFatefulEncounter);
|
|
}
|
|
|
|
void ConvertPokemonToBattleTowerPokemon(struct Pokemon *mon, struct BattleTowerPokemon *dest)
|
|
{
|
|
s32 i;
|
|
u16 heldItem;
|
|
|
|
dest->species = GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, NULL);
|
|
|
|
if (heldItem == ITEM_ENIGMA_BERRY)
|
|
heldItem = 0;
|
|
|
|
dest->heldItem = heldItem;
|
|
|
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
dest->moves[i] = GetMonData(mon, MON_DATA_MOVE1 + i, NULL);
|
|
|
|
dest->level = GetMonData(mon, MON_DATA_LEVEL, NULL);
|
|
dest->ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES, NULL);
|
|
dest->otId = GetMonData(mon, MON_DATA_OT_ID, NULL);
|
|
dest->hpEV = GetMonData(mon, MON_DATA_HP_EV, NULL);
|
|
dest->attackEV = GetMonData(mon, MON_DATA_ATK_EV, NULL);
|
|
dest->defenseEV = GetMonData(mon, MON_DATA_DEF_EV, NULL);
|
|
dest->speedEV = GetMonData(mon, MON_DATA_SPEED_EV, NULL);
|
|
dest->spAttackEV = GetMonData(mon, MON_DATA_SPATK_EV, NULL);
|
|
dest->spDefenseEV = GetMonData(mon, MON_DATA_SPDEF_EV, NULL);
|
|
dest->friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL);
|
|
dest->hpIV = GetMonData(mon, MON_DATA_HP_IV, NULL);
|
|
dest->attackIV = GetMonData(mon, MON_DATA_ATK_IV, NULL);
|
|
dest->defenseIV = GetMonData(mon, MON_DATA_DEF_IV, NULL);
|
|
dest->speedIV = GetMonData(mon, MON_DATA_SPEED_IV, NULL);
|
|
dest->spAttackIV = GetMonData(mon, MON_DATA_SPATK_IV, NULL);
|
|
dest->spDefenseIV = GetMonData(mon, MON_DATA_SPDEF_IV, NULL);
|
|
dest->abilityNum = GetMonData(mon, MON_DATA_ABILITY_NUM, NULL);
|
|
dest->personality = GetMonData(mon, MON_DATA_PERSONALITY, NULL);
|
|
GetMonData(mon, MON_DATA_NICKNAME, dest->nickname);
|
|
}
|
|
|
|
static u16 CalculateBoxMonChecksum(struct BoxPokemon *boxMon)
|
|
{
|
|
u16 checksum = 0;
|
|
union PokemonSubstruct *substruct0 = GetSubstruct(boxMon, boxMon->personality, 0);
|
|
union PokemonSubstruct *substruct1 = GetSubstruct(boxMon, boxMon->personality, 1);
|
|
union PokemonSubstruct *substruct2 = GetSubstruct(boxMon, boxMon->personality, 2);
|
|
union PokemonSubstruct *substruct3 = GetSubstruct(boxMon, boxMon->personality, 3);
|
|
s32 i;
|
|
|
|
for (i = 0; i < (s32)ARRAY_COUNT(substruct0->raw); i++)
|
|
checksum += substruct0->raw[i];
|
|
|
|
for (i = 0; i < (s32)ARRAY_COUNT(substruct1->raw); i++)
|
|
checksum += substruct1->raw[i];
|
|
|
|
for (i = 0; i < (s32)ARRAY_COUNT(substruct2->raw); i++)
|
|
checksum += substruct2->raw[i];
|
|
|
|
for (i = 0; i < (s32)ARRAY_COUNT(substruct3->raw); i++)
|
|
checksum += substruct3->raw[i];
|
|
|
|
return checksum;
|
|
}
|
|
|
|
#define CALC_STAT(base, iv, ev, statIndex, field) \
|
|
{ \
|
|
u8 baseStat = gSpeciesInfo[species].base; \
|
|
s32 n = (((2 * baseStat + iv + ev / 4) * level) / 100) + 5; \
|
|
u8 nature = GetNature(mon); \
|
|
n = ModifyStatByNature(nature, n, statIndex); \
|
|
SetMonData(mon, field, &n); \
|
|
}
|
|
|
|
void CalculateMonStats(struct Pokemon *mon)
|
|
{
|
|
s32 oldMaxHP = GetMonData(mon, MON_DATA_MAX_HP, NULL);
|
|
s32 currentHP = GetMonData(mon, MON_DATA_HP, NULL);
|
|
s32 hpIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_HP) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_HP_IV, NULL);
|
|
s32 hpEV = GetMonData(mon, MON_DATA_HP_EV, NULL);
|
|
s32 attackIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_ATK) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_ATK_IV, NULL);
|
|
s32 attackEV = GetMonData(mon, MON_DATA_ATK_EV, NULL);
|
|
s32 defenseIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_DEF) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_DEF_IV, NULL);
|
|
s32 defenseEV = GetMonData(mon, MON_DATA_DEF_EV, NULL);
|
|
s32 speedIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_SPEED) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_SPEED_IV, NULL);
|
|
s32 speedEV = GetMonData(mon, MON_DATA_SPEED_EV, NULL);
|
|
s32 spAttackIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_SPATK) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_SPATK_IV, NULL);
|
|
s32 spAttackEV = GetMonData(mon, MON_DATA_SPATK_EV, NULL);
|
|
s32 spDefenseIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_SPDEF) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_SPDEF_IV, NULL);
|
|
s32 spDefenseEV = GetMonData(mon, MON_DATA_SPDEF_EV, NULL);
|
|
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
u8 friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL);
|
|
s32 level = GetLevelFromMonExp(mon);
|
|
s32 newMaxHP;
|
|
|
|
u8 nature = GetMonData(mon, MON_DATA_HIDDEN_NATURE, NULL);
|
|
|
|
SetMonData(mon, MON_DATA_LEVEL, &level);
|
|
|
|
if (species == SPECIES_SHEDINJA)
|
|
{
|
|
newMaxHP = 1;
|
|
}
|
|
else
|
|
{
|
|
s32 n = 2 * gSpeciesInfo[species].baseHP + hpIV;
|
|
newMaxHP = (((n + hpEV / 4) * level) / 100) + level + 10;
|
|
}
|
|
|
|
gBattleScripting.levelUpHP = newMaxHP - oldMaxHP;
|
|
if (gBattleScripting.levelUpHP == 0)
|
|
gBattleScripting.levelUpHP = 1;
|
|
|
|
SetMonData(mon, MON_DATA_MAX_HP, &newMaxHP);
|
|
|
|
CALC_STAT(baseAttack, attackIV, attackEV, STAT_ATK, MON_DATA_ATK)
|
|
CALC_STAT(baseDefense, defenseIV, defenseEV, STAT_DEF, MON_DATA_DEF)
|
|
CALC_STAT(baseSpeed, speedIV, speedEV, STAT_SPEED, MON_DATA_SPEED)
|
|
CALC_STAT(baseSpAttack, spAttackIV, spAttackEV, STAT_SPATK, MON_DATA_SPATK)
|
|
CALC_STAT(baseSpDefense, spDefenseIV, spDefenseEV, STAT_SPDEF, MON_DATA_SPDEF)
|
|
|
|
if (species == SPECIES_SHEDINJA)
|
|
{
|
|
if (currentHP != 0 || oldMaxHP == 0)
|
|
currentHP = 1;
|
|
else
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (currentHP == 0 && oldMaxHP == 0)
|
|
currentHP = newMaxHP;
|
|
else if (currentHP != 0)
|
|
{
|
|
if (newMaxHP > oldMaxHP)
|
|
currentHP += newMaxHP - oldMaxHP;
|
|
if (currentHP <= 0)
|
|
currentHP = 1;
|
|
if (currentHP > newMaxHP)
|
|
currentHP = newMaxHP;
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
SetMonData(mon, MON_DATA_HP, ¤tHP);
|
|
}
|
|
|
|
void BoxMonToMon(struct BoxPokemon *src, struct Pokemon *dest)
|
|
{
|
|
u32 value = 0;
|
|
dest->box = *src;
|
|
SetMonData(dest, MON_DATA_STATUS, &value);
|
|
SetMonData(dest, MON_DATA_HP, &value);
|
|
SetMonData(dest, MON_DATA_MAX_HP, &value);
|
|
value = MAIL_NONE;
|
|
SetMonData(dest, MON_DATA_MAIL, &value);
|
|
CalculateMonStats(dest);
|
|
}
|
|
|
|
static u8 GetLevelFromMonExp(struct Pokemon *mon)
|
|
{
|
|
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
u32 exp = GetMonData(mon, MON_DATA_EXP, NULL);
|
|
s32 level = 1;
|
|
|
|
while (level <= MAX_LEVEL && gExperienceTables[gSpeciesInfo[species].growthRate][level] <= exp)
|
|
level++;
|
|
|
|
return level - 1;
|
|
}
|
|
|
|
u8 GetLevelFromBoxMonExp(struct BoxPokemon *boxMon)
|
|
{
|
|
u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
|
|
u32 exp = GetBoxMonData(boxMon, MON_DATA_EXP, NULL);
|
|
s32 level = 1;
|
|
|
|
while (level <= MAX_LEVEL && gExperienceTables[gSpeciesInfo[species].growthRate][level] <= exp)
|
|
level++;
|
|
|
|
return level - 1;
|
|
}
|
|
|
|
u16 GiveMoveToMon(struct Pokemon *mon, u16 move)
|
|
{
|
|
return GiveMoveToBoxMon(&mon->box, move);
|
|
}
|
|
|
|
static u16 GiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move)
|
|
{
|
|
s32 i;
|
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
{
|
|
u16 existingMove = GetBoxMonData(boxMon, MON_DATA_MOVE1 + i, NULL);
|
|
if (!existingMove)
|
|
{
|
|
SetBoxMonData(boxMon, MON_DATA_MOVE1 + i, &move);
|
|
SetBoxMonData(boxMon, MON_DATA_PP1 + i, &gMovesInfo[move].pp);
|
|
return move;
|
|
}
|
|
if (existingMove == move)
|
|
return MON_ALREADY_KNOWS_MOVE;
|
|
}
|
|
return MON_HAS_MAX_MOVES;
|
|
}
|
|
|
|
u16 GiveMoveToBattleMon(struct BattlePokemon *mon, u16 move)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
{
|
|
if (!mon->moves[i])
|
|
{
|
|
mon->moves[i] = move;
|
|
mon->pp[i] = gMovesInfo[move].pp;
|
|
return move;
|
|
}
|
|
}
|
|
|
|
return MON_HAS_MAX_MOVES;
|
|
}
|
|
|
|
void SetMonMoveSlot(struct Pokemon *mon, u16 move, u8 slot)
|
|
{
|
|
SetMonData(mon, MON_DATA_MOVE1 + slot, &move);
|
|
SetMonData(mon, MON_DATA_PP1 + slot, &gMovesInfo[move].pp);
|
|
}
|
|
|
|
static void SetMonMoveSlot_KeepPP(struct Pokemon *mon, u16 move, u8 slot)
|
|
{
|
|
u8 ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES, NULL);
|
|
u8 currPP = GetMonData(mon, MON_DATA_PP1 + slot, NULL);
|
|
u8 newPP = CalculatePPWithBonus(move, ppBonuses, slot);
|
|
u16 finalPP = min(currPP, newPP);
|
|
|
|
SetMonData(mon, MON_DATA_MOVE1 + slot, &move);
|
|
SetMonData(mon, MON_DATA_PP1 + slot, &finalPP);
|
|
}
|
|
|
|
void SetBattleMonMoveSlot(struct BattlePokemon *mon, u16 move, u8 slot)
|
|
{
|
|
mon->moves[slot] = move;
|
|
mon->pp[slot] = gMovesInfo[move].pp;
|
|
}
|
|
|
|
static void GiveMonInitialMoveset(struct Pokemon *mon)
|
|
{
|
|
GiveBoxMonInitialMoveset(&mon->box);
|
|
}
|
|
|
|
static void GiveBoxMonInitialMoveset(struct BoxPokemon *boxMon)
|
|
{
|
|
u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
|
|
s32 level = GetLevelFromBoxMonExp(boxMon);
|
|
s32 i;
|
|
|
|
for (i = 0; gSpeciesInfo[species].levelUpLearnset[i].move != LEVEL_UP_MOVE_END; i++)
|
|
{
|
|
u16 moveLevel;
|
|
u16 move;
|
|
|
|
moveLevel = gSpeciesInfo[species].levelUpLearnset[i].level;
|
|
|
|
if (moveLevel > level)
|
|
break;
|
|
|
|
move = gSpeciesInfo[species].levelUpLearnset[i].move;
|
|
|
|
if (GiveMoveToBoxMon(boxMon, move) == MON_HAS_MAX_MOVES)
|
|
DeleteFirstMoveAndGiveMoveToBoxMon(boxMon, move);
|
|
}
|
|
}
|
|
|
|
u16 MonTryLearningNewMove(struct Pokemon *mon, bool8 firstMove)
|
|
{
|
|
u32 retVal = MOVE_NONE;
|
|
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
u8 level = GetMonData(mon, MON_DATA_LEVEL, NULL);
|
|
|
|
// since you can learn more than one move per level
|
|
// the game needs to know whether you decided to
|
|
// learn it or keep the old set to avoid asking
|
|
// you to learn the same move over and over again
|
|
if (firstMove)
|
|
{
|
|
sLearningMoveTableID = 0;
|
|
|
|
while (gSpeciesInfo[species].levelUpLearnset[sLearningMoveTableID].level != level)
|
|
{
|
|
sLearningMoveTableID++;
|
|
if (gSpeciesInfo[species].levelUpLearnset[sLearningMoveTableID].move == LEVEL_UP_MOVE_END)
|
|
return MOVE_NONE;
|
|
}
|
|
}
|
|
|
|
if (gSpeciesInfo[species].levelUpLearnset[sLearningMoveTableID].level == level)
|
|
{
|
|
gMoveToLearn = gSpeciesInfo[species].levelUpLearnset[sLearningMoveTableID].move;
|
|
sLearningMoveTableID++;
|
|
retVal = GiveMoveToMon(mon, gMoveToLearn);
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
void DeleteFirstMoveAndGiveMoveToMon(struct Pokemon *mon, u16 move)
|
|
{
|
|
s32 i;
|
|
u16 moves[MAX_MON_MOVES];
|
|
u8 pp[MAX_MON_MOVES];
|
|
u8 ppBonuses;
|
|
|
|
for (i = 0; i < MAX_MON_MOVES - 1; i++)
|
|
{
|
|
moves[i] = GetMonData(mon, MON_DATA_MOVE2 + i, NULL);
|
|
pp[i] = GetMonData(mon, MON_DATA_PP2 + i, NULL);
|
|
}
|
|
|
|
ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES, NULL);
|
|
ppBonuses >>= 2;
|
|
moves[MAX_MON_MOVES - 1] = move;
|
|
pp[MAX_MON_MOVES - 1] = gMovesInfo[move].pp;
|
|
|
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
{
|
|
SetMonData(mon, MON_DATA_MOVE1 + i, &moves[i]);
|
|
SetMonData(mon, MON_DATA_PP1 + i, &pp[i]);
|
|
}
|
|
|
|
SetMonData(mon, MON_DATA_PP_BONUSES, &ppBonuses);
|
|
}
|
|
|
|
static void DeleteFirstMoveAndGiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move)
|
|
{
|
|
s32 i;
|
|
u16 moves[MAX_MON_MOVES];
|
|
u8 pp[MAX_MON_MOVES];
|
|
u8 ppBonuses;
|
|
|
|
for (i = 0; i < MAX_MON_MOVES - 1; i++)
|
|
{
|
|
moves[i] = GetBoxMonData(boxMon, MON_DATA_MOVE2 + i, NULL);
|
|
pp[i] = GetBoxMonData(boxMon, MON_DATA_PP2 + i, NULL);
|
|
}
|
|
|
|
ppBonuses = GetBoxMonData(boxMon, MON_DATA_PP_BONUSES, NULL);
|
|
ppBonuses >>= 2;
|
|
moves[MAX_MON_MOVES - 1] = move;
|
|
pp[MAX_MON_MOVES - 1] = gMovesInfo[move].pp;
|
|
|
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
{
|
|
SetBoxMonData(boxMon, MON_DATA_MOVE1 + i, &moves[i]);
|
|
SetBoxMonData(boxMon, MON_DATA_PP1 + i, &pp[i]);
|
|
}
|
|
|
|
SetBoxMonData(boxMon, MON_DATA_PP_BONUSES, &ppBonuses);
|
|
}
|
|
|
|
#define APPLY_STAT_MOD(var, mon, stat, statIndex) \
|
|
{ \
|
|
(var) = (stat) * (gStatStageRatios)[(mon)->statStages[(statIndex)]][0]; \
|
|
(var) /= (gStatStageRatios)[(mon)->statStages[(statIndex)]][1]; \
|
|
}
|
|
|
|
// Own function in pokeemerald
|
|
#define ShouldGetStatBadgeBoost(flag, battler)\
|
|
(!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_EREADER_TRAINER)) && FlagGet(flag) && GetBattlerSide(battler) == B_SIDE_PLAYER)
|
|
|
|
|
|
s32 CalculateBaseDamageOld(struct BattlePokemon *attacker, struct BattlePokemon *defender, u32 move, u32 sideStatus, u16 powerOverride, u8 typeOverride, u8 battlerIdAtk, u8 battlerIdDef)
|
|
{
|
|
u32 i;
|
|
s32 damage = 0;
|
|
s32 damageHelper;
|
|
u8 type;
|
|
u16 attack, defense;
|
|
u16 spAttack, spDefense;
|
|
u8 defenderHoldEffect;
|
|
u8 defenderHoldEffectParam;
|
|
u8 attackerHoldEffect;
|
|
u8 attackerHoldEffectParam;
|
|
|
|
if (!powerOverride)
|
|
gBattleMovePower = gMovesInfo[move].power;
|
|
else
|
|
gBattleMovePower = powerOverride;
|
|
|
|
if (!typeOverride)
|
|
type = gMovesInfo[move].type;
|
|
else
|
|
type = typeOverride & DYNAMIC_TYPE_MASK;
|
|
|
|
attack = attacker->attack;
|
|
defense = defender->defense;
|
|
spAttack = attacker->spAttack;
|
|
spDefense = defender->spDefense;
|
|
|
|
// Get attacker hold item info
|
|
if (attacker->item == ITEM_ENIGMA_BERRY)
|
|
{
|
|
attackerHoldEffect = gEnigmaBerries[battlerIdAtk].holdEffect;
|
|
attackerHoldEffectParam = gEnigmaBerries[battlerIdAtk].holdEffectParam;
|
|
}
|
|
else
|
|
{
|
|
attackerHoldEffect = ItemId_GetHoldEffect(attacker->item);
|
|
attackerHoldEffectParam = ItemId_GetHoldEffectParam(attacker->item);
|
|
}
|
|
|
|
// Get defender hold item info
|
|
if (defender->item == ITEM_ENIGMA_BERRY)
|
|
{
|
|
defenderHoldEffect = gEnigmaBerries[battlerIdDef].holdEffect;
|
|
defenderHoldEffectParam = gEnigmaBerries[battlerIdDef].holdEffectParam;
|
|
}
|
|
else
|
|
{
|
|
defenderHoldEffect = ItemId_GetHoldEffect(defender->item);
|
|
defenderHoldEffectParam = ItemId_GetHoldEffectParam(defender->item);
|
|
}
|
|
|
|
if (attacker->ability == ABILITY_HUGE_POWER || attacker->ability == ABILITY_PURE_POWER)
|
|
attack *= 2;
|
|
|
|
if (ShouldGetStatBadgeBoost(FLAG_BADGE01_GET, battlerIdAtk))
|
|
attack = (110 * attack) / 100;
|
|
if (ShouldGetStatBadgeBoost(FLAG_BADGE05_GET, battlerIdDef))
|
|
defense = (110 * defense) / 100;
|
|
if (ShouldGetStatBadgeBoost(FLAG_BADGE07_GET, battlerIdAtk))
|
|
spAttack = (110 * spAttack) / 100;
|
|
if (ShouldGetStatBadgeBoost(FLAG_BADGE07_GET, battlerIdDef))
|
|
spDefense = (110 * spDefense) / 100;
|
|
|
|
// Apply type-bonus hold item
|
|
for (i = 0; i < ARRAY_COUNT(sHoldEffectToType); i++)
|
|
{
|
|
if (attackerHoldEffect == sHoldEffectToType[i][0]
|
|
&& type == sHoldEffectToType[i][1])
|
|
{
|
|
if (IS_TYPE_PHYSICAL(type))
|
|
attack = (attack * (attackerHoldEffectParam + 100)) / 100;
|
|
else
|
|
spAttack = (spAttack * (attackerHoldEffectParam + 100)) / 100;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Apply boosts from hold items
|
|
if (attackerHoldEffect == HOLD_EFFECT_CHOICE_BAND)
|
|
attack = (150 * attack) / 100;
|
|
if (attackerHoldEffect == HOLD_EFFECT_SOUL_DEW && !(gBattleTypeFlags & (BATTLE_TYPE_BATTLE_TOWER)) && (attacker->species == SPECIES_LATIAS || attacker->species == SPECIES_LATIOS))
|
|
spAttack = (150 * spAttack) / 100;
|
|
if (defenderHoldEffect == HOLD_EFFECT_SOUL_DEW && !(gBattleTypeFlags & (BATTLE_TYPE_BATTLE_TOWER)) && (defender->species == SPECIES_LATIAS || defender->species == SPECIES_LATIOS))
|
|
spDefense = (150 * spDefense) / 100;
|
|
if (attackerHoldEffect == HOLD_EFFECT_DEEP_SEA_TOOTH && attacker->species == SPECIES_CLAMPERL)
|
|
spAttack *= 2;
|
|
if (defenderHoldEffect == HOLD_EFFECT_DEEP_SEA_SCALE && defender->species == SPECIES_CLAMPERL)
|
|
spDefense *= 2;
|
|
if (attackerHoldEffect == HOLD_EFFECT_LIGHT_BALL && attacker->species == SPECIES_PIKACHU)
|
|
spAttack *= 2;
|
|
if (defenderHoldEffect == HOLD_EFFECT_METAL_POWDER && defender->species == SPECIES_DITTO)
|
|
defense *= 2;
|
|
if (attackerHoldEffect == HOLD_EFFECT_THICK_CLUB && (attacker->species == SPECIES_CUBONE || attacker->species == SPECIES_MAROWAK))
|
|
attack *= 2;
|
|
if (defender->ability == ABILITY_THICK_FAT && (type == TYPE_FIRE || type == TYPE_ICE))
|
|
spAttack /= 2;
|
|
if (attacker->ability == ABILITY_HUSTLE)
|
|
attack = (150 * attack) / 100;
|
|
if (attacker->ability == ABILITY_PLUS && ABILITY_ON_FIELD2(ABILITY_MINUS))
|
|
spAttack = (150 * spAttack) / 100;
|
|
if (attacker->ability == ABILITY_MINUS && ABILITY_ON_FIELD2(ABILITY_PLUS))
|
|
spAttack = (150 * spAttack) / 100;
|
|
if (attacker->ability == ABILITY_GUTS && attacker->status1)
|
|
attack = (150 * attack) / 100;
|
|
if (defender->ability == ABILITY_MARVEL_SCALE && defender->status1)
|
|
defense = (150 * defense) / 100;
|
|
if (type == TYPE_ELECTRIC && AbilityBattleEffects(ABILITYEFFECT_FIELD_SPORT, 0, 0, ABILITYEFFECT_MUD_SPORT, 0))
|
|
gBattleMovePower /= 2;
|
|
if (type == TYPE_FIRE && AbilityBattleEffects(ABILITYEFFECT_FIELD_SPORT, 0, 0, ABILITYEFFECT_WATER_SPORT, 0))
|
|
gBattleMovePower /= 2;
|
|
if (type == TYPE_GRASS && attacker->ability == ABILITY_OVERGROW && attacker->hp <= (attacker->maxHP / 3))
|
|
gBattleMovePower = (150 * gBattleMovePower) / 100;
|
|
if (type == TYPE_FIRE && attacker->ability == ABILITY_BLAZE && attacker->hp <= (attacker->maxHP / 3))
|
|
gBattleMovePower = (150 * gBattleMovePower) / 100;
|
|
if (type == TYPE_WATER && attacker->ability == ABILITY_TORRENT && attacker->hp <= (attacker->maxHP / 3))
|
|
gBattleMovePower = (150 * gBattleMovePower) / 100;
|
|
if (type == TYPE_BUG && attacker->ability == ABILITY_SWARM && attacker->hp <= (attacker->maxHP / 3))
|
|
gBattleMovePower = (150 * gBattleMovePower) / 100;
|
|
|
|
// Self-destruct / Explosion cut defense in half
|
|
if (gMovesInfo[gCurrentMove].effect == EFFECT_EXPLOSION)
|
|
defense /= 2;
|
|
|
|
if (IS_TYPE_PHYSICAL(type))
|
|
{
|
|
if (gCritMultiplier == 2)
|
|
{
|
|
// Critical hit, if attacker has lost attack stat stages then ignore stat drop
|
|
if (attacker->statStages[STAT_ATK] > DEFAULT_STAT_STAGE)
|
|
APPLY_STAT_MOD(damage, attacker, attack, STAT_ATK)
|
|
else
|
|
damage = attack;
|
|
}
|
|
else
|
|
APPLY_STAT_MOD(damage, attacker, attack, STAT_ATK)
|
|
|
|
damage = damage * gBattleMovePower;
|
|
damage *= (2 * attacker->level / 5 + 2);
|
|
|
|
if (gCritMultiplier == 2)
|
|
{
|
|
// Critical hit, if defender has gained defense stat stages then ignore stat increase
|
|
if (defender->statStages[STAT_DEF] < DEFAULT_STAT_STAGE)
|
|
APPLY_STAT_MOD(damageHelper, defender, defense, STAT_DEF)
|
|
else
|
|
damageHelper = defense;
|
|
}
|
|
else
|
|
APPLY_STAT_MOD(damageHelper, defender, defense, STAT_DEF)
|
|
|
|
damage = damage / damageHelper;
|
|
damage /= 50;
|
|
|
|
// Burn cuts attack in half
|
|
if ((attacker->status1 & STATUS1_BURN) && attacker->ability != ABILITY_GUTS)
|
|
damage /= 2;
|
|
|
|
// Apply Reflect
|
|
if ((sideStatus & SIDE_STATUS_REFLECT) && gCritMultiplier == 1)
|
|
{
|
|
if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && CountAliveMonsInBattle(BATTLE_ALIVE_SIDE, gBattlerTarget) == 2)
|
|
damage = 2 * (damage / 3);
|
|
else
|
|
damage /= 2;
|
|
}
|
|
|
|
// Moves hitting both targets do half damage in double battles
|
|
if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && gMovesInfo[move].target == MOVE_TARGET_BOTH && CountAliveMonsInBattle(BATTLE_ALIVE_SIDE, gBattlerTarget) == 2)
|
|
damage /= 2;
|
|
|
|
// Moves always do at least 1 damage.
|
|
if (damage == 0)
|
|
damage = 1;
|
|
}
|
|
|
|
if (type == TYPE_MYSTERY)
|
|
damage = 0; // is ??? type. does 0 damage.
|
|
|
|
if (IS_TYPE_SPECIAL(type))
|
|
{
|
|
if (gCritMultiplier == 2)
|
|
{
|
|
// Critical hit, if attacker has lost sp. attack stat stages then ignore stat drop
|
|
if (attacker->statStages[STAT_SPATK] > DEFAULT_STAT_STAGE)
|
|
APPLY_STAT_MOD(damage, attacker, spAttack, STAT_SPATK)
|
|
else
|
|
damage = spAttack;
|
|
}
|
|
else
|
|
APPLY_STAT_MOD(damage, attacker, spAttack, STAT_SPATK)
|
|
|
|
damage = damage * gBattleMovePower;
|
|
damage *= (2 * attacker->level / 5 + 2);
|
|
|
|
if (gCritMultiplier == 2)
|
|
{
|
|
// Critical hit, if defender has gained sp. defense stat stages then ignore stat increase
|
|
if (defender->statStages[STAT_SPDEF] < DEFAULT_STAT_STAGE)
|
|
APPLY_STAT_MOD(damageHelper, defender, spDefense, STAT_SPDEF)
|
|
else
|
|
damageHelper = spDefense;
|
|
}
|
|
else
|
|
APPLY_STAT_MOD(damageHelper, defender, spDefense, STAT_SPDEF)
|
|
|
|
damage = (damage / damageHelper);
|
|
damage /= 50;
|
|
|
|
// Apply Lightscreen
|
|
if ((sideStatus & SIDE_STATUS_LIGHTSCREEN) && gCritMultiplier == 1)
|
|
{
|
|
if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && CountAliveMonsInBattle(BATTLE_ALIVE_SIDE, gBattlerTarget) == 2)
|
|
damage = 2 * (damage / 3);
|
|
else
|
|
damage /= 2;
|
|
}
|
|
|
|
// Moves hitting both targets do half damage in double battles
|
|
if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && gMovesInfo[move].target == MOVE_TARGET_BOTH && CountAliveMonsInBattle(BATTLE_ALIVE_SIDE, gBattlerTarget) == 2)
|
|
damage /= 2;
|
|
|
|
// Are effects of weather negated with cloud nine or air lock
|
|
if (WEATHER_HAS_EFFECT2)
|
|
{
|
|
// Rain weakens Fire, boosts Water
|
|
if (gBattleWeather & B_WEATHER_RAIN_TEMPORARY)
|
|
{
|
|
switch (type)
|
|
{
|
|
case TYPE_FIRE:
|
|
damage /= 2;
|
|
break;
|
|
case TYPE_WATER:
|
|
damage = (15 * damage) / 10;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Any weather except sun weakens solar beam
|
|
if ((gBattleWeather & (B_WEATHER_RAIN | B_WEATHER_SANDSTORM | B_WEATHER_HAIL_TEMPORARY)) && gCurrentMove == MOVE_SOLAR_BEAM)
|
|
damage /= 2;
|
|
|
|
// Sun boosts Fire, weakens Water
|
|
if (gBattleWeather & B_WEATHER_SUN)
|
|
{
|
|
switch (type)
|
|
{
|
|
case TYPE_FIRE:
|
|
damage = (15 * damage) / 10;
|
|
break;
|
|
case TYPE_WATER:
|
|
damage /= 2;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Flash fire triggered
|
|
if ((gBattleResources->flags->flags[battlerIdAtk] & RESOURCE_FLAG_FLASH_FIRE) && type == TYPE_FIRE)
|
|
damage = (15 * damage) / 10;
|
|
}
|
|
|
|
return damage + 2;
|
|
}
|
|
|
|
u8 CountAliveMonsInBattle(u8 caseId, u32 battler)
|
|
{
|
|
s32 i;
|
|
u8 retVal = 0;
|
|
|
|
switch (caseId)
|
|
{
|
|
case BATTLE_ALIVE_EXCEPT_BATTLER:
|
|
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
|
|
{
|
|
if (i != battler && !(gAbsentBattlerFlags & gBitTable[i]))
|
|
retVal++;
|
|
}
|
|
break;
|
|
case BATTLE_ALIVE_SIDE:
|
|
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
|
|
{
|
|
if (GetBattlerSide(i) == GetBattlerSide(battler) && !(gAbsentBattlerFlags & gBitTable[i]))
|
|
retVal++;
|
|
}
|
|
break;
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
u8 GetDefaultMoveTarget(u8 battlerId)
|
|
{
|
|
u8 opposing = BATTLE_OPPOSITE(GetBattlerPosition(battlerId) & BIT_SIDE);
|
|
|
|
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
|
|
return GetBattlerAtPosition(opposing);
|
|
if (CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_BATTLER, gActiveBattler) > 1)
|
|
{
|
|
u8 position;
|
|
|
|
if ((Random() & 1) == 0)
|
|
position = BATTLE_PARTNER(opposing);
|
|
else
|
|
position = opposing;
|
|
return GetBattlerAtPosition(position);
|
|
}
|
|
else
|
|
{
|
|
if ((gAbsentBattlerFlags & gBitTable[opposing]))
|
|
return GetBattlerAtPosition(BATTLE_PARTNER(opposing));
|
|
else
|
|
return GetBattlerAtPosition(opposing);
|
|
}
|
|
}
|
|
|
|
u8 GetMonGender(struct Pokemon *mon)
|
|
{
|
|
return GetBoxMonGender(&mon->box);
|
|
}
|
|
|
|
u8 GetBoxMonGender(struct BoxPokemon *boxMon)
|
|
{
|
|
u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
|
|
u32 personality = GetBoxMonData(boxMon, MON_DATA_PERSONALITY, NULL);
|
|
|
|
switch (gSpeciesInfo[species].genderRatio)
|
|
{
|
|
case MON_MALE:
|
|
case MON_FEMALE:
|
|
case MON_GENDERLESS:
|
|
return gSpeciesInfo[species].genderRatio;
|
|
}
|
|
|
|
if (gSpeciesInfo[species].genderRatio > (personality & 0xFF))
|
|
return MON_FEMALE;
|
|
else
|
|
return MON_MALE;
|
|
}
|
|
|
|
u8 GetGenderFromSpeciesAndPersonality(u16 species, u32 personality)
|
|
{
|
|
switch (gSpeciesInfo[species].genderRatio)
|
|
{
|
|
case MON_MALE:
|
|
case MON_FEMALE:
|
|
case MON_GENDERLESS:
|
|
return gSpeciesInfo[species].genderRatio;
|
|
}
|
|
|
|
if (gSpeciesInfo[species].genderRatio > (personality & 0xFF))
|
|
return MON_FEMALE;
|
|
else
|
|
return MON_MALE;
|
|
}
|
|
|
|
void SetMultiuseSpriteTemplateToPokemon(u16 speciesTag, u8 battlerPosition)
|
|
{
|
|
if (gMonSpritesGfxPtr != NULL)
|
|
{
|
|
if (battlerPosition >= MAX_BATTLERS_COUNT)
|
|
battlerPosition = 0;
|
|
|
|
gMultiuseSpriteTemplate = gMonSpritesGfxPtr->templates[battlerPosition];
|
|
}
|
|
else
|
|
{
|
|
if (sMonSpritesGfxManager)
|
|
{
|
|
if (battlerPosition >= (s8)sMonSpritesGfxManager->battlePosition) // why a cast?!? changing the unk0_2 type to s8 causes extra shifts, but a cast is the correct fix. why, compiler?
|
|
battlerPosition = 0;
|
|
|
|
gMultiuseSpriteTemplate = sMonSpritesGfxManager->templates[battlerPosition];
|
|
}
|
|
else
|
|
{
|
|
if (battlerPosition >= MAX_BATTLERS_COUNT)
|
|
battlerPosition = 0;
|
|
|
|
gMultiuseSpriteTemplate = gSpriteTemplates_Battlers[battlerPosition];
|
|
}
|
|
}
|
|
gMultiuseSpriteTemplate.paletteTag = speciesTag;
|
|
gMultiuseSpriteTemplate.anims = gAnims_MonPic;
|
|
}
|
|
|
|
void SetMultiuseSpriteTemplateToTrainerBack(u16 trainerSpriteId, u8 battlerPosition)
|
|
{
|
|
gMultiuseSpriteTemplate.paletteTag = trainerSpriteId;
|
|
if (battlerPosition == B_POSITION_PLAYER_LEFT || battlerPosition == B_POSITION_PLAYER_RIGHT)
|
|
{
|
|
gMultiuseSpriteTemplate = sTrainerBackSpriteTemplates[trainerSpriteId];
|
|
gMultiuseSpriteTemplate.anims = gTrainerBackAnimsPtrTable[trainerSpriteId];
|
|
}
|
|
else
|
|
{
|
|
if (gMonSpritesGfxPtr != NULL)
|
|
gMultiuseSpriteTemplate = gMonSpritesGfxPtr->templates[battlerPosition];
|
|
else
|
|
gMultiuseSpriteTemplate = gSpriteTemplates_Battlers[battlerPosition];
|
|
gMultiuseSpriteTemplate.anims = gTrainerFrontAnimsPtrTable[trainerSpriteId];
|
|
}
|
|
}
|
|
|
|
static void EncryptBoxMon(struct BoxPokemon *boxMon)
|
|
{
|
|
u32 i;
|
|
for (i = 0; i < ARRAY_COUNT(boxMon->secure.raw); i++)
|
|
{
|
|
boxMon->secure.raw[i] ^= boxMon->personality;
|
|
boxMon->secure.raw[i] ^= boxMon->otId;
|
|
}
|
|
}
|
|
|
|
static void DecryptBoxMon(struct BoxPokemon *boxMon)
|
|
{
|
|
u32 i;
|
|
for (i = 0; i < ARRAY_COUNT(boxMon->secure.raw); i++)
|
|
{
|
|
boxMon->secure.raw[i] ^= boxMon->otId;
|
|
boxMon->secure.raw[i] ^= boxMon->personality;
|
|
}
|
|
}
|
|
|
|
#define SUBSTRUCT_CASE(n, v1, v2, v3, v4) \
|
|
case n: \
|
|
{ \
|
|
union PokemonSubstruct *substructs0 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs1 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs2 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs3 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs4 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs5 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs6 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs7 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs8 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs9 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs10 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs11 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs12 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs13 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs14 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs15 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs16 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs17 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs18 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs19 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs20 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs21 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs22 = boxMon->secure.substructs; \
|
|
union PokemonSubstruct *substructs23 = boxMon->secure.substructs; \
|
|
\
|
|
switch (substructType) \
|
|
{ \
|
|
case 0: \
|
|
substruct = &substructs ## n [v1]; \
|
|
break; \
|
|
case 1: \
|
|
substruct = &substructs ## n [v2]; \
|
|
break; \
|
|
case 2: \
|
|
substruct = &substructs ## n [v3]; \
|
|
break; \
|
|
case 3: \
|
|
substruct = &substructs ## n [v4]; \
|
|
break; \
|
|
} \
|
|
break; \
|
|
} \
|
|
|
|
static union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 personality, u8 substructType)
|
|
{
|
|
union PokemonSubstruct *substruct = NULL;
|
|
|
|
switch (personality % 24)
|
|
{
|
|
SUBSTRUCT_CASE( 0,0,1,2,3)
|
|
SUBSTRUCT_CASE( 1,0,1,3,2)
|
|
SUBSTRUCT_CASE( 2,0,2,1,3)
|
|
SUBSTRUCT_CASE( 3,0,3,1,2)
|
|
SUBSTRUCT_CASE( 4,0,2,3,1)
|
|
SUBSTRUCT_CASE( 5,0,3,2,1)
|
|
SUBSTRUCT_CASE( 6,1,0,2,3)
|
|
SUBSTRUCT_CASE( 7,1,0,3,2)
|
|
SUBSTRUCT_CASE( 8,2,0,1,3)
|
|
SUBSTRUCT_CASE( 9,3,0,1,2)
|
|
SUBSTRUCT_CASE(10,2,0,3,1)
|
|
SUBSTRUCT_CASE(11,3,0,2,1)
|
|
SUBSTRUCT_CASE(12,1,2,0,3)
|
|
SUBSTRUCT_CASE(13,1,3,0,2)
|
|
SUBSTRUCT_CASE(14,2,1,0,3)
|
|
SUBSTRUCT_CASE(15,3,1,0,2)
|
|
SUBSTRUCT_CASE(16,2,3,0,1)
|
|
SUBSTRUCT_CASE(17,3,2,0,1)
|
|
SUBSTRUCT_CASE(18,1,2,3,0)
|
|
SUBSTRUCT_CASE(19,1,3,2,0)
|
|
SUBSTRUCT_CASE(20,2,1,3,0)
|
|
SUBSTRUCT_CASE(21,3,1,2,0)
|
|
SUBSTRUCT_CASE(22,2,3,1,0)
|
|
SUBSTRUCT_CASE(23,3,2,1,0)
|
|
}
|
|
|
|
return substruct;
|
|
}
|
|
|
|
/* GameFreak called GetMonData with either 2 or 3 arguments, for type
|
|
* safety we have a GetMonData macro (in include/pokemon.h) which
|
|
* dispatches to either GetMonData2 or GetMonData3 based on the number
|
|
* of arguments. */
|
|
u32 GetMonData3(struct Pokemon *mon, s32 field, u8 *data)
|
|
{
|
|
u32 ret;
|
|
|
|
switch (field)
|
|
{
|
|
case MON_DATA_STATUS:
|
|
ret = mon->status;
|
|
break;
|
|
case MON_DATA_LEVEL:
|
|
ret = mon->level;
|
|
break;
|
|
case MON_DATA_HP:
|
|
ret = mon->hp;
|
|
break;
|
|
case MON_DATA_MAX_HP:
|
|
ret = mon->maxHP;
|
|
break;
|
|
case MON_DATA_ATK:
|
|
ret = mon->attack;
|
|
break;
|
|
case MON_DATA_DEF:
|
|
ret = mon->defense;
|
|
break;
|
|
case MON_DATA_SPEED:
|
|
ret = mon->speed;
|
|
break;
|
|
case MON_DATA_SPATK:
|
|
ret = mon->spAttack;
|
|
break;
|
|
case MON_DATA_SPDEF:
|
|
ret = mon->spDefense;
|
|
break;
|
|
case MON_DATA_ATK2:
|
|
ret = mon->attack;
|
|
break;
|
|
case MON_DATA_DEF2:
|
|
ret = mon->defense;
|
|
break;
|
|
case MON_DATA_SPEED2:
|
|
ret = mon->speed;
|
|
break;
|
|
case MON_DATA_SPATK2:
|
|
ret = mon->spAttack;
|
|
break;
|
|
case MON_DATA_SPDEF2:
|
|
ret = mon->spDefense;
|
|
break;
|
|
case MON_DATA_MAIL:
|
|
ret = mon->mail;
|
|
break;
|
|
default:
|
|
ret = GetBoxMonData(&mon->box, field, data);
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
u32 GetMonData2(struct Pokemon *mon, s32 field)
|
|
{
|
|
return GetMonData3(mon, field, NULL);
|
|
}
|
|
|
|
struct EvolutionTrackerBitfield
|
|
{
|
|
u16 a: 5;
|
|
u16 b: 4;
|
|
u16 unused: 7;
|
|
};
|
|
|
|
union EvolutionTracker
|
|
{
|
|
u16 value;
|
|
struct EvolutionTrackerBitfield asField;
|
|
};
|
|
|
|
/* GameFreak called GetBoxMonData with either 2 or 3 arguments, for type
|
|
* safety we have a GetBoxMonData macro (in include/pokemon.h) which
|
|
* dispatches to either GetBoxMonData2 or GetBoxMonData3 based on the
|
|
* number of arguments. */
|
|
u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data)
|
|
{
|
|
s32 i;
|
|
u32 retVal = 0;
|
|
struct PokemonSubstruct0 *substruct0 = NULL;
|
|
struct PokemonSubstruct1 *substruct1 = NULL;
|
|
struct PokemonSubstruct2 *substruct2 = NULL;
|
|
struct PokemonSubstruct3 *substruct3 = NULL;
|
|
union EvolutionTracker evoTracker;
|
|
|
|
// Any field greater than MON_DATA_ENCRYPT_SEPARATOR is encrypted and must be treated as such
|
|
if (field > MON_DATA_ENCRYPT_SEPARATOR)
|
|
{
|
|
substruct0 = &(GetSubstruct(boxMon, boxMon->personality, 0)->type0);
|
|
substruct1 = &(GetSubstruct(boxMon, boxMon->personality, 1)->type1);
|
|
substruct2 = &(GetSubstruct(boxMon, boxMon->personality, 2)->type2);
|
|
substruct3 = &(GetSubstruct(boxMon, boxMon->personality, 3)->type3);
|
|
|
|
DecryptBoxMon(boxMon);
|
|
|
|
if (CalculateBoxMonChecksum(boxMon) != boxMon->checksum)
|
|
{
|
|
boxMon->isBadEgg = TRUE;
|
|
boxMon->isEgg = TRUE;
|
|
substruct3->isEgg = TRUE;
|
|
}
|
|
|
|
switch (field)
|
|
{
|
|
case MON_DATA_NICKNAME:
|
|
{
|
|
if (boxMon->isBadEgg)
|
|
{
|
|
for (retVal = 0;
|
|
retVal < POKEMON_NAME_LENGTH && gText_BadEgg[retVal] != EOS;
|
|
data[retVal] = gText_BadEgg[retVal], retVal++) {}
|
|
|
|
data[retVal] = EOS;
|
|
}
|
|
else if (boxMon->isEgg)
|
|
{
|
|
StringCopy(data, gText_EggNickname);
|
|
retVal = StringLength(data);
|
|
}
|
|
else if (boxMon->language == LANGUAGE_JAPANESE)
|
|
{
|
|
data[0] = EXT_CTRL_CODE_BEGIN;
|
|
data[1] = EXT_CTRL_CODE_JPN;
|
|
|
|
for (retVal = 2, i = 0;
|
|
i < 5 && boxMon->nickname[i] != EOS;
|
|
data[retVal] = boxMon->nickname[i], retVal++, i++) {}
|
|
|
|
data[retVal++] = EXT_CTRL_CODE_BEGIN;
|
|
data[retVal++] = EXT_CTRL_CODE_ENG;
|
|
data[retVal] = EOS;
|
|
}
|
|
else
|
|
{
|
|
// if (DECAP_ENABLED && !DECAP_NICKNAMES && IsStringAddrSafe(data, POKEMON_NAME_LENGTH))
|
|
// *data++ = CHAR_FIXED_CASE;
|
|
retVal = 0;
|
|
while (retVal < min(sizeof(boxMon->nickname), POKEMON_NAME_LENGTH))
|
|
{
|
|
data[retVal] = boxMon->nickname[retVal];
|
|
retVal++;
|
|
}
|
|
|
|
// Vanilla Pokémon have 0s in nickname11 and nickname12
|
|
// so if both are 0 we assume that this is a vanilla
|
|
// Pokémon and replace them with EOS. This means that
|
|
// two CHAR_SPACE at the end of a nickname are trimmed.
|
|
if (POKEMON_NAME_LENGTH >= 12)
|
|
{
|
|
if (substruct0->nickname11 == 0 && substruct0->nickname12 == 0)
|
|
{
|
|
data[retVal++] = EOS;
|
|
data[retVal++] = EOS;
|
|
}
|
|
else
|
|
{
|
|
data[retVal++] = substruct0->nickname11;
|
|
data[retVal++] = substruct0->nickname12;
|
|
}
|
|
}
|
|
else if (POKEMON_NAME_LENGTH >= 11)
|
|
{
|
|
if (substruct0->nickname11 == 0)
|
|
{
|
|
data[retVal++] = EOS;
|
|
}
|
|
else
|
|
{
|
|
data[retVal++] = substruct0->nickname11;
|
|
}
|
|
}
|
|
|
|
data[retVal] = EOS;
|
|
}
|
|
break;
|
|
}
|
|
case MON_DATA_SPECIES:
|
|
retVal = boxMon->isBadEgg ? SPECIES_EGG : substruct0->species;
|
|
break;
|
|
case MON_DATA_HELD_ITEM:
|
|
retVal = substruct0->heldItem;
|
|
break;
|
|
case MON_DATA_EXP:
|
|
retVal = substruct0->experience;
|
|
break;
|
|
case MON_DATA_PP_BONUSES:
|
|
retVal = substruct0->ppBonuses;
|
|
break;
|
|
case MON_DATA_FRIENDSHIP:
|
|
retVal = substruct0->friendship;
|
|
break;
|
|
case MON_DATA_MOVE1:
|
|
retVal = substruct1->move1;
|
|
break;
|
|
case MON_DATA_MOVE2:
|
|
retVal = substruct1->move2;
|
|
break;
|
|
case MON_DATA_MOVE3:
|
|
retVal = substruct1->move3;
|
|
break;
|
|
case MON_DATA_MOVE4:
|
|
retVal = substruct1->move4;
|
|
break;
|
|
case MON_DATA_PP1:
|
|
retVal = substruct1->pp1;
|
|
break;
|
|
case MON_DATA_PP2:
|
|
retVal = substruct1->pp2;
|
|
break;
|
|
case MON_DATA_PP3:
|
|
retVal = substruct1->pp3;
|
|
break;
|
|
case MON_DATA_PP4:
|
|
retVal = substruct1->pp4;
|
|
break;
|
|
case MON_DATA_HP_EV:
|
|
retVal = substruct2->hpEV;
|
|
break;
|
|
case MON_DATA_ATK_EV:
|
|
retVal = substruct2->attackEV;
|
|
break;
|
|
case MON_DATA_DEF_EV:
|
|
retVal = substruct2->defenseEV;
|
|
break;
|
|
case MON_DATA_SPEED_EV:
|
|
retVal = substruct2->speedEV;
|
|
break;
|
|
case MON_DATA_SPATK_EV:
|
|
retVal = substruct2->spAttackEV;
|
|
break;
|
|
case MON_DATA_SPDEF_EV:
|
|
retVal = substruct2->spDefenseEV;
|
|
break;
|
|
case MON_DATA_COOL:
|
|
retVal = substruct2->cool;
|
|
break;
|
|
case MON_DATA_BEAUTY:
|
|
retVal = substruct2->beauty;
|
|
break;
|
|
case MON_DATA_CUTE:
|
|
retVal = substruct2->cute;
|
|
break;
|
|
case MON_DATA_SMART:
|
|
retVal = substruct2->smart;
|
|
break;
|
|
case MON_DATA_TOUGH:
|
|
retVal = substruct2->tough;
|
|
break;
|
|
case MON_DATA_SHEEN:
|
|
retVal = substruct2->sheen;
|
|
break;
|
|
case MON_DATA_POKERUS:
|
|
retVal = substruct3->pokerus;
|
|
break;
|
|
case MON_DATA_MET_LOCATION:
|
|
retVal = substruct3->metLocation;
|
|
break;
|
|
case MON_DATA_MET_LEVEL:
|
|
retVal = substruct3->metLevel;
|
|
break;
|
|
case MON_DATA_MET_GAME:
|
|
retVal = substruct3->metGame;
|
|
break;
|
|
case MON_DATA_POKEBALL:
|
|
retVal = substruct0->pokeball;
|
|
break;
|
|
case MON_DATA_OT_GENDER:
|
|
retVal = substruct3->otGender;
|
|
break;
|
|
case MON_DATA_HP_IV:
|
|
retVal = substruct3->hpIV;
|
|
break;
|
|
case MON_DATA_ATK_IV:
|
|
retVal = substruct3->attackIV;
|
|
break;
|
|
case MON_DATA_DEF_IV:
|
|
retVal = substruct3->defenseIV;
|
|
break;
|
|
case MON_DATA_SPEED_IV:
|
|
retVal = substruct3->speedIV;
|
|
break;
|
|
case MON_DATA_SPATK_IV:
|
|
retVal = substruct3->spAttackIV;
|
|
break;
|
|
case MON_DATA_SPDEF_IV:
|
|
retVal = substruct3->spDefenseIV;
|
|
break;
|
|
case MON_DATA_IS_EGG:
|
|
retVal = substruct3->isEgg;
|
|
break;
|
|
case MON_DATA_ABILITY_NUM:
|
|
retVal = substruct3->abilityNum;
|
|
break;
|
|
case MON_DATA_COOL_RIBBON:
|
|
retVal = substruct3->coolRibbon;
|
|
break;
|
|
case MON_DATA_BEAUTY_RIBBON:
|
|
retVal = substruct3->beautyRibbon;
|
|
break;
|
|
case MON_DATA_CUTE_RIBBON:
|
|
retVal = substruct3->cuteRibbon;
|
|
break;
|
|
case MON_DATA_SMART_RIBBON:
|
|
retVal = substruct3->smartRibbon;
|
|
break;
|
|
case MON_DATA_TOUGH_RIBBON:
|
|
retVal = substruct3->toughRibbon;
|
|
break;
|
|
case MON_DATA_CHAMPION_RIBBON:
|
|
retVal = substruct3->championRibbon;
|
|
break;
|
|
case MON_DATA_WINNING_RIBBON:
|
|
retVal = substruct3->winningRibbon;
|
|
break;
|
|
case MON_DATA_VICTORY_RIBBON:
|
|
retVal = substruct3->victoryRibbon;
|
|
break;
|
|
case MON_DATA_ARTIST_RIBBON:
|
|
retVal = substruct3->artistRibbon;
|
|
break;
|
|
case MON_DATA_EFFORT_RIBBON:
|
|
retVal = substruct3->effortRibbon;
|
|
break;
|
|
case MON_DATA_MARINE_RIBBON:
|
|
retVal = substruct3->marineRibbon;
|
|
break;
|
|
case MON_DATA_LAND_RIBBON:
|
|
retVal = substruct3->landRibbon;
|
|
break;
|
|
case MON_DATA_SKY_RIBBON:
|
|
retVal = substruct3->skyRibbon;
|
|
break;
|
|
case MON_DATA_COUNTRY_RIBBON:
|
|
retVal = substruct3->countryRibbon;
|
|
break;
|
|
case MON_DATA_NATIONAL_RIBBON:
|
|
retVal = substruct3->nationalRibbon;
|
|
break;
|
|
case MON_DATA_EARTH_RIBBON:
|
|
retVal = substruct3->earthRibbon;
|
|
break;
|
|
case MON_DATA_WORLD_RIBBON:
|
|
retVal = substruct3->worldRibbon;
|
|
break;
|
|
case MON_DATA_MODERN_FATEFUL_ENCOUNTER:
|
|
retVal = substruct3->modernFatefulEncounter;
|
|
break;
|
|
case MON_DATA_SPECIES_OR_EGG:
|
|
retVal = substruct0->species;
|
|
if (substruct0->species && (substruct3->isEgg || boxMon->isBadEgg))
|
|
retVal = SPECIES_EGG;
|
|
break;
|
|
case MON_DATA_IVS:
|
|
retVal = substruct3->hpIV
|
|
| (substruct3->attackIV << 5)
|
|
| (substruct3->defenseIV << 10)
|
|
| (substruct3->speedIV << 15)
|
|
| (substruct3->spAttackIV << 20)
|
|
| (substruct3->spDefenseIV << 25);
|
|
break;
|
|
case MON_DATA_KNOWN_MOVES:
|
|
if (substruct0->species && !substruct3->isEgg)
|
|
{
|
|
u16 *moves = (u16 *)data;
|
|
s32 i = 0;
|
|
|
|
while (moves[i] != MOVES_COUNT)
|
|
{
|
|
u16 move = moves[i];
|
|
if (substruct1->move1 == move
|
|
|| substruct1->move2 == move
|
|
|| substruct1->move3 == move
|
|
|| substruct1->move4 == move)
|
|
retVal |= gBitTable[i];
|
|
i++;
|
|
}
|
|
}
|
|
break;
|
|
case MON_DATA_RIBBON_COUNT:
|
|
retVal = 0;
|
|
if (substruct0->species && !substruct3->isEgg)
|
|
{
|
|
retVal += substruct3->coolRibbon;
|
|
retVal += substruct3->beautyRibbon;
|
|
retVal += substruct3->cuteRibbon;
|
|
retVal += substruct3->smartRibbon;
|
|
retVal += substruct3->toughRibbon;
|
|
retVal += substruct3->championRibbon;
|
|
retVal += substruct3->winningRibbon;
|
|
retVal += substruct3->victoryRibbon;
|
|
retVal += substruct3->artistRibbon;
|
|
retVal += substruct3->effortRibbon;
|
|
retVal += substruct3->marineRibbon;
|
|
retVal += substruct3->landRibbon;
|
|
retVal += substruct3->skyRibbon;
|
|
retVal += substruct3->countryRibbon;
|
|
retVal += substruct3->nationalRibbon;
|
|
retVal += substruct3->earthRibbon;
|
|
retVal += substruct3->worldRibbon;
|
|
}
|
|
break;
|
|
case MON_DATA_RIBBONS:
|
|
retVal = 0;
|
|
if (substruct0->species && !substruct3->isEgg)
|
|
{
|
|
retVal = substruct3->championRibbon
|
|
| (substruct3->coolRibbon << 1)
|
|
| (substruct3->beautyRibbon << 4)
|
|
| (substruct3->cuteRibbon << 7)
|
|
| (substruct3->smartRibbon << 10)
|
|
| (substruct3->toughRibbon << 13)
|
|
| (substruct3->winningRibbon << 16)
|
|
| (substruct3->victoryRibbon << 17)
|
|
| (substruct3->artistRibbon << 18)
|
|
| (substruct3->effortRibbon << 19)
|
|
| (substruct3->marineRibbon << 20)
|
|
| (substruct3->landRibbon << 21)
|
|
| (substruct3->skyRibbon << 22)
|
|
| (substruct3->countryRibbon << 23)
|
|
| (substruct3->nationalRibbon << 24)
|
|
| (substruct3->earthRibbon << 25)
|
|
| (substruct3->worldRibbon << 26);
|
|
}
|
|
break;
|
|
case MON_DATA_HYPER_TRAINED_HP:
|
|
retVal = substruct1->hyperTrainedHP;
|
|
break;
|
|
case MON_DATA_HYPER_TRAINED_ATK:
|
|
retVal = substruct1->hyperTrainedAttack;
|
|
break;
|
|
case MON_DATA_HYPER_TRAINED_DEF:
|
|
retVal = substruct1->hyperTrainedDefense;
|
|
break;
|
|
case MON_DATA_HYPER_TRAINED_SPEED:
|
|
retVal = substruct1->hyperTrainedSpeed;
|
|
break;
|
|
case MON_DATA_HYPER_TRAINED_SPATK:
|
|
retVal = substruct1->hyperTrainedSpAttack;
|
|
break;
|
|
case MON_DATA_HYPER_TRAINED_SPDEF:
|
|
retVal = substruct1->hyperTrainedSpDefense;
|
|
break;
|
|
case MON_DATA_IS_SHADOW:
|
|
retVal = substruct3->isShadow;
|
|
break;
|
|
case MON_DATA_DYNAMAX_LEVEL:
|
|
retVal = substruct3->dynamaxLevel;
|
|
break;
|
|
case MON_DATA_GIGANTAMAX_FACTOR:
|
|
retVal = substruct3->gigantamaxFactor;
|
|
break;
|
|
case MON_DATA_TERA_TYPE:
|
|
{
|
|
if (substruct0->teraType == 0)
|
|
{
|
|
const u8 *types = gSpeciesInfo[substruct0->species].types;
|
|
retVal = (boxMon->personality & 0x1) == 0 ? types[0] : types[1];
|
|
}
|
|
else
|
|
{
|
|
retVal = substruct0->teraType - 1;
|
|
}
|
|
break;
|
|
}
|
|
case MON_DATA_EVOLUTION_TRACKER:
|
|
evoTracker.asField.a = substruct1->evolutionTracker1;
|
|
evoTracker.asField.b = substruct1->evolutionTracker2;
|
|
retVal = evoTracker.value;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (field)
|
|
{
|
|
case MON_DATA_STATUS:
|
|
retVal = UncompressStatus(boxMon->compressedStatus);
|
|
break;
|
|
case MON_DATA_HP_LOST:
|
|
retVal = boxMon->hpLost;
|
|
break;
|
|
case MON_DATA_PERSONALITY:
|
|
retVal = boxMon->personality;
|
|
break;
|
|
case MON_DATA_OT_ID:
|
|
retVal = boxMon->otId;
|
|
break;
|
|
case MON_DATA_LANGUAGE:
|
|
retVal = boxMon->language;
|
|
break;
|
|
case MON_DATA_SANITY_IS_BAD_EGG:
|
|
retVal = boxMon->isBadEgg;
|
|
break;
|
|
case MON_DATA_SANITY_HAS_SPECIES:
|
|
retVal = boxMon->hasSpecies;
|
|
break;
|
|
case MON_DATA_SANITY_IS_EGG:
|
|
retVal = boxMon->isEgg;
|
|
break;
|
|
case MON_DATA_OT_NAME:
|
|
{
|
|
// if (DECAP_ENABLED && !DECAP_NICKNAMES && IsStringAddrSafe(data, PLAYER_NAME_LENGTH))
|
|
// *data++ = CHAR_FIXED_CASE;
|
|
retVal = 0;
|
|
|
|
while (retVal < PLAYER_NAME_LENGTH)
|
|
{
|
|
data[retVal] = boxMon->otName[retVal];
|
|
retVal++;
|
|
}
|
|
|
|
data[retVal] = EOS;
|
|
break;
|
|
}
|
|
case MON_DATA_MARKINGS:
|
|
retVal = boxMon->markings;
|
|
break;
|
|
case MON_DATA_CHECKSUM:
|
|
retVal = boxMon->checksum;
|
|
break;
|
|
case MON_DATA_IS_SHINY:
|
|
{
|
|
u32 shinyValue = GET_SHINY_VALUE(boxMon->otId, boxMon->personality);
|
|
retVal = (shinyValue < SHINY_ODDS) ^ boxMon->shinyModifier;
|
|
break;
|
|
}
|
|
case MON_DATA_HIDDEN_NATURE:
|
|
{
|
|
u32 nature = GetNatureFromPersonality(boxMon->personality);
|
|
retVal = nature ^ boxMon->hiddenNatureModifier;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (field > MON_DATA_ENCRYPT_SEPARATOR)
|
|
EncryptBoxMon(boxMon);
|
|
|
|
return retVal;
|
|
}
|
|
|
|
u32 GetBoxMonData2(struct BoxPokemon *boxMon, s32 field)
|
|
{
|
|
return GetBoxMonData3(boxMon, field, NULL);
|
|
}
|
|
|
|
#define SET8(lhs) (lhs) = *data
|
|
#define SET16(lhs) (lhs) = data[0] + (data[1] << 8)
|
|
#define SET32(lhs) (lhs) = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24)
|
|
|
|
|
|
void SetMonData(struct Pokemon *mon, s32 field, const void *dataArg)
|
|
{
|
|
const u8 *data = dataArg;
|
|
|
|
switch (field)
|
|
{
|
|
case MON_DATA_STATUS:
|
|
SET32(mon->status);
|
|
SetBoxMonData(&mon->box, MON_DATA_STATUS, dataArg);
|
|
break;
|
|
case MON_DATA_LEVEL:
|
|
SET8(mon->level);
|
|
break;
|
|
case MON_DATA_HP:
|
|
{
|
|
u32 hpLost;
|
|
SET16(mon->hp);
|
|
hpLost = mon->maxHP - mon->hp;
|
|
SetBoxMonData(&mon->box, MON_DATA_HP_LOST, &hpLost);
|
|
break;
|
|
}
|
|
case MON_DATA_HP_LOST:
|
|
{
|
|
u32 hpLost;
|
|
SET16(hpLost);
|
|
mon->hp = mon->maxHP - hpLost;
|
|
SetBoxMonData(&mon->box, MON_DATA_HP_LOST, &hpLost);
|
|
break;
|
|
}
|
|
case MON_DATA_MAX_HP:
|
|
SET16(mon->maxHP);
|
|
break;
|
|
case MON_DATA_ATK:
|
|
SET16(mon->attack);
|
|
break;
|
|
case MON_DATA_DEF:
|
|
SET16(mon->defense);
|
|
break;
|
|
case MON_DATA_SPEED:
|
|
SET16(mon->speed);
|
|
break;
|
|
case MON_DATA_SPATK:
|
|
SET16(mon->spAttack);
|
|
break;
|
|
case MON_DATA_SPDEF:
|
|
SET16(mon->spDefense);
|
|
break;
|
|
case MON_DATA_MAIL:
|
|
SET8(mon->mail);
|
|
break;
|
|
case MON_DATA_SPECIES_OR_EGG:
|
|
break;
|
|
default:
|
|
SetBoxMonData(&mon->box, field, data);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg)
|
|
{
|
|
const u8 *data = dataArg;
|
|
|
|
struct PokemonSubstruct0 *substruct0 = NULL;
|
|
struct PokemonSubstruct1 *substruct1 = NULL;
|
|
struct PokemonSubstruct2 *substruct2 = NULL;
|
|
struct PokemonSubstruct3 *substruct3 = NULL;
|
|
|
|
if (field > MON_DATA_ENCRYPT_SEPARATOR)
|
|
{
|
|
substruct0 = &(GetSubstruct(boxMon, boxMon->personality, 0)->type0);
|
|
substruct1 = &(GetSubstruct(boxMon, boxMon->personality, 1)->type1);
|
|
substruct2 = &(GetSubstruct(boxMon, boxMon->personality, 2)->type2);
|
|
substruct3 = &(GetSubstruct(boxMon, boxMon->personality, 3)->type3);
|
|
|
|
DecryptBoxMon(boxMon);
|
|
|
|
if (CalculateBoxMonChecksum(boxMon) != boxMon->checksum)
|
|
{
|
|
boxMon->isBadEgg = TRUE;
|
|
boxMon->isEgg = TRUE;
|
|
substruct3->isEgg = TRUE;
|
|
EncryptBoxMon(boxMon);
|
|
return;
|
|
}
|
|
|
|
switch (field)
|
|
{
|
|
case MON_DATA_NICKNAME:
|
|
{
|
|
s32 i;
|
|
for (i = 0; i < min(sizeof(boxMon->nickname), POKEMON_NAME_LENGTH); i++)
|
|
boxMon->nickname[i] = data[i];
|
|
if (POKEMON_NAME_LENGTH >= 11)
|
|
substruct0->nickname11 = data[10];
|
|
if (POKEMON_NAME_LENGTH >= 12)
|
|
substruct0->nickname12 = data[11];
|
|
break;
|
|
}
|
|
case MON_DATA_SPECIES:
|
|
{
|
|
SET16(substruct0->species);
|
|
if (substruct0->species)
|
|
boxMon->hasSpecies = TRUE;
|
|
else
|
|
boxMon->hasSpecies = FALSE;
|
|
break;
|
|
}
|
|
case MON_DATA_HELD_ITEM:
|
|
SET16(substruct0->heldItem);
|
|
break;
|
|
case MON_DATA_EXP:
|
|
SET32(substruct0->experience);
|
|
break;
|
|
case MON_DATA_PP_BONUSES:
|
|
SET8(substruct0->ppBonuses);
|
|
break;
|
|
case MON_DATA_FRIENDSHIP:
|
|
SET8(substruct0->friendship);
|
|
break;
|
|
case MON_DATA_MOVE1:
|
|
SET16(substruct1->move1);
|
|
break;
|
|
case MON_DATA_MOVE2:
|
|
SET16(substruct1->move2);
|
|
break;
|
|
case MON_DATA_MOVE3:
|
|
SET16(substruct1->move3);
|
|
break;
|
|
case MON_DATA_MOVE4:
|
|
SET16(substruct1->move4);
|
|
break;
|
|
case MON_DATA_PP1:
|
|
SET8(substruct1->pp1);
|
|
break;
|
|
case MON_DATA_PP2:
|
|
SET8(substruct1->pp2);
|
|
break;
|
|
case MON_DATA_PP3:
|
|
SET8(substruct1->pp3);
|
|
break;
|
|
case MON_DATA_PP4:
|
|
SET8(substruct1->pp4);
|
|
break;
|
|
case MON_DATA_HP_EV:
|
|
SET8(substruct2->hpEV);
|
|
break;
|
|
case MON_DATA_ATK_EV:
|
|
SET8(substruct2->attackEV);
|
|
break;
|
|
case MON_DATA_DEF_EV:
|
|
SET8(substruct2->defenseEV);
|
|
break;
|
|
case MON_DATA_SPEED_EV:
|
|
SET8(substruct2->speedEV);
|
|
break;
|
|
case MON_DATA_SPATK_EV:
|
|
SET8(substruct2->spAttackEV);
|
|
break;
|
|
case MON_DATA_SPDEF_EV:
|
|
SET8(substruct2->spDefenseEV);
|
|
break;
|
|
case MON_DATA_COOL:
|
|
SET8(substruct2->cool);
|
|
break;
|
|
case MON_DATA_BEAUTY:
|
|
SET8(substruct2->beauty);
|
|
break;
|
|
case MON_DATA_CUTE:
|
|
SET8(substruct2->cute);
|
|
break;
|
|
case MON_DATA_SMART:
|
|
SET8(substruct2->smart);
|
|
break;
|
|
case MON_DATA_TOUGH:
|
|
SET8(substruct2->tough);
|
|
break;
|
|
case MON_DATA_SHEEN:
|
|
SET8(substruct2->sheen);
|
|
break;
|
|
case MON_DATA_POKERUS:
|
|
SET8(substruct3->pokerus);
|
|
break;
|
|
case MON_DATA_MET_LOCATION:
|
|
SET8(substruct3->metLocation);
|
|
break;
|
|
case MON_DATA_MET_LEVEL:
|
|
{
|
|
u8 metLevel = *data;
|
|
substruct3->metLevel = metLevel;
|
|
break;
|
|
}
|
|
case MON_DATA_MET_GAME:
|
|
SET8(substruct3->metGame);
|
|
break;
|
|
case MON_DATA_POKEBALL:
|
|
{
|
|
u8 pokeball = *data;
|
|
substruct0->pokeball = pokeball;
|
|
break;
|
|
}
|
|
case MON_DATA_OT_GENDER:
|
|
SET8(substruct3->otGender);
|
|
break;
|
|
case MON_DATA_HP_IV:
|
|
SET8(substruct3->hpIV);
|
|
break;
|
|
case MON_DATA_ATK_IV:
|
|
SET8(substruct3->attackIV);
|
|
break;
|
|
case MON_DATA_DEF_IV:
|
|
SET8(substruct3->defenseIV);
|
|
break;
|
|
case MON_DATA_SPEED_IV:
|
|
SET8(substruct3->speedIV);
|
|
break;
|
|
case MON_DATA_SPATK_IV:
|
|
SET8(substruct3->spAttackIV);
|
|
break;
|
|
case MON_DATA_SPDEF_IV:
|
|
SET8(substruct3->spDefenseIV);
|
|
break;
|
|
case MON_DATA_IS_EGG:
|
|
SET8(substruct3->isEgg);
|
|
if (substruct3->isEgg)
|
|
boxMon->isEgg = TRUE;
|
|
else
|
|
boxMon->isEgg = FALSE;
|
|
break;
|
|
case MON_DATA_ABILITY_NUM:
|
|
SET8(substruct3->abilityNum);
|
|
break;
|
|
case MON_DATA_COOL_RIBBON:
|
|
SET8(substruct3->coolRibbon);
|
|
break;
|
|
case MON_DATA_BEAUTY_RIBBON:
|
|
SET8(substruct3->beautyRibbon);
|
|
break;
|
|
case MON_DATA_CUTE_RIBBON:
|
|
SET8(substruct3->cuteRibbon);
|
|
break;
|
|
case MON_DATA_SMART_RIBBON:
|
|
SET8(substruct3->smartRibbon);
|
|
break;
|
|
case MON_DATA_TOUGH_RIBBON:
|
|
SET8(substruct3->toughRibbon);
|
|
break;
|
|
case MON_DATA_CHAMPION_RIBBON:
|
|
SET8(substruct3->championRibbon);
|
|
break;
|
|
case MON_DATA_WINNING_RIBBON:
|
|
SET8(substruct3->winningRibbon);
|
|
break;
|
|
case MON_DATA_VICTORY_RIBBON:
|
|
SET8(substruct3->victoryRibbon);
|
|
break;
|
|
case MON_DATA_ARTIST_RIBBON:
|
|
SET8(substruct3->artistRibbon);
|
|
break;
|
|
case MON_DATA_EFFORT_RIBBON:
|
|
SET8(substruct3->effortRibbon);
|
|
break;
|
|
case MON_DATA_MARINE_RIBBON:
|
|
SET8(substruct3->marineRibbon);
|
|
break;
|
|
case MON_DATA_LAND_RIBBON:
|
|
SET8(substruct3->landRibbon);
|
|
break;
|
|
case MON_DATA_SKY_RIBBON:
|
|
SET8(substruct3->skyRibbon);
|
|
break;
|
|
case MON_DATA_COUNTRY_RIBBON:
|
|
SET8(substruct3->countryRibbon);
|
|
break;
|
|
case MON_DATA_NATIONAL_RIBBON:
|
|
SET8(substruct3->nationalRibbon);
|
|
break;
|
|
case MON_DATA_EARTH_RIBBON:
|
|
SET8(substruct3->earthRibbon);
|
|
break;
|
|
case MON_DATA_WORLD_RIBBON:
|
|
SET8(substruct3->worldRibbon);
|
|
break;
|
|
case MON_DATA_MODERN_FATEFUL_ENCOUNTER:
|
|
SET8(substruct3->modernFatefulEncounter);
|
|
break;
|
|
case MON_DATA_IVS:
|
|
{
|
|
u32 ivs = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
|
|
substruct3->hpIV = ivs & MAX_IV_MASK;
|
|
substruct3->attackIV = (ivs >> 5) & MAX_IV_MASK;
|
|
substruct3->defenseIV = (ivs >> 10) & MAX_IV_MASK;
|
|
substruct3->speedIV = (ivs >> 15) & MAX_IV_MASK;
|
|
substruct3->spAttackIV = (ivs >> 20) & MAX_IV_MASK;
|
|
substruct3->spDefenseIV = (ivs >> 25) & MAX_IV_MASK;
|
|
break;
|
|
}
|
|
case MON_DATA_HYPER_TRAINED_HP:
|
|
SET8(substruct1->hyperTrainedHP);
|
|
break;
|
|
case MON_DATA_HYPER_TRAINED_ATK:
|
|
SET8(substruct1->hyperTrainedAttack);
|
|
break;
|
|
case MON_DATA_HYPER_TRAINED_DEF:
|
|
SET8(substruct1->hyperTrainedDefense);
|
|
break;
|
|
case MON_DATA_HYPER_TRAINED_SPEED:
|
|
SET8(substruct1->hyperTrainedSpeed);
|
|
break;
|
|
case MON_DATA_HYPER_TRAINED_SPATK:
|
|
SET8(substruct1->hyperTrainedSpAttack);
|
|
break;
|
|
case MON_DATA_HYPER_TRAINED_SPDEF:
|
|
SET8(substruct1->hyperTrainedSpDefense);
|
|
break;
|
|
case MON_DATA_IS_SHADOW:
|
|
SET8(substruct3->isShadow);
|
|
break;
|
|
case MON_DATA_DYNAMAX_LEVEL:
|
|
SET8(substruct3->dynamaxLevel);
|
|
break;
|
|
case MON_DATA_GIGANTAMAX_FACTOR:
|
|
SET8(substruct3->gigantamaxFactor);
|
|
break;
|
|
case MON_DATA_TERA_TYPE:
|
|
{
|
|
u32 teraType;
|
|
SET8(teraType);
|
|
substruct0->teraType = 1 + teraType;
|
|
break;
|
|
}
|
|
case MON_DATA_EVOLUTION_TRACKER:
|
|
{
|
|
union EvolutionTracker evoTracker;
|
|
u32 evoTrackerValue;
|
|
SET32(evoTrackerValue);
|
|
evoTracker.value = evoTrackerValue;
|
|
substruct1->evolutionTracker1 = evoTracker.asField.a;
|
|
substruct1->evolutionTracker2 = evoTracker.asField.b;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (field)
|
|
{
|
|
case MON_DATA_STATUS:
|
|
{
|
|
u32 status;
|
|
SET32(status);
|
|
boxMon->compressedStatus = CompressStatus(status);
|
|
break;
|
|
}
|
|
case MON_DATA_HP_LOST:
|
|
SET16(boxMon->hpLost);
|
|
break;
|
|
case MON_DATA_PERSONALITY:
|
|
SET32(boxMon->personality);
|
|
break;
|
|
case MON_DATA_OT_ID:
|
|
SET32(boxMon->otId);
|
|
break;
|
|
case MON_DATA_LANGUAGE:
|
|
SET8(boxMon->language);
|
|
break;
|
|
case MON_DATA_SANITY_IS_BAD_EGG:
|
|
SET8(boxMon->isBadEgg);
|
|
break;
|
|
case MON_DATA_SANITY_HAS_SPECIES:
|
|
SET8(boxMon->hasSpecies);
|
|
break;
|
|
case MON_DATA_SANITY_IS_EGG:
|
|
SET8(boxMon->isEgg);
|
|
break;
|
|
case MON_DATA_OT_NAME:
|
|
{
|
|
s32 i;
|
|
for (i = 0; i < PLAYER_NAME_LENGTH; i++)
|
|
boxMon->otName[i] = data[i];
|
|
break;
|
|
}
|
|
case MON_DATA_MARKINGS:
|
|
SET8(boxMon->markings);
|
|
break;
|
|
case MON_DATA_CHECKSUM:
|
|
SET16(boxMon->checksum);
|
|
break;
|
|
case MON_DATA_IS_SHINY:
|
|
{
|
|
u32 shinyValue = GET_SHINY_VALUE(boxMon->otId, boxMon->personality);
|
|
bool32 isShiny;
|
|
SET8(isShiny);
|
|
boxMon->shinyModifier = (shinyValue < SHINY_ODDS) ^ isShiny;
|
|
break;
|
|
}
|
|
case MON_DATA_HIDDEN_NATURE:
|
|
{
|
|
u32 nature = GetNatureFromPersonality(boxMon->personality);
|
|
u32 hiddenNature;
|
|
SET8(hiddenNature);
|
|
boxMon->hiddenNatureModifier = nature ^ hiddenNature;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (field > MON_DATA_ENCRYPT_SEPARATOR)
|
|
{
|
|
boxMon->checksum = CalculateBoxMonChecksum(boxMon);
|
|
EncryptBoxMon(boxMon);
|
|
}
|
|
}
|
|
|
|
|
|
void CopyMon(void *dest, void *src, size_t size)
|
|
{
|
|
memcpy(dest, src, size);
|
|
}
|
|
|
|
u8 GiveMonToPlayer(struct Pokemon *mon)
|
|
{
|
|
s32 i;
|
|
|
|
SetMonData(mon, MON_DATA_OT_NAME, gSaveBlock2Ptr->playerName);
|
|
SetMonData(mon, MON_DATA_OT_GENDER, &gSaveBlock2Ptr->playerGender);
|
|
SetMonData(mon, MON_DATA_OT_ID, gSaveBlock2Ptr->playerTrainerId);
|
|
|
|
for (i = 0; i < PARTY_SIZE; i++)
|
|
{
|
|
if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL) == SPECIES_NONE)
|
|
break;
|
|
}
|
|
|
|
if (i >= PARTY_SIZE)
|
|
return SendMonToPC(mon);
|
|
|
|
CopyMon(&gPlayerParty[i], mon, sizeof(*mon));
|
|
gPlayerPartyCount = i + 1;
|
|
return MON_GIVEN_TO_PARTY;
|
|
}
|
|
|
|
static u8 SendMonToPC(struct Pokemon* mon)
|
|
{
|
|
s32 boxNo, boxPos;
|
|
|
|
SetPCBoxToSendMon(VarGet(VAR_PC_BOX_TO_SEND_MON));
|
|
|
|
boxNo = StorageGetCurrentBox();
|
|
|
|
do
|
|
{
|
|
for (boxPos = 0; boxPos < IN_BOX_COUNT; boxPos++)
|
|
{
|
|
struct BoxPokemon* checkingMon = GetBoxedMonPtr(boxNo, boxPos);
|
|
if (GetBoxMonData(checkingMon, MON_DATA_SPECIES, NULL) == SPECIES_NONE)
|
|
{
|
|
MonRestorePP(mon);
|
|
CopyMon(checkingMon, &mon->box, sizeof(mon->box));
|
|
gSpecialVar_MonBoxId = boxNo;
|
|
gSpecialVar_MonBoxPos = boxPos;
|
|
if (GetPCBoxToSendMon() != boxNo)
|
|
FlagClear(FLAG_SHOWN_BOX_WAS_FULL_MESSAGE);
|
|
VarSet(VAR_PC_BOX_TO_SEND_MON, boxNo);
|
|
return MON_GIVEN_TO_PC;
|
|
}
|
|
}
|
|
|
|
boxNo++;
|
|
if (boxNo == TOTAL_BOXES_COUNT)
|
|
boxNo = 0;
|
|
} while (boxNo != StorageGetCurrentBox());
|
|
|
|
return MON_CANT_GIVE;
|
|
}
|
|
|
|
u8 CalculatePlayerPartyCount(void)
|
|
{
|
|
gPlayerPartyCount = 0;
|
|
|
|
while (gPlayerPartyCount < PARTY_SIZE
|
|
&& GetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_SPECIES, NULL) != SPECIES_NONE)
|
|
{
|
|
gPlayerPartyCount++;
|
|
}
|
|
|
|
return gPlayerPartyCount;
|
|
}
|
|
|
|
|
|
u8 CalculateEnemyPartyCount(void)
|
|
{
|
|
gEnemyPartyCount = 0;
|
|
|
|
while (gEnemyPartyCount < PARTY_SIZE
|
|
&& GetMonData(&gEnemyParty[gEnemyPartyCount], MON_DATA_SPECIES, NULL) != SPECIES_NONE)
|
|
{
|
|
gEnemyPartyCount++;
|
|
}
|
|
|
|
return gEnemyPartyCount;
|
|
}
|
|
|
|
u8 GetMonsStateToDoubles(void)
|
|
{
|
|
s32 aliveCount = 0;
|
|
s32 i;
|
|
CalculatePlayerPartyCount();
|
|
|
|
if (gPlayerPartyCount == 1)
|
|
return gPlayerPartyCount; // PLAYER_HAS_ONE_MON
|
|
|
|
for (i = 0; i < gPlayerPartyCount; i++)
|
|
{
|
|
// FRLG changed the order of these checks, but there's no point to doing that
|
|
// because of the requirement of all 3 of these checks.
|
|
if (GetMonData(&gPlayerParty[i], MON_DATA_HP, NULL) != 0
|
|
&& GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG, NULL) != SPECIES_NONE
|
|
&& GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG, NULL) != SPECIES_EGG)
|
|
aliveCount++;
|
|
}
|
|
|
|
return (aliveCount > 1) ? PLAYER_HAS_TWO_USABLE_MONS : PLAYER_HAS_ONE_USABLE_MON;
|
|
}
|
|
|
|
u16 GetAbilityBySpecies(u16 species, bool8 abilityNum)
|
|
{
|
|
int i;
|
|
|
|
if (abilityNum < NUM_ABILITY_SLOTS)
|
|
gLastUsedAbility = gSpeciesInfo[species].abilities[abilityNum];
|
|
else
|
|
gLastUsedAbility = ABILITY_NONE;
|
|
|
|
if (abilityNum >= NUM_NORMAL_ABILITY_SLOTS) // if abilityNum is empty hidden ability, look for other hidden abilities
|
|
{
|
|
for (i = NUM_NORMAL_ABILITY_SLOTS; i < NUM_ABILITY_SLOTS && gLastUsedAbility == ABILITY_NONE; i++)
|
|
{
|
|
gLastUsedAbility = gSpeciesInfo[species].abilities[i];
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < NUM_ABILITY_SLOTS && gLastUsedAbility == ABILITY_NONE; i++) // look for any non-empty ability
|
|
{
|
|
gLastUsedAbility = gSpeciesInfo[species].abilities[i];
|
|
}
|
|
|
|
return gLastUsedAbility;
|
|
}
|
|
|
|
u16 GetMonAbility(struct Pokemon *mon)
|
|
{
|
|
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
u8 abilityNum = GetMonData(mon, MON_DATA_ABILITY_NUM, NULL);
|
|
return GetAbilityBySpecies(species, abilityNum);
|
|
}
|
|
|
|
static void CreateSecretBaseEnemyParty(struct SecretBaseRecord *secretBaseRecord)
|
|
{
|
|
s32 i, j;
|
|
|
|
ZeroEnemyPartyMons();
|
|
*gBattleResources->secretBase = *secretBaseRecord;
|
|
|
|
for (i = 0; i < PARTY_SIZE; i++)
|
|
{
|
|
if (gBattleResources->secretBase->party.species[i])
|
|
{
|
|
CreateMon(&gEnemyParty[i],
|
|
gBattleResources->secretBase->party.species[i],
|
|
gBattleResources->secretBase->party.levels[i],
|
|
15,
|
|
TRUE,
|
|
gBattleResources->secretBase->party.personality[i],
|
|
OT_ID_RANDOM_NO_SHINY,
|
|
0);
|
|
|
|
SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gBattleResources->secretBase->party.heldItems[i]);
|
|
|
|
for (j = 0; j < NUM_STATS; j++)
|
|
SetMonData(&gEnemyParty[i], MON_DATA_HP_EV + j, &gBattleResources->secretBase->party.EVs[i]);
|
|
|
|
for (j = 0; j < MAX_MON_MOVES; j++)
|
|
{
|
|
SetMonData(&gEnemyParty[i], MON_DATA_MOVE1 + j, &gBattleResources->secretBase->party.moves[i * MAX_MON_MOVES + j]);
|
|
SetMonData(&gEnemyParty[i], MON_DATA_PP1 + j, &gMovesInfo[gBattleResources->secretBase->party.moves[i * MAX_MON_MOVES + j]].pp);
|
|
}
|
|
}
|
|
}
|
|
gBattleTypeFlags = BATTLE_TYPE_TRAINER;
|
|
gTrainerBattleOpponent_A = TRAINER_SECRET_BASE;
|
|
}
|
|
|
|
u8 GetSecretBaseTrainerPicIndex(void)
|
|
{
|
|
u8 facilityClass = sSecretBaseFacilityClasses[gBattleResources->secretBase->gender][gBattleResources->secretBase->trainerId[0] % 5];
|
|
return gFacilityClassToPicIndex[facilityClass];
|
|
}
|
|
|
|
u8 GetSecretBaseTrainerNameIndex(void)
|
|
{
|
|
u8 facilityClass = sSecretBaseFacilityClasses[gBattleResources->secretBase->gender][gBattleResources->secretBase->trainerId[0] % 5];
|
|
return gFacilityClassToTrainerClass[facilityClass];
|
|
}
|
|
|
|
bool8 IsPlayerPartyAndPokemonStorageFull(void)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < PARTY_SIZE; i++)
|
|
if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL) == SPECIES_NONE)
|
|
return FALSE;
|
|
|
|
return IsPokemonStorageFull();
|
|
}
|
|
|
|
static bool8 IsPokemonStorageFull(void)
|
|
{
|
|
s32 i, j;
|
|
|
|
for (i = 0; i < TOTAL_BOXES_COUNT; i++)
|
|
for (j = 0; j < IN_BOX_COUNT; j++)
|
|
if (GetBoxMonDataAt(i, j, MON_DATA_SPECIES) == SPECIES_NONE)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
const u8 *GetSpeciesName(u16 species)
|
|
{
|
|
species = SanitizeSpeciesId(species);
|
|
if (gSpeciesInfo[species].speciesName[0] == 0)
|
|
return gSpeciesInfo[SPECIES_NONE].speciesName;
|
|
return gSpeciesInfo[species].speciesName;
|
|
}
|
|
|
|
const u8 *GetSpeciesCategory(u16 species)
|
|
{
|
|
species = SanitizeSpeciesId(species);
|
|
if (gSpeciesInfo[species].categoryName[0] == 0)
|
|
return gSpeciesInfo[SPECIES_NONE].categoryName;
|
|
return gSpeciesInfo[species].categoryName;
|
|
}
|
|
|
|
const u8 *GetSpeciesPokedexDescription(u16 species)
|
|
{
|
|
species = SanitizeSpeciesId(species);
|
|
if (gSpeciesInfo[species].description == NULL)
|
|
return gSpeciesInfo[SPECIES_NONE].description;
|
|
return gSpeciesInfo[species].description;
|
|
}
|
|
|
|
u16 GetSpeciesHeight(u16 species)
|
|
{
|
|
return gSpeciesInfo[SanitizeSpeciesId(species)].height;
|
|
}
|
|
|
|
u16 GetSpeciesWeight(u16 species)
|
|
{
|
|
return gSpeciesInfo[SanitizeSpeciesId(species)].weight;
|
|
}
|
|
|
|
const struct LevelUpMove *GetSpeciesLevelUpLearnset(u16 species)
|
|
{
|
|
const struct LevelUpMove *learnset = gSpeciesInfo[SanitizeSpeciesId(species)].levelUpLearnset;
|
|
if (learnset == NULL)
|
|
return gSpeciesInfo[SPECIES_NONE].levelUpLearnset;
|
|
return learnset;
|
|
}
|
|
|
|
const u16 *GetSpeciesTeachableLearnset(u16 species)
|
|
{
|
|
const u16 *learnset = gSpeciesInfo[SanitizeSpeciesId(species)].teachableLearnset;
|
|
if (learnset == NULL)
|
|
return gSpeciesInfo[SPECIES_NONE].teachableLearnset;
|
|
return learnset;
|
|
}
|
|
|
|
const struct Evolution *GetSpeciesEvolutions(u16 species)
|
|
{
|
|
const struct Evolution *evolutions = gSpeciesInfo[SanitizeSpeciesId(species)].evolutions;
|
|
if (evolutions == NULL)
|
|
return gSpeciesInfo[SPECIES_NONE].evolutions;
|
|
return evolutions;
|
|
}
|
|
|
|
const u16 *GetSpeciesFormTable(u16 species)
|
|
{
|
|
const u16 *formTable = gSpeciesInfo[SanitizeSpeciesId(species)].formSpeciesIdTable;
|
|
if (formTable == NULL)
|
|
return gSpeciesInfo[SPECIES_NONE].formSpeciesIdTable;
|
|
return formTable;
|
|
}
|
|
|
|
const struct FormChange *GetSpeciesFormChanges(u16 species)
|
|
{
|
|
const struct FormChange *evolutions = gSpeciesInfo[SanitizeSpeciesId(species)].formChangeTable;
|
|
if (evolutions == NULL)
|
|
return gSpeciesInfo[SPECIES_NONE].formChangeTable;
|
|
return evolutions;
|
|
}
|
|
|
|
u8 CalculatePPWithBonus(u16 move, u8 ppBonuses, u8 moveIndex)
|
|
{
|
|
u8 basePP = gMovesInfo[move].pp;
|
|
return basePP + ((basePP * 20 * ((gPPUpGetMask[moveIndex] & ppBonuses) >> (2 * moveIndex))) / 100);
|
|
}
|
|
|
|
void RemoveMonPPBonus(struct Pokemon *mon, u8 moveIndex)
|
|
{
|
|
u8 ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES, NULL);
|
|
ppBonuses &= gPPUpClearMask[moveIndex];
|
|
SetMonData(mon, MON_DATA_PP_BONUSES, &ppBonuses);
|
|
}
|
|
|
|
void RemoveBattleMonPPBonus(struct BattlePokemon *mon, u8 moveIndex)
|
|
{
|
|
mon->ppBonuses &= gPPUpClearMask[moveIndex];
|
|
}
|
|
|
|
static void CopyPlayerPartyMonToBattleData(u8 battlerId, u8 partyIndex)
|
|
{
|
|
u16 *hpSwitchout;
|
|
s32 i;
|
|
u8 nickname[POKEMON_NAME_LENGTH * 2]; // Why is the nickname array here longer in FR/LG?
|
|
|
|
gBattleMons[battlerId].species = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPECIES, NULL);
|
|
gBattleMons[battlerId].item = GetMonData(&gPlayerParty[partyIndex], MON_DATA_HELD_ITEM, NULL);
|
|
|
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
{
|
|
gBattleMons[battlerId].moves[i] = GetMonData(&gPlayerParty[partyIndex], MON_DATA_MOVE1 + i, NULL);
|
|
gBattleMons[battlerId].pp[i] = GetMonData(&gPlayerParty[partyIndex], MON_DATA_PP1 + i, NULL);
|
|
}
|
|
|
|
gBattleMons[battlerId].ppBonuses = GetMonData(&gPlayerParty[partyIndex], MON_DATA_PP_BONUSES, NULL);
|
|
gBattleMons[battlerId].friendship = GetMonData(&gPlayerParty[partyIndex], MON_DATA_FRIENDSHIP, NULL);
|
|
gBattleMons[battlerId].experience = GetMonData(&gPlayerParty[partyIndex], MON_DATA_EXP, NULL);
|
|
gBattleMons[battlerId].hpIV = GetMonData(&gPlayerParty[partyIndex], MON_DATA_HP_IV, NULL);
|
|
gBattleMons[battlerId].attackIV = GetMonData(&gPlayerParty[partyIndex], MON_DATA_ATK_IV, NULL);
|
|
gBattleMons[battlerId].defenseIV = GetMonData(&gPlayerParty[partyIndex], MON_DATA_DEF_IV, NULL);
|
|
gBattleMons[battlerId].speedIV = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPEED_IV, NULL);
|
|
gBattleMons[battlerId].spAttackIV = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPATK_IV, NULL);
|
|
gBattleMons[battlerId].spDefenseIV = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPDEF_IV, NULL);
|
|
gBattleMons[battlerId].personality = GetMonData(&gPlayerParty[partyIndex], MON_DATA_PERSONALITY, NULL);
|
|
gBattleMons[battlerId].status1 = GetMonData(&gPlayerParty[partyIndex], MON_DATA_STATUS, NULL);
|
|
gBattleMons[battlerId].level = GetMonData(&gPlayerParty[partyIndex], MON_DATA_LEVEL, NULL);
|
|
gBattleMons[battlerId].hp = GetMonData(&gPlayerParty[partyIndex], MON_DATA_HP, NULL);
|
|
gBattleMons[battlerId].maxHP = GetMonData(&gPlayerParty[partyIndex], MON_DATA_MAX_HP, NULL);
|
|
gBattleMons[battlerId].attack = GetMonData(&gPlayerParty[partyIndex], MON_DATA_ATK, NULL);
|
|
gBattleMons[battlerId].defense = GetMonData(&gPlayerParty[partyIndex], MON_DATA_DEF, NULL);
|
|
gBattleMons[battlerId].speed = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPEED, NULL);
|
|
gBattleMons[battlerId].spAttack = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPATK, NULL);
|
|
gBattleMons[battlerId].spDefense = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPDEF, NULL);
|
|
// gBattleMons[battlerId].isEgg = GetMonData(&gPlayerParty[partyIndex], MON_DATA_IS_EGG, NULL);
|
|
gBattleMons[battlerId].abilityNum = GetMonData(&gPlayerParty[partyIndex], MON_DATA_ABILITY_NUM, NULL);
|
|
gBattleMons[battlerId].otId = GetMonData(&gPlayerParty[partyIndex], MON_DATA_OT_ID, NULL);
|
|
gBattleMons[battlerId].type1 = gSpeciesInfo[gBattleMons[battlerId].species].types[0];
|
|
gBattleMons[battlerId].type2 = gSpeciesInfo[gBattleMons[battlerId].species].types[1];
|
|
gBattleMons[battlerId].ability = GetAbilityBySpecies(gBattleMons[battlerId].species, gBattleMons[battlerId].abilityNum);
|
|
GetMonData(&gPlayerParty[partyIndex], MON_DATA_NICKNAME, nickname);
|
|
StringCopy_Nickname(gBattleMons[battlerId].nickname, nickname);
|
|
GetMonData(&gPlayerParty[partyIndex], MON_DATA_OT_NAME, gBattleMons[battlerId].otName);
|
|
|
|
hpSwitchout = &gBattleStruct->hpOnSwitchout[GetBattlerSide(battlerId)];
|
|
*hpSwitchout = gBattleMons[battlerId].hp;
|
|
|
|
for (i = 0; i < NUM_BATTLE_STATS; i++)
|
|
gBattleMons[battlerId].statStages[i] = DEFAULT_STAT_STAGE;
|
|
|
|
gBattleMons[battlerId].status2 = 0;
|
|
UpdateSentPokesToOpponentValue(battlerId);
|
|
ClearTemporarySpeciesSpriteData(battlerId, FALSE);
|
|
}
|
|
|
|
bool8 ExecuteTableBasedItemEffect(struct Pokemon *mon, u16 item, u8 partyIndex, u8 moveIndex)
|
|
{
|
|
return PokemonUseItemEffects(mon, item, partyIndex, moveIndex, FALSE);
|
|
}
|
|
|
|
#define UPDATE_FRIENDSHIP_FROM_ITEM() \
|
|
{ \
|
|
if ((retVal == 0 || friendshipOnly) && !ShouldSkipFriendshipChange() && friendshipChange == 0) \
|
|
{ \
|
|
friendshipChange = itemEffect[itemEffectParam]; \
|
|
friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL); \
|
|
if (friendshipChange > 0 && holdEffect == HOLD_EFFECT_FRIENDSHIP_UP) \
|
|
friendship += 150 * friendshipChange / 100; \
|
|
else \
|
|
friendship += friendshipChange; \
|
|
if (friendshipChange > 0) \
|
|
{ \
|
|
if (GetMonData(mon, MON_DATA_POKEBALL, NULL) == ITEM_LUXURY_BALL) \
|
|
friendship++; \
|
|
if (GetMonData(mon, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId()) \
|
|
friendship++; \
|
|
} \
|
|
if (friendship < 0) \
|
|
friendship = 0; \
|
|
if (friendship > MAX_FRIENDSHIP) \
|
|
friendship = MAX_FRIENDSHIP; \
|
|
SetMonData(mon, MON_DATA_FRIENDSHIP, &friendship); \
|
|
retVal = FALSE; \
|
|
} \
|
|
}
|
|
|
|
// EXP candies store an index for this table in their holdEffectParam.
|
|
const u32 sExpCandyExperienceTable[] = {
|
|
[EXP_100 - 1] = 100,
|
|
[EXP_800 - 1] = 800,
|
|
[EXP_3000 - 1] = 3000,
|
|
[EXP_10000 - 1] = 10000,
|
|
[EXP_30000 - 1] = 30000,
|
|
};
|
|
|
|
// Returns TRUE if the item has no effect on the Pokémon, FALSE otherwise
|
|
bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 moveIndex, bool8 usedByAI)
|
|
{
|
|
u32 dataUnsigned;
|
|
s32 dataSigned, evCap;
|
|
s32 friendship;
|
|
s32 i;
|
|
bool8 retVal = TRUE;
|
|
const u8 *itemEffect;
|
|
u8 itemEffectParam = ITEM_EFFECT_ARG_START;
|
|
u32 temp1, temp2;
|
|
s8 friendshipChange = 0;
|
|
u8 holdEffect;
|
|
u8 battlerId = MAX_BATTLERS_COUNT;
|
|
u32 friendshipOnly = FALSE;
|
|
u16 heldItem;
|
|
u8 effectFlags;
|
|
s8 evChange;
|
|
u16 evCount;
|
|
|
|
// Get item hold effect
|
|
heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, NULL);
|
|
if (heldItem == ITEM_ENIGMA_BERRY)
|
|
{
|
|
if (gMain.inBattle)
|
|
holdEffect = gEnigmaBerries[gBattlerInMenuId].holdEffect;
|
|
else
|
|
holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
|
|
}
|
|
else
|
|
{
|
|
holdEffect = ItemId_GetHoldEffect(heldItem);
|
|
}
|
|
|
|
// Get battler id (if relevant)
|
|
gPotentialItemEffectBattler = gBattlerInMenuId;
|
|
if (gMain.inBattle)
|
|
{
|
|
gActiveBattler = gBattlerInMenuId;
|
|
i = (GetBattlerSide(gActiveBattler) != B_SIDE_PLAYER);
|
|
while (i < gBattlersCount)
|
|
{
|
|
if (gBattlerPartyIndexes[i] == partyIndex)
|
|
{
|
|
battlerId = i;
|
|
break;
|
|
}
|
|
i += 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gActiveBattler = 0;
|
|
battlerId = MAX_BATTLERS_COUNT;
|
|
}
|
|
|
|
// Skip using the item if it won't do anything
|
|
if (ItemId_GetEffect(item) == NULL && item != ITEM_ENIGMA_BERRY)
|
|
return TRUE;
|
|
|
|
// Get item effect
|
|
itemEffect = ItemId_GetEffect(item);
|
|
|
|
for (i = 0; i < ITEM_EFFECT_ARG_START; i++)
|
|
{
|
|
switch (i)
|
|
{
|
|
|
|
// Handle ITEM0 effects (infatuation, Dire Hit, X Attack). ITEM0_SACRED_ASH is handled in party_menu.c
|
|
// Now handled in item battle scripts.
|
|
case 0:
|
|
break;
|
|
|
|
// Handle ITEM1 effects (in-battle stat boosting effects)
|
|
// Now handled in item battle scripts.
|
|
case 1:
|
|
break;
|
|
// Formerly used by the item effects of the X Sp. Atk and the X Accuracy
|
|
case 2:
|
|
break;
|
|
|
|
// Handle ITEM3 effects (Guard Spec, Rare Candy, cure status)
|
|
case 3:
|
|
// Rare Candy / EXP Candy
|
|
if ((itemEffect[i] & ITEM3_LEVEL_UP)
|
|
&& GetMonData(mon, MON_DATA_LEVEL, NULL) != MAX_LEVEL)
|
|
{
|
|
u8 param = ItemId_GetHoldEffectParam(item);
|
|
dataUnsigned = 0;
|
|
|
|
if (param == 0) // Rare Candy
|
|
{
|
|
dataUnsigned = gExperienceTables[gSpeciesInfo[GetMonData(mon, MON_DATA_SPECIES, NULL)].growthRate][GetMonData(mon, MON_DATA_LEVEL, NULL) + 1];
|
|
}
|
|
else if (param - 1 < ARRAY_COUNT(sExpCandyExperienceTable)) // EXP Candies
|
|
{
|
|
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
dataUnsigned = sExpCandyExperienceTable[param - 1] + GetMonData(mon, MON_DATA_EXP, NULL);
|
|
if (dataUnsigned > gExperienceTables[gSpeciesInfo[species].growthRate][MAX_LEVEL])
|
|
dataUnsigned = gExperienceTables[gSpeciesInfo[species].growthRate][MAX_LEVEL];
|
|
}
|
|
|
|
if (dataUnsigned != 0) // Failsafe
|
|
{
|
|
SetMonData(mon, MON_DATA_EXP, &dataUnsigned);
|
|
CalculateMonStats(mon);
|
|
retVal = FALSE;
|
|
}
|
|
}
|
|
|
|
// Cure status
|
|
if ((itemEffect[i] & ITEM3_SLEEP) && HealStatusConditions(mon, STATUS1_SLEEP, battlerId) == 0)
|
|
retVal = FALSE;
|
|
if ((itemEffect[i] & ITEM3_POISON) && HealStatusConditions(mon, STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER, battlerId) == 0)
|
|
retVal = FALSE;
|
|
if ((itemEffect[i] & ITEM3_BURN) && HealStatusConditions(mon, STATUS1_BURN, battlerId) == 0)
|
|
retVal = FALSE;
|
|
if ((itemEffect[i] & ITEM3_FREEZE) && HealStatusConditions(mon, STATUS1_FREEZE | STATUS1_FROSTBITE, battlerId) == 0)
|
|
retVal = FALSE;
|
|
if ((itemEffect[i] & ITEM3_PARALYSIS) && HealStatusConditions(mon, STATUS1_PARALYSIS, battlerId) == 0)
|
|
retVal = FALSE;
|
|
break;
|
|
|
|
// Handle ITEM4 effects (Change HP/Atk EVs, HP heal, PP heal, PP up, Revive, and evolution stones)
|
|
case 4:
|
|
effectFlags = itemEffect[i];
|
|
|
|
// PP Up
|
|
if (effectFlags & ITEM4_PP_UP)
|
|
{
|
|
effectFlags &= ~ITEM4_PP_UP;
|
|
dataUnsigned = (GetMonData(mon, MON_DATA_PP_BONUSES, NULL) & gPPUpGetMask[moveIndex]) >> (moveIndex * 2);
|
|
temp1 = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex);
|
|
if (dataUnsigned <= 2 && temp1 > 4)
|
|
{
|
|
dataUnsigned = GetMonData(mon, MON_DATA_PP_BONUSES, NULL) + gPPUpAddValues[moveIndex];
|
|
SetMonData(mon, MON_DATA_PP_BONUSES, &dataUnsigned);
|
|
|
|
dataUnsigned = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), dataUnsigned, moveIndex) - temp1;
|
|
dataUnsigned = GetMonData(mon, MON_DATA_PP1 + moveIndex, NULL) + dataUnsigned;
|
|
SetMonData(mon, MON_DATA_PP1 + moveIndex, &dataUnsigned);
|
|
retVal = FALSE;
|
|
}
|
|
}
|
|
temp1 = 0;
|
|
|
|
// Loop through and try each of the remaining ITEM4 effects
|
|
while (effectFlags != 0)
|
|
{
|
|
if (effectFlags & 1)
|
|
{
|
|
switch (temp1)
|
|
{
|
|
case 0: // ITEM4_EV_HP
|
|
case 1: // ITEM4_EV_ATK
|
|
evCount = GetMonEVCount(mon);
|
|
temp2 = itemEffect[itemEffectParam];
|
|
dataSigned = GetMonData(mon, sGetMonDataEVConstants[temp1], NULL);
|
|
evChange = temp2;
|
|
|
|
if (evChange > 0) // Increasing EV (HP or Atk)
|
|
{
|
|
// Has EV increase limit already been reached?
|
|
if (evCount >= MAX_TOTAL_EVS)
|
|
return TRUE;
|
|
|
|
if (itemEffect[10] & ITEM10_IS_VITAMIN)
|
|
evCap = EV_ITEM_RAISE_LIMIT;
|
|
else
|
|
evCap = MAX_PER_STAT_EVS;
|
|
|
|
if (dataSigned >= evCap)
|
|
break;
|
|
|
|
// Limit the increase
|
|
if (dataSigned + evChange > evCap)
|
|
temp2 = evCap - (dataSigned + evChange) + evChange;
|
|
else
|
|
temp2 = evChange;
|
|
|
|
if (evCount + temp2 > MAX_TOTAL_EVS)
|
|
temp2 += MAX_TOTAL_EVS - (evCount + temp2);
|
|
|
|
dataSigned += temp2;
|
|
}
|
|
else if (evChange < 0) // Decreasing EV (HP or Atk)
|
|
{
|
|
if (dataSigned == 0)
|
|
{
|
|
// No EVs to lose, but make sure friendship updates anyway
|
|
friendshipOnly = TRUE;
|
|
itemEffectParam++;
|
|
break;
|
|
}
|
|
dataSigned += evChange;
|
|
#if I_EV_LOWERING_BERRY_JUMP == GEN_4
|
|
if (dataSigned > 100)
|
|
dataSigned = 100;
|
|
#endif
|
|
if (dataSigned < 0)
|
|
dataSigned = 0;
|
|
}
|
|
else // Reset EV (HP or Atk)
|
|
{
|
|
if (dataSigned == 0)
|
|
break;
|
|
|
|
dataSigned = 0;
|
|
}
|
|
|
|
// Update EVs and stats
|
|
SetMonData(mon, sGetMonDataEVConstants[temp1], &dataSigned);
|
|
CalculateMonStats(mon);
|
|
itemEffectParam++;
|
|
retVal = FALSE;
|
|
break;
|
|
|
|
case 2: // ITEM4_HEAL_HP
|
|
// Check use validity.
|
|
if ((effectFlags & (ITEM4_REVIVE >> 2) && GetMonData(mon, MON_DATA_HP, NULL) != 0)
|
|
|| (!(effectFlags & (ITEM4_REVIVE >> 2)) && GetMonData(mon, MON_DATA_HP, NULL) == 0))
|
|
{
|
|
itemEffectParam++;
|
|
break;
|
|
}
|
|
|
|
// Get amount of HP to restore
|
|
dataUnsigned = itemEffect[itemEffectParam++];
|
|
switch (dataUnsigned)
|
|
{
|
|
case ITEM6_HEAL_HP_FULL:
|
|
dataUnsigned = GetMonData(mon, MON_DATA_MAX_HP, NULL) - GetMonData(mon, MON_DATA_HP, NULL);
|
|
break;
|
|
case ITEM6_HEAL_HP_HALF:
|
|
dataUnsigned = GetMonData(mon, MON_DATA_MAX_HP, NULL) / 2;
|
|
if (dataUnsigned == 0)
|
|
dataUnsigned = 1;
|
|
break;
|
|
case ITEM6_HEAL_HP_LVL_UP:
|
|
dataUnsigned = gBattleScripting.levelUpHP;
|
|
break;
|
|
case ITEM6_HEAL_HP_QUARTER:
|
|
dataUnsigned = GetMonData(mon, MON_DATA_MAX_HP, NULL) / 4;
|
|
if (dataUnsigned == 0)
|
|
dataUnsigned = 1;
|
|
break;
|
|
}
|
|
|
|
// Only restore HP if not at max health
|
|
if (GetMonData(mon, MON_DATA_MAX_HP, NULL) != GetMonData(mon, MON_DATA_HP, NULL))
|
|
{
|
|
// Restore HP
|
|
dataUnsigned = GetMonData(mon, MON_DATA_HP, NULL) + dataUnsigned;
|
|
if (dataUnsigned > GetMonData(mon, MON_DATA_MAX_HP, NULL))
|
|
dataUnsigned = GetMonData(mon, MON_DATA_MAX_HP, NULL);
|
|
SetMonData(mon, MON_DATA_HP, &dataUnsigned);
|
|
retVal = FALSE;
|
|
}
|
|
effectFlags &= ~(ITEM4_REVIVE >> 2);
|
|
break;
|
|
|
|
case 3: // ITEM4_HEAL_PP
|
|
if (!(effectFlags & (ITEM4_HEAL_PP_ONE >> 3)))
|
|
{
|
|
// Heal PP for all moves
|
|
for (temp2 = 0; (signed)(temp2) < (signed)(MAX_MON_MOVES); temp2++)
|
|
{
|
|
u16 moveId;
|
|
dataUnsigned = GetMonData(mon, MON_DATA_PP1 + temp2, NULL);
|
|
moveId = GetMonData(mon, MON_DATA_MOVE1 + temp2, NULL);
|
|
if (dataUnsigned != CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), temp2))
|
|
{
|
|
dataUnsigned += itemEffect[itemEffectParam];
|
|
moveId = GetMonData(mon, MON_DATA_MOVE1 + temp2, NULL); // Redundant
|
|
if (dataUnsigned > CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), temp2))
|
|
{
|
|
moveId = GetMonData(mon, MON_DATA_MOVE1 + temp2, NULL); // Redundant
|
|
dataUnsigned = CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), temp2);
|
|
}
|
|
SetMonData(mon, MON_DATA_PP1 + temp2, &dataUnsigned);
|
|
retVal = FALSE;
|
|
}
|
|
}
|
|
itemEffectParam++;
|
|
}
|
|
else
|
|
{
|
|
// Heal PP for one move
|
|
u16 moveId;
|
|
dataUnsigned = GetMonData(mon, MON_DATA_PP1 + moveIndex, NULL);
|
|
moveId = GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL);
|
|
if (dataUnsigned != CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex))
|
|
{
|
|
dataUnsigned += itemEffect[itemEffectParam++];
|
|
moveId = GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL); // Redundant
|
|
if (dataUnsigned > CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex))
|
|
{
|
|
moveId = GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL); // Redundant
|
|
dataUnsigned = CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex);
|
|
}
|
|
SetMonData(mon, MON_DATA_PP1 + moveIndex, &dataUnsigned);
|
|
retVal = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// cases 4-6 are ITEM4_HEAL_PP_ONE, ITEM4_PP_UP, and ITEM4_REVIVE, which
|
|
// are already handled above by other cases or before the loop
|
|
|
|
case 7: // ITEM4_EVO_STONE
|
|
{
|
|
u16 targetSpecies = GetEvolutionTargetSpecies(mon, EVO_MODE_ITEM_USE, item, NULL);
|
|
|
|
if (targetSpecies != SPECIES_NONE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
temp1++;
|
|
effectFlags >>= 1;
|
|
}
|
|
break;
|
|
|
|
// Handle ITEM5 effects (Change Def/SpDef/SpAtk/Speed EVs, PP Max, and friendship changes)
|
|
case 5:
|
|
effectFlags = itemEffect[i];
|
|
temp1 = 0;
|
|
|
|
// Loop through and try each of the ITEM5 effects
|
|
while (effectFlags != 0)
|
|
{
|
|
if (effectFlags & 1)
|
|
{
|
|
switch (temp1)
|
|
{
|
|
case 0: // ITEM5_EV_DEF
|
|
case 1: // ITEM5_EV_SPEED
|
|
case 2: // ITEM5_EV_SPDEF
|
|
case 3: // ITEM5_EV_SPATK
|
|
evCount = GetMonEVCount(mon);
|
|
temp2 = itemEffect[itemEffectParam];
|
|
dataSigned = GetMonData(mon, sGetMonDataEVConstants[temp1 + 2], NULL);
|
|
evChange = temp2;
|
|
if (evChange > 0) // Increasing EV
|
|
{
|
|
// Has EV increase limit already been reached?
|
|
if (evCount >= MAX_TOTAL_EVS)
|
|
return TRUE;
|
|
|
|
if (itemEffect[10] & ITEM10_IS_VITAMIN)
|
|
evCap = EV_ITEM_RAISE_LIMIT;
|
|
else
|
|
evCap = MAX_PER_STAT_EVS;
|
|
|
|
if (dataSigned >= evCap)
|
|
break;
|
|
|
|
// Limit the increase
|
|
if (dataSigned + evChange > evCap)
|
|
temp2 = evCap - (dataSigned + evChange) + evChange;
|
|
else
|
|
temp2 = evChange;
|
|
|
|
if (evCount + temp2 > MAX_TOTAL_EVS)
|
|
temp2 += MAX_TOTAL_EVS - (evCount + temp2);
|
|
|
|
dataSigned += temp2;
|
|
}
|
|
else if (evChange < 0) // Decreasing EV
|
|
{
|
|
if (dataSigned == 0)
|
|
{
|
|
// No EVs to lose, but make sure friendship updates anyway
|
|
friendshipOnly = TRUE;
|
|
itemEffectParam++;
|
|
break;
|
|
}
|
|
dataSigned += evChange;
|
|
#if I_BERRY_EV_JUMP == GEN_4
|
|
if (dataSigned > 100)
|
|
dataSigned = 100;
|
|
#endif
|
|
if (dataSigned < 0)
|
|
dataSigned = 0;
|
|
}
|
|
else // Reset EV
|
|
{
|
|
if (dataSigned == 0)
|
|
break;
|
|
|
|
dataSigned = 0;
|
|
}
|
|
|
|
// Update EVs and stats
|
|
SetMonData(mon, sGetMonDataEVConstants[temp1 + 2], &dataSigned);
|
|
CalculateMonStats(mon);
|
|
retVal = FALSE;
|
|
itemEffectParam++;
|
|
break;
|
|
|
|
case 4: // ITEM5_PP_MAX
|
|
dataUnsigned = (GetMonData(mon, MON_DATA_PP_BONUSES, NULL) & gPPUpGetMask[moveIndex]) >> (moveIndex * 2);
|
|
temp2 = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex);
|
|
|
|
// Check if 3 PP Ups have been applied already, and that the move has a total PP of at least 5 (excludes Sketch)
|
|
if (dataUnsigned < 3 && temp2 >= 5)
|
|
{
|
|
dataUnsigned = GetMonData(mon, MON_DATA_PP_BONUSES, NULL);
|
|
dataUnsigned &= gPPUpClearMask[moveIndex];
|
|
dataUnsigned += gPPUpAddValues[moveIndex] * 3; // Apply 3 PP Ups (max)
|
|
|
|
SetMonData(mon, MON_DATA_PP_BONUSES, &dataUnsigned);
|
|
dataUnsigned = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), dataUnsigned, moveIndex) - temp2;
|
|
dataUnsigned = GetMonData(mon, MON_DATA_PP1 + moveIndex, NULL) + dataUnsigned;
|
|
SetMonData(mon, MON_DATA_PP1 + moveIndex, &dataUnsigned);
|
|
retVal = FALSE;
|
|
}
|
|
break;
|
|
|
|
case 5: // ITEM5_FRIENDSHIP_LOW
|
|
// Changes to friendship are given differently depending on
|
|
// how much friendship the Pokémon already has.
|
|
// In general, Pokémon with lower friendship receive more,
|
|
// and Pokémon with higher friendship receive less.
|
|
if (GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) < 100)
|
|
UPDATE_FRIENDSHIP_FROM_ITEM();
|
|
itemEffectParam++;
|
|
break;
|
|
|
|
case 6: // ITEM5_FRIENDSHIP_MID
|
|
if (GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) >= 100 && GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) < 200)
|
|
UPDATE_FRIENDSHIP_FROM_ITEM();
|
|
itemEffectParam++;
|
|
break;
|
|
|
|
case 7: // ITEM5_FRIENDSHIP_HIGH
|
|
if (GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) >= 200)
|
|
UPDATE_FRIENDSHIP_FROM_ITEM();
|
|
itemEffectParam++;
|
|
break;
|
|
}
|
|
}
|
|
temp1++;
|
|
effectFlags >>= 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
bool8 HealStatusConditions(struct Pokemon *mon, u32 healMask, u8 battleId)
|
|
{
|
|
u32 status = GetMonData(mon, MON_DATA_STATUS, NULL);
|
|
|
|
if (status & healMask)
|
|
{
|
|
status &= ~healMask;
|
|
SetMonData(mon, MON_DATA_STATUS, &status);
|
|
if (gMain.inBattle && battleId != MAX_BATTLERS_COUNT) {
|
|
gBattleMons[battleId].status1 &= ~healMask;
|
|
}
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static bool8 PartyMonHasStatus(struct Pokemon *mon, u32 unused, u32 healMask, u8 battleId)
|
|
{
|
|
if ((GetMonData(mon, MON_DATA_STATUS, NULL) & healMask) != 0)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
u8 GetItemEffectParamOffset(u16 itemId, u8 effectByte, u8 effectBit)
|
|
{
|
|
const u8 *temp;
|
|
const u8 *itemEffect;
|
|
u8 offset;
|
|
int i;
|
|
u8 j;
|
|
u8 val;
|
|
|
|
offset = ITEM_EFFECT_ARG_START;
|
|
|
|
temp = ItemId_GetEffect(itemId);
|
|
|
|
if (!temp && itemId != ITEM_ENIGMA_BERRY)
|
|
return 0;
|
|
|
|
if (itemId == ITEM_ENIGMA_BERRY)
|
|
{
|
|
temp = gEnigmaBerries[gActiveBattler].itemEffect;
|
|
}
|
|
|
|
itemEffect = temp;
|
|
|
|
for (i = 0; i < ITEM_EFFECT_ARG_START; i++)
|
|
{
|
|
switch (i)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
if (i == effectByte)
|
|
return 0;
|
|
break;
|
|
case 4:
|
|
val = itemEffect[4];
|
|
if (val & ITEM4_PP_UP)
|
|
val &= ~ITEM4_PP_UP;
|
|
j = 0;
|
|
while (val)
|
|
{
|
|
if (val & 1)
|
|
{
|
|
switch (j)
|
|
{
|
|
case 2: // ITEM4_HEAL_HP
|
|
if (val & (ITEM4_REVIVE >> 2))
|
|
val &= ~(ITEM4_REVIVE >> 2);
|
|
// fallthrough
|
|
case 0: // ITEM4_EV_HP
|
|
if (i == effectByte && (val & effectBit))
|
|
return offset;
|
|
offset++;
|
|
break;
|
|
case 1: // ITEM4_EV_ATK
|
|
if (i == effectByte && (val & effectBit))
|
|
return offset;
|
|
offset++;
|
|
break;
|
|
case 3: // ITEM4_HEAL_PP
|
|
if (i == effectByte && (val & effectBit))
|
|
return offset;
|
|
offset++;
|
|
break;
|
|
case 7: // ITEM4_EVO_STONE
|
|
if (i == effectByte)
|
|
return 0;
|
|
break;
|
|
}
|
|
}
|
|
j++;
|
|
val >>= 1;
|
|
if (i == effectByte)
|
|
effectBit >>= 1;
|
|
}
|
|
break;
|
|
case 5:
|
|
val = itemEffect[5];
|
|
j = 0;
|
|
while (val)
|
|
{
|
|
if (val & 1)
|
|
{
|
|
switch (j)
|
|
{
|
|
case 0: // ITEM5_EV_DEF
|
|
case 1: // ITEM5_EV_SPEED
|
|
case 2: // ITEM5_EV_SPDEF
|
|
case 3: // ITEM5_EV_SPATK
|
|
case 4: // ITEM5_PP_MAX
|
|
case 5: // ITEM5_FRIENDSHIP_LOW
|
|
case 6: // ITEM5_FRIENDSHIP_MID
|
|
if (i == effectByte && (val & effectBit))
|
|
return offset;
|
|
offset++;
|
|
break;
|
|
case 7: // ITEM5_FRIENDSHIP_HIGH
|
|
if (i == effectByte)
|
|
return 0;
|
|
break;
|
|
}
|
|
}
|
|
j++;
|
|
val >>= 1;
|
|
if (i == effectByte)
|
|
effectBit >>= 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
static void BufferStatRoseMessage(int statIdx)
|
|
{
|
|
gBattlerTarget = gBattlerInMenuId;
|
|
StringCopy(gBattleTextBuff1, gStatNamesTable[sStatsToRaise[statIdx]]);
|
|
StringCopy(gBattleTextBuff2, gBattleText_Rose);
|
|
BattleStringExpandPlaceholdersToDisplayedString(gText_DefendersStatRose);
|
|
}
|
|
|
|
u8 GetNature(struct Pokemon *mon)
|
|
{
|
|
return GetMonData(mon, MON_DATA_PERSONALITY, NULL) % NUM_NATURES;
|
|
}
|
|
|
|
static u8 GetNatureFromPersonality(u32 personality)
|
|
{
|
|
return personality % NUM_NATURES;
|
|
}
|
|
|
|
static u32 GetGMaxTargetSpecies(u32 species)
|
|
{
|
|
// const struct FormChange *formChanges = GetSpeciesFormChanges(species);
|
|
// u32 i;
|
|
// for (i = 0; formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
|
|
// {
|
|
// if (formChanges[i].method == FORM_CHANGE_BATTLE_GIGANTAMAX)
|
|
// return formChanges[i].targetSpecies;
|
|
// }
|
|
return SPECIES_NONE;
|
|
}
|
|
|
|
u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 mode, u16 evolutionItem, struct Pokemon *tradePartner)
|
|
{
|
|
int i, j;
|
|
u16 targetSpecies = SPECIES_NONE;
|
|
u16 species = GetMonData(mon, MON_DATA_SPECIES, 0);
|
|
u16 heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, 0);
|
|
u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, 0);
|
|
u8 level;
|
|
u16 friendship;
|
|
u8 beauty = GetMonData(mon, MON_DATA_BEAUTY, 0);
|
|
u16 upperPersonality = personality >> 16;
|
|
u32 holdEffect, currentMap, partnerSpecies, partnerHeldItem, partnerHoldEffect;
|
|
bool32 consumeItem = FALSE;
|
|
u16 evolutionTracker = GetMonData(mon, MON_DATA_EVOLUTION_TRACKER, 0);
|
|
const struct Evolution *evolutions = GetSpeciesEvolutions(species);
|
|
|
|
if (evolutions == NULL)
|
|
return SPECIES_NONE;
|
|
|
|
if (tradePartner != NULL)
|
|
{
|
|
partnerSpecies = GetMonData(tradePartner, MON_DATA_SPECIES, 0);
|
|
partnerHeldItem = GetMonData(tradePartner, MON_DATA_HELD_ITEM, 0);
|
|
|
|
if (partnerHeldItem == ITEM_ENIGMA_BERRY_E_READER)
|
|
#if FREE_ENIGMA_BERRY == FALSE
|
|
partnerHoldEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
|
|
#else
|
|
partnerHoldEffect = 0;
|
|
#endif //FREE_ENIGMA_BERRY
|
|
else
|
|
partnerHoldEffect = ItemId_GetHoldEffect(partnerHeldItem);
|
|
}
|
|
else
|
|
{
|
|
partnerSpecies = SPECIES_NONE;
|
|
partnerHeldItem = ITEM_NONE;
|
|
partnerHoldEffect = HOLD_EFFECT_NONE;
|
|
}
|
|
|
|
if (heldItem == ITEM_ENIGMA_BERRY_E_READER)
|
|
#if FREE_ENIGMA_BERRY == FALSE
|
|
holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
|
|
#else
|
|
holdEffect = 0;
|
|
#endif //FREE_ENIGMA_BERRY
|
|
else
|
|
holdEffect = ItemId_GetHoldEffect(heldItem);
|
|
|
|
// Prevent evolution with Everstone, unless we're just viewing the party menu with an evolution item
|
|
if (holdEffect == HOLD_EFFECT_PREVENT_EVOLVE
|
|
&& mode != EVO_MODE_ITEM_CHECK
|
|
&& (P_KADABRA_EVERSTONE < GEN_4 || species != SPECIES_KADABRA))
|
|
return SPECIES_NONE;
|
|
|
|
switch (mode)
|
|
{
|
|
case EVO_MODE_NORMAL:
|
|
level = GetMonData(mon, MON_DATA_LEVEL, 0);
|
|
friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, 0);
|
|
|
|
for (i = 0; evolutions[i].method != EVOLUTIONS_END; i++)
|
|
{
|
|
if (SanitizeSpeciesId(evolutions[i].targetSpecies) == SPECIES_NONE)
|
|
continue;
|
|
|
|
switch (evolutions[i].method)
|
|
{
|
|
case EVO_FRIENDSHIP:
|
|
if (friendship >= FRIENDSHIP_EVO_THRESHOLD)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_FRIENDSHIP_DAY:
|
|
if (/*GetTimeOfDay() != TIME_NIGHT &&*/ friendship >= FRIENDSHIP_EVO_THRESHOLD)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_LEVEL_DAY:
|
|
if (/* GetTimeOfDay() != TIME_NIGHT && */ evolutions[i].param <= level)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_FRIENDSHIP_NIGHT:
|
|
if (/* GetTimeOfDay() == TIME_NIGHT && */ friendship >= FRIENDSHIP_EVO_THRESHOLD)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_LEVEL_NIGHT:
|
|
if (/* GetTimeOfDay() == TIME_NIGHT && */ evolutions[i].param <= level)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_ITEM_HOLD_NIGHT:
|
|
if (/* GetTimeOfDay() == TIME_NIGHT && */ heldItem == evolutions[i].param)
|
|
{
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
consumeItem = TRUE;
|
|
}
|
|
break;
|
|
case EVO_ITEM_HOLD_DAY:
|
|
if (/* GetTimeOfDay() != TIME_NIGHT && */ heldItem == evolutions[i].param)
|
|
{
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
consumeItem = TRUE;
|
|
}
|
|
break;
|
|
case EVO_LEVEL_DUSK:
|
|
if (/* GetTimeOfDay() == TIME_EVENING && */ evolutions[i].param <= level)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_LEVEL:
|
|
if (evolutions[i].param <= level)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_LEVEL_FEMALE:
|
|
if (evolutions[i].param <= level && GetMonGender(mon) == MON_FEMALE)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_LEVEL_MALE:
|
|
if (evolutions[i].param <= level && GetMonGender(mon) == MON_MALE)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_LEVEL_ATK_GT_DEF:
|
|
if (evolutions[i].param <= level)
|
|
if (GetMonData(mon, MON_DATA_ATK, 0) > GetMonData(mon, MON_DATA_DEF, 0))
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_LEVEL_ATK_EQ_DEF:
|
|
if (evolutions[i].param <= level)
|
|
if (GetMonData(mon, MON_DATA_ATK, 0) == GetMonData(mon, MON_DATA_DEF, 0))
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_LEVEL_ATK_LT_DEF:
|
|
if (evolutions[i].param <= level)
|
|
if (GetMonData(mon, MON_DATA_ATK, 0) < GetMonData(mon, MON_DATA_DEF, 0))
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_LEVEL_SILCOON:
|
|
if (evolutions[i].param <= level && (upperPersonality % 10) <= 4)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_LEVEL_CASCOON:
|
|
if (evolutions[i].param <= level && (upperPersonality % 10) > 4)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_LEVEL_NINJASK:
|
|
if (evolutions[i].param <= level)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_LEVEL_FAMILY_OF_FOUR:
|
|
if (evolutions[i].param <= level && (personality % 100) != 0)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_LEVEL_FAMILY_OF_THREE:
|
|
if (evolutions[i].param <= level && (personality % 100) == 0)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_BEAUTY:
|
|
if (evolutions[i].param <= beauty)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_MOVE:
|
|
if (MonKnowsMove(mon, evolutions[i].param))
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_MOVE_TWO_SEGMENT:
|
|
if (MonKnowsMove(mon, evolutions[i].param) && (personality % 100) != 0)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_MOVE_THREE_SEGMENT:
|
|
if (MonKnowsMove(mon, evolutions[i].param) && (personality % 100) == 0)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_FRIENDSHIP_MOVE_TYPE:
|
|
if (friendship >= FRIENDSHIP_EVO_THRESHOLD)
|
|
{
|
|
for (j = 0; j < MAX_MON_MOVES; j++)
|
|
{
|
|
if (gMovesInfo[GetMonData(mon, MON_DATA_MOVE1 + j, NULL)].type == evolutions[i].param)
|
|
{
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case EVO_SPECIFIC_MON_IN_PARTY:
|
|
for (j = 0; j < PARTY_SIZE; j++)
|
|
{
|
|
if (GetMonData(&gPlayerParty[j], MON_DATA_SPECIES, NULL) == evolutions[i].param)
|
|
{
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case EVO_LEVEL_DARK_TYPE_MON_IN_PARTY:
|
|
if (evolutions[i].param <= level)
|
|
{
|
|
for (j = 0; j < PARTY_SIZE; j++)
|
|
{
|
|
u16 currSpecies = GetMonData(&gPlayerParty[j], MON_DATA_SPECIES, NULL);
|
|
if (gSpeciesInfo[currSpecies].types[0] == TYPE_DARK
|
|
|| gSpeciesInfo[currSpecies].types[1] == TYPE_DARK)
|
|
{
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case EVO_LEVEL_RAIN:
|
|
j = GetCurrentWeather();
|
|
if (evolutions[i].param <= level
|
|
&& (j == WEATHER_RAIN || j == WEATHER_RAIN_THUNDERSTORM || j == WEATHER_DOWNPOUR))
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_LEVEL_FOG:
|
|
j = GetCurrentWeather();
|
|
if (evolutions[i].param <= level
|
|
&& (j == WEATHER_FOG_HORIZONTAL || j == WEATHER_FOG_DIAGONAL))
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_MAPSEC:
|
|
if (gMapHeader.regionMapSectionId == evolutions[i].param)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_SPECIFIC_MAP:
|
|
currentMap = ((gSaveBlock1Ptr->location.mapGroup) << 8 | gSaveBlock1Ptr->location.mapNum);
|
|
if (currentMap == evolutions[i].param)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_LEVEL_NATURE_AMPED:
|
|
if (evolutions[i].param <= level)
|
|
{
|
|
u8 nature = GetNature(mon);
|
|
switch (nature)
|
|
{
|
|
case NATURE_HARDY:
|
|
case NATURE_BRAVE:
|
|
case NATURE_ADAMANT:
|
|
case NATURE_NAUGHTY:
|
|
case NATURE_DOCILE:
|
|
case NATURE_IMPISH:
|
|
case NATURE_LAX:
|
|
case NATURE_HASTY:
|
|
case NATURE_JOLLY:
|
|
case NATURE_NAIVE:
|
|
case NATURE_RASH:
|
|
case NATURE_SASSY:
|
|
case NATURE_QUIRKY:
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case EVO_LEVEL_NATURE_LOW_KEY:
|
|
if (evolutions[i].param <= level)
|
|
{
|
|
u8 nature = GetNature(mon);
|
|
switch (nature)
|
|
{
|
|
case NATURE_LONELY:
|
|
case NATURE_BOLD:
|
|
case NATURE_RELAXED:
|
|
case NATURE_TIMID:
|
|
case NATURE_SERIOUS:
|
|
case NATURE_MODEST:
|
|
case NATURE_MILD:
|
|
case NATURE_QUIET:
|
|
case NATURE_BASHFUL:
|
|
case NATURE_CALM:
|
|
case NATURE_GENTLE:
|
|
case NATURE_CAREFUL:
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case EVO_ITEM_HOLD:
|
|
if (heldItem == evolutions[i].param)
|
|
{
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
consumeItem = TRUE;
|
|
}
|
|
break;
|
|
case EVO_LEVEL_MOVE_TWENTY_TIMES:
|
|
if (evolutionTracker >= 20)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_LEVEL_RECOIL_DAMAGE_MALE:
|
|
if (evolutionTracker >= evolutions[i].param && GetMonGender(mon) == MON_MALE)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_LEVEL_RECOIL_DAMAGE_FEMALE:
|
|
if (evolutionTracker >= evolutions[i].param && GetMonGender(mon) == MON_FEMALE)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case EVO_MODE_TRADE:
|
|
for (i = 0; evolutions[i].method != EVOLUTIONS_END; i++)
|
|
{
|
|
if (SanitizeSpeciesId(evolutions[i].targetSpecies) == SPECIES_NONE)
|
|
continue;
|
|
|
|
switch (evolutions[i].method)
|
|
{
|
|
case EVO_TRADE:
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_TRADE_ITEM:
|
|
if (evolutions[i].param == heldItem)
|
|
{
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
consumeItem = TRUE;
|
|
}
|
|
break;
|
|
case EVO_TRADE_SPECIFIC_MON:
|
|
if (evolutions[i].param == partnerSpecies && partnerHoldEffect != HOLD_EFFECT_PREVENT_EVOLVE)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case EVO_MODE_ITEM_USE:
|
|
case EVO_MODE_ITEM_CHECK:
|
|
for (i = 0; evolutions[i].method != EVOLUTIONS_END; i++)
|
|
{
|
|
if (SanitizeSpeciesId(evolutions[i].targetSpecies) == SPECIES_NONE)
|
|
continue;
|
|
|
|
switch (evolutions[i].method)
|
|
{
|
|
case EVO_ITEM:
|
|
if (evolutions[i].param == evolutionItem)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_ITEM_FEMALE:
|
|
if (GetMonGender(mon) == MON_FEMALE && evolutions[i].param == evolutionItem)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_ITEM_MALE:
|
|
if (GetMonGender(mon) == MON_MALE && evolutions[i].param == evolutionItem)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_ITEM_NIGHT:
|
|
if (/* GetTimeOfDay() == TIME_NIGHT && */ evolutions[i].param == evolutionItem)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_ITEM_DAY:
|
|
if (/* GetTimeOfDay() != TIME_NIGHT && */ evolutions[i].param == evolutionItem)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
// Battle evolution without leveling; party slot is being passed into the evolutionItem arg.
|
|
case EVO_MODE_BATTLE_SPECIAL:
|
|
for (i = 0; evolutions[i].method != EVOLUTIONS_END; i++)
|
|
{
|
|
if (SanitizeSpeciesId(evolutions[i].targetSpecies) == SPECIES_NONE)
|
|
continue;
|
|
|
|
switch (evolutions[i].method)
|
|
{
|
|
case EVO_CRITICAL_HITS:
|
|
if (gPartyCriticalHits[evolutionItem] >= evolutions[i].param)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
// Overworld evolution without leveling; evolution method is being passed into the evolutionItem arg.
|
|
case EVO_MODE_OVERWORLD_SPECIAL:
|
|
for (i = 0; evolutions[i].method != EVOLUTIONS_END; i++)
|
|
{
|
|
if (SanitizeSpeciesId(evolutions[i].targetSpecies) == SPECIES_NONE)
|
|
continue;
|
|
|
|
switch (evolutions[i].method)
|
|
{
|
|
case EVO_SCRIPT_TRIGGER_DMG:
|
|
{
|
|
u16 currentHp = GetMonData(mon, MON_DATA_HP, NULL);
|
|
if (evolutionItem == EVO_SCRIPT_TRIGGER_DMG
|
|
&& currentHp != 0
|
|
&& (GetMonData(mon, MON_DATA_MAX_HP, NULL) - currentHp >= evolutions[i].param))
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
}
|
|
case EVO_DARK_SCROLL:
|
|
if (evolutionItem == EVO_DARK_SCROLL)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
case EVO_WATER_SCROLL:
|
|
if (evolutionItem == EVO_WATER_SCROLL)
|
|
targetSpecies = evolutions[i].targetSpecies;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Pikachu, Meowth, and Eevee cannot evolve if they have the
|
|
// Gigantamax Factor. We assume that is because their evolutions
|
|
// do not have a Gigantamax Form.
|
|
if (GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR, NULL)
|
|
&& GetGMaxTargetSpecies(species) != SPECIES_NONE
|
|
&& GetGMaxTargetSpecies(targetSpecies) == SPECIES_NONE)
|
|
{
|
|
return SPECIES_NONE;
|
|
}
|
|
|
|
if (consumeItem)
|
|
{
|
|
heldItem = ITEM_NONE;
|
|
SetMonData(mon, MON_DATA_HELD_ITEM, &heldItem);
|
|
}
|
|
|
|
return targetSpecies;
|
|
}
|
|
|
|
static u16 HoennPokedexNumToSpecies(u16 hoennNum)
|
|
{
|
|
u16 species;
|
|
|
|
if (!hoennNum)
|
|
return 0;
|
|
|
|
species = 0;
|
|
|
|
while (species < NUM_SPECIES - 1 && SpeciesToHoennPokedexNum(species) != hoennNum)
|
|
species++;
|
|
|
|
if (species == NUM_SPECIES - 1)
|
|
return 0;
|
|
|
|
return species + 1;
|
|
}
|
|
|
|
u16 NationalPokedexNumToSpecies(u16 nationalNum)
|
|
{
|
|
u16 species;
|
|
|
|
if (!nationalNum)
|
|
return 0;
|
|
|
|
species = 1;
|
|
|
|
while (species < NUM_SPECIES && gSpeciesInfo[species].natDexNum != nationalNum)
|
|
species++;
|
|
|
|
if (species == NUM_SPECIES)
|
|
return NATIONAL_DEX_NONE;
|
|
|
|
return species;
|
|
}
|
|
|
|
u16 NationalToKantoOrder(u16 nationalNum)
|
|
{
|
|
u16 kantoNum;
|
|
|
|
if (!nationalNum)
|
|
return 0;
|
|
|
|
kantoNum = 0;
|
|
|
|
while (kantoNum < (KANTO_DEX_COUNT - 1) && sKantoToNationalOrder[kantoNum] != nationalNum)
|
|
kantoNum++;
|
|
|
|
if (kantoNum >= KANTO_DEX_COUNT - 1)
|
|
return 0;
|
|
|
|
return kantoNum + 1;
|
|
}
|
|
|
|
static u16 NationalToHoennOrder(u16 nationalNum)
|
|
{
|
|
u16 hoennNum;
|
|
|
|
if (!nationalNum)
|
|
return 0;
|
|
|
|
hoennNum = 0;
|
|
|
|
while (hoennNum < (HOENN_DEX_COUNT - 1) && sHoennToNationalOrder[hoennNum] != nationalNum)
|
|
hoennNum++;
|
|
|
|
if (hoennNum >= HOENN_DEX_COUNT - 1)
|
|
return 0;
|
|
|
|
return hoennNum + 1;
|
|
// u16 hoennNum;
|
|
|
|
// if (!nationalNum)
|
|
// return 0;
|
|
|
|
// hoennNum = 0;
|
|
|
|
// while (hoennNum < NUM_SPECIES - 1 && sHoennToNationalOrder[hoennNum] != nationalNum)
|
|
// hoennNum++;
|
|
|
|
// if (hoennNum == NUM_SPECIES - 1)
|
|
// return 0;
|
|
|
|
// return hoennNum + 1;
|
|
}
|
|
|
|
u16 SpeciesToNationalPokedexNum(u16 species)
|
|
{
|
|
if (!species)
|
|
return NATIONAL_DEX_NONE;
|
|
|
|
return gSpeciesInfo[species].natDexNum;
|
|
// if (!species)
|
|
// return 0;
|
|
|
|
// return sSpeciesToNationalPokedexNum[species - 1];
|
|
}
|
|
|
|
static u16 SpeciesToHoennPokedexNum(u16 species)
|
|
{
|
|
if (!species)
|
|
return 0;
|
|
return NationalToHoennOrder(gSpeciesInfo[species].natDexNum);
|
|
// if (!species)
|
|
// return 0;
|
|
|
|
// return sSpeciesToHoennPokedexNum[species - 1];
|
|
}
|
|
|
|
u16 KantoToNationalOrder(u16 kantoNum)
|
|
{
|
|
if (!kantoNum || kantoNum >= KANTO_DEX_COUNT)
|
|
return 0;
|
|
|
|
return sKantoToNationalOrder[kantoNum - 1];
|
|
}
|
|
|
|
u16 HoennToNationalOrder(u16 hoennNum)
|
|
{
|
|
if (!hoennNum || hoennNum >= HOENN_DEX_COUNT)
|
|
return 0;
|
|
|
|
return sHoennToNationalOrder[hoennNum - 1];
|
|
|
|
// if (!hoennNum)
|
|
// return 0;
|
|
|
|
// return sHoennToNationalOrder[hoennNum - 1];
|
|
}
|
|
|
|
u16 SpeciesToCryId(u16 species)
|
|
{
|
|
species = SanitizeSpeciesId(species);
|
|
return gSpeciesInfo[species].cryId;
|
|
}
|
|
|
|
// Spots can be drawn on Spinda's color indexes 1, 2, or 3
|
|
#define FIRST_SPOT_COLOR 1
|
|
#define LAST_SPOT_COLOR 3
|
|
|
|
// To draw a spot pixel, add 4 to the color index
|
|
#define SPOT_COLOR_ADJUSTMENT 4
|
|
/*
|
|
The macro below handles drawing the randomly-placed spots on Spinda's front sprite.
|
|
Spinda has 4 spots, each with an entry in sSpindaSpotGraphics. Each entry contains
|
|
a base x and y coordinate for the spot and a 16x16 binary image. Each bit in the image
|
|
determines whether that pixel should be considered part of the spot.
|
|
|
|
The position of each spot is randomized using the Spinda's personality. The entire 32 bit
|
|
personality value is used, 4 bits for each coordinate of the 4 spots. If the personality
|
|
value is 0x87654321, then 0x1 will be used for the 1st spot's x coord, 0x2 will be used for
|
|
the 1st spot's y coord, 0x3 will be used for the 2nd spot's x coord, and so on. Each
|
|
coordinate is calculated as (baseCoord + (given 4 bits of personality) - 8). In effect this
|
|
means each spot can start at any position -8 to +7 off of its base coordinates (256 possibilities).
|
|
|
|
The macro then loops over the 16x16 spot image. For each bit in the spot's binary image, if
|
|
the bit is set then it's part of the spot; try to draw it. A pixel is drawn on Spinda if the
|
|
pixel on Spinda satisfies the following formula: ((u8)(colorIndex - 1) <= 2). The -1 excludes
|
|
transparent pixels, as these are index 0. Therefore only colors 1, 2, or 3 on Spinda will
|
|
allow a spot to be drawn. These color indexes are Spinda's light brown body colors. To create
|
|
the spot it adds 4 to the color index, so Spinda's spots will be colors 5, 6, and 7.
|
|
|
|
The above is done two different ways in the macro: one with << 4, and one without. This
|
|
is because Spinda's sprite is a 4 bits per pixel image, but the pointer to Spinda's pixels
|
|
(destPixels) is an 8 bit pointer, so it addresses two pixels. Shifting by 4 accesses the 2nd
|
|
of these pixels, so this is done every other time.
|
|
*/
|
|
#define DRAW_SPINDA_SPOTS(personality, dest) \
|
|
{ \
|
|
s32 i; \
|
|
for (i = 0; i < (s32)ARRAY_COUNT(sSpindaSpotGraphics); i++) \
|
|
{ \
|
|
s32 row; \
|
|
u8 x = sSpindaSpotGraphics[i].x + ((personality & 0x0F) - 8); \
|
|
u8 y = sSpindaSpotGraphics[i].y + (((personality & 0xF0) >> 4) - 8); \
|
|
\
|
|
for (row = 0; row < SPINDA_SPOT_HEIGHT; row++) \
|
|
{ \
|
|
s32 column; \
|
|
s32 spotPixelRow = sSpindaSpotGraphics[i].image[row]; \
|
|
\
|
|
for (column = x; column < x + SPINDA_SPOT_WIDTH; column++) \
|
|
{ \
|
|
/* Get target pixels on Spinda's sprite */ \
|
|
u8 *destPixels = dest + ((column / 8) * TILE_SIZE_4BPP) + \
|
|
((column % 8) / 2) + \
|
|
((y / 8) * TILE_SIZE_4BPP * 8) + \
|
|
((y % 8) * 4); \
|
|
\
|
|
/* Is this pixel in the 16x16 spot image part of the spot? */ \
|
|
if (spotPixelRow & 1) \
|
|
{ \
|
|
/* destPixels addressess two pixels, alternate which */ \
|
|
/* of the two pixels is being considered for drawing */ \
|
|
if (column & 1) \
|
|
{ \
|
|
/* Draw spot pixel if this is Spinda's body color */ \
|
|
if ((u8)((*destPixels & 0xF0) - (FIRST_SPOT_COLOR << 4))\
|
|
<= ((LAST_SPOT_COLOR - FIRST_SPOT_COLOR) << 4))\
|
|
*destPixels += (SPOT_COLOR_ADJUSTMENT << 4); \
|
|
} \
|
|
else \
|
|
{ \
|
|
/* Draw spot pixel if this is Spinda's body color */ \
|
|
if ((u8)((*destPixels & 0xF) - FIRST_SPOT_COLOR) \
|
|
<= (LAST_SPOT_COLOR - FIRST_SPOT_COLOR)) \
|
|
*destPixels += SPOT_COLOR_ADJUSTMENT; \
|
|
} \
|
|
} \
|
|
\
|
|
spotPixelRow >>= 1; \
|
|
} \
|
|
\
|
|
y++; \
|
|
} \
|
|
\
|
|
personality >>= 8; \
|
|
} \
|
|
}
|
|
|
|
// Same as DrawSpindaSpots but attempts to discern for itself whether or
|
|
// not it's the front pic.
|
|
static void DrawSpindaSpotsUnused(u16 species, u32 personality, u8 *dest)
|
|
{
|
|
if (species == SPECIES_SPINDA
|
|
&& dest != gMonSpritesGfxPtr->sprites[B_POSITION_PLAYER_LEFT]
|
|
&& dest != gMonSpritesGfxPtr->sprites[B_POSITION_PLAYER_RIGHT])
|
|
DRAW_SPINDA_SPOTS(personality, dest);
|
|
}
|
|
|
|
void DrawSpindaSpots(u32 species, u32 personality, u8 *dest, bool8 isFrontPic)
|
|
{
|
|
if (species == SPECIES_SPINDA && isFrontPic)
|
|
DRAW_SPINDA_SPOTS(personality, dest);
|
|
}
|
|
|
|
void EvolutionRenameMon(struct Pokemon *mon, u16 oldSpecies, u16 newSpecies)
|
|
{
|
|
u8 language;
|
|
GetMonData(mon, MON_DATA_NICKNAME, gStringVar1);
|
|
language = GetMonData(mon, MON_DATA_LANGUAGE, &language);
|
|
if (language == GAME_LANGUAGE && !StringCompare(gSpeciesInfo[oldSpecies].speciesName, gStringVar1))
|
|
SetMonData(mon, MON_DATA_NICKNAME, gSpeciesInfo[newSpecies].speciesName);
|
|
}
|
|
|
|
// The below two functions determine which side of a multi battle the trainer battles on
|
|
// 0 is the left (top in party menu), 1 is right (bottom in party menu)
|
|
u8 GetPlayerFlankId(void)
|
|
{
|
|
u8 flankId = 0;
|
|
switch (gLinkPlayers[GetMultiplayerId()].id)
|
|
{
|
|
case 0:
|
|
case 3:
|
|
flankId = 0;
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
flankId = 1;
|
|
break;
|
|
}
|
|
return flankId;
|
|
}
|
|
|
|
u16 GetLinkTrainerFlankId(u8 linkPlayerId)
|
|
{
|
|
u16 flankId = 0;
|
|
switch (gLinkPlayers[linkPlayerId].id)
|
|
{
|
|
case 0:
|
|
case 3:
|
|
flankId = 0;
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
flankId = 1;
|
|
break;
|
|
}
|
|
return flankId;
|
|
}
|
|
|
|
s32 GetBattlerMultiplayerId(u16 id)
|
|
{
|
|
s32 multiplayerId;
|
|
for (multiplayerId = 0; multiplayerId < MAX_LINK_PLAYERS; multiplayerId++)
|
|
if (gLinkPlayers[multiplayerId].id == id)
|
|
break;
|
|
return multiplayerId;
|
|
}
|
|
|
|
u8 GetTrainerEncounterMusicId(u16 trainerId)
|
|
{
|
|
return TRAINER_ENCOUNTER_MUSIC(trainerId);
|
|
}
|
|
|
|
static u16 ModifyStatByNature(u8 nature, u16 stat, u8 statIndex)
|
|
{
|
|
// Because this is a u16 it will be unable to store the
|
|
// result of the multiplication for any stat > 595 for a
|
|
// positive nature and > 728 for a negative nature.
|
|
// Neither occur in the base game, but this can happen if
|
|
// any Nature-affected base stat is increased to a value
|
|
// above 248. The closest by default is Shuckle at 230.
|
|
#ifdef BUGFIX
|
|
u32 retVal;
|
|
#else
|
|
u16 retVal;
|
|
#endif
|
|
|
|
// Don't modify HP, Accuracy, or Evasion by nature
|
|
if (statIndex <= STAT_HP || statIndex > NUM_NATURE_STATS)
|
|
return stat;
|
|
|
|
switch (sNatureStatTable[nature][statIndex - 1])
|
|
{
|
|
case 1:
|
|
retVal = stat * 110;
|
|
retVal /= 100;
|
|
break;
|
|
case -1:
|
|
retVal = stat * 90;
|
|
retVal /= 100;
|
|
break;
|
|
default:
|
|
retVal = stat;
|
|
break;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
void AdjustFriendship(struct Pokemon *mon, u8 event)
|
|
{
|
|
u16 species = GetMonData(mon, MON_DATA_SPECIES_OR_EGG, NULL);
|
|
u16 heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, NULL);
|
|
u8 holdEffect;
|
|
|
|
if (heldItem == ITEM_ENIGMA_BERRY)
|
|
{
|
|
if (gMain.inBattle)
|
|
holdEffect = gEnigmaBerries[0].holdEffect;
|
|
else
|
|
holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
|
|
}
|
|
else
|
|
{
|
|
holdEffect = ItemId_GetHoldEffect(heldItem);
|
|
}
|
|
|
|
if (species && species != SPECIES_EGG)
|
|
{
|
|
s8 delta;
|
|
// Friendship level refers to the column in sFriendshipEventDeltas.
|
|
// 0-99: Level 0 (maximum increase, typically)
|
|
// 100-199: Level 1
|
|
// 200-255: Level 2
|
|
u8 friendshipLevel = 0;
|
|
s16 friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL);
|
|
if (friendship >= 100)
|
|
friendshipLevel++;
|
|
if (friendship >= 200)
|
|
friendshipLevel++;
|
|
|
|
if (event == FRIENDSHIP_EVENT_WALKING)
|
|
{
|
|
// 50% chance every 128 steps
|
|
if (Random() & 1)
|
|
return;
|
|
}
|
|
if (event == FRIENDSHIP_EVENT_LEAGUE_BATTLE)
|
|
{
|
|
// Only if it's a trainer battle with league progression significance
|
|
if (!(gBattleTypeFlags & BATTLE_TYPE_TRAINER))
|
|
return;
|
|
if (!(gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_LEADER
|
|
|| gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_ELITE_FOUR
|
|
|| gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_CHAMPION))
|
|
return;
|
|
}
|
|
|
|
delta = sFriendshipEventDeltas[event][friendshipLevel];
|
|
if (delta > 0 && holdEffect == HOLD_EFFECT_FRIENDSHIP_UP)
|
|
// 50% increase, rounding down
|
|
delta = (150 * delta) / 100;
|
|
|
|
friendship += delta;
|
|
if (delta > 0)
|
|
{
|
|
if (GetMonData(mon, MON_DATA_POKEBALL, NULL) == ITEM_LUXURY_BALL)
|
|
friendship++;
|
|
if (GetMonData(mon, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId())
|
|
friendship++;
|
|
}
|
|
|
|
if (friendship < 0)
|
|
friendship = 0;
|
|
if (friendship > MAX_FRIENDSHIP)
|
|
friendship = MAX_FRIENDSHIP;
|
|
|
|
SetMonData(mon, MON_DATA_FRIENDSHIP, &friendship);
|
|
}
|
|
}
|
|
|
|
void MonGainEVs(struct Pokemon *mon, u16 defeatedSpecies)
|
|
{
|
|
u8 evs[NUM_STATS];
|
|
u16 evIncrease = 0;
|
|
u16 totalEVs = 0;
|
|
u16 heldItem;
|
|
u8 holdEffect;
|
|
int i;
|
|
|
|
for (i = 0; i < NUM_STATS; i++)
|
|
{
|
|
evs[i] = GetMonData(mon, MON_DATA_HP_EV + i, NULL);
|
|
totalEVs += evs[i];
|
|
}
|
|
|
|
for (i = 0; i < NUM_STATS; i++)
|
|
{
|
|
u8 hasHadPokerus;
|
|
int multiplier;
|
|
|
|
if (totalEVs >= MAX_TOTAL_EVS)
|
|
break;
|
|
|
|
hasHadPokerus = CheckPartyHasHadPokerus(mon, 0);
|
|
|
|
if (hasHadPokerus)
|
|
multiplier = 2;
|
|
else
|
|
multiplier = 1;
|
|
|
|
switch (i)
|
|
{
|
|
case STAT_HP:
|
|
evIncrease = gSpeciesInfo[defeatedSpecies].evYield_HP * multiplier;
|
|
break;
|
|
case STAT_ATK:
|
|
evIncrease = gSpeciesInfo[defeatedSpecies].evYield_Attack * multiplier;
|
|
break;
|
|
case STAT_DEF:
|
|
evIncrease = gSpeciesInfo[defeatedSpecies].evYield_Defense * multiplier;
|
|
break;
|
|
case STAT_SPEED:
|
|
evIncrease = gSpeciesInfo[defeatedSpecies].evYield_Speed * multiplier;
|
|
break;
|
|
case STAT_SPATK:
|
|
evIncrease = gSpeciesInfo[defeatedSpecies].evYield_SpAttack * multiplier;
|
|
break;
|
|
case STAT_SPDEF:
|
|
evIncrease = gSpeciesInfo[defeatedSpecies].evYield_SpDefense * multiplier;
|
|
break;
|
|
}
|
|
|
|
heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, NULL);
|
|
|
|
if (heldItem == ITEM_ENIGMA_BERRY)
|
|
{
|
|
if (gMain.inBattle)
|
|
holdEffect = gEnigmaBerries[0].holdEffect;
|
|
else
|
|
holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
|
|
}
|
|
else
|
|
{
|
|
holdEffect = ItemId_GetHoldEffect(heldItem);
|
|
}
|
|
|
|
if (holdEffect == HOLD_EFFECT_MACHO_BRACE)
|
|
evIncrease *= 2;
|
|
|
|
if (totalEVs + (s16)evIncrease > MAX_TOTAL_EVS)
|
|
evIncrease = ((s16)evIncrease + MAX_TOTAL_EVS) - (totalEVs + evIncrease);
|
|
|
|
if (evs[i] + (s16)evIncrease > MAX_PER_STAT_EVS)
|
|
{
|
|
int val1 = (s16)evIncrease + MAX_PER_STAT_EVS;
|
|
int val2 = evs[i] + evIncrease;
|
|
evIncrease = val1 - val2;
|
|
}
|
|
|
|
evs[i] += evIncrease;
|
|
totalEVs += evIncrease;
|
|
SetMonData(mon, MON_DATA_HP_EV + i, &evs[i]);
|
|
}
|
|
}
|
|
|
|
u16 GetMonEVCount(struct Pokemon *mon)
|
|
{
|
|
int i;
|
|
u16 count = 0;
|
|
|
|
for (i = 0; i < NUM_STATS; i++)
|
|
count += GetMonData(mon, MON_DATA_HP_EV + i, NULL);
|
|
|
|
return count;
|
|
}
|
|
|
|
// This function was stubbed from RS, but it is stubbed badly.
|
|
// This variable is likely the u8 passed to SetMonData in RSE.
|
|
// The pointer reference causes agbcc to reserve it on the stack before even checking
|
|
// whether it's used.
|
|
void RandomlyGivePartyPokerus(struct Pokemon *party)
|
|
{
|
|
u8 foo;
|
|
&foo;
|
|
}
|
|
|
|
u8 CheckPartyPokerus(struct Pokemon *party, u8 selection)
|
|
{
|
|
u8 retVal;
|
|
|
|
int partyIndex = 0;
|
|
unsigned curBit = 1;
|
|
retVal = 0;
|
|
|
|
if (selection)
|
|
{
|
|
do
|
|
{
|
|
if ((selection & 1) && (GetMonData(&party[partyIndex], MON_DATA_POKERUS, NULL) & 0xF))
|
|
retVal |= curBit;
|
|
partyIndex++;
|
|
curBit <<= 1;
|
|
selection >>= 1;
|
|
}
|
|
while (selection);
|
|
}
|
|
else if (GetMonData(&party[0], MON_DATA_POKERUS, NULL) & 0xF)
|
|
{
|
|
retVal = 1;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
u8 CheckPartyHasHadPokerus(struct Pokemon *party, u8 selection)
|
|
{
|
|
u8 retVal;
|
|
|
|
int partyIndex = 0;
|
|
unsigned curBit = 1;
|
|
retVal = 0;
|
|
|
|
if (selection)
|
|
{
|
|
do
|
|
{
|
|
if ((selection & 1) && GetMonData(&party[partyIndex], MON_DATA_POKERUS, NULL))
|
|
retVal |= curBit;
|
|
partyIndex++;
|
|
curBit <<= 1;
|
|
selection >>= 1;
|
|
}
|
|
while (selection);
|
|
}
|
|
else if (GetMonData(&party[0], MON_DATA_POKERUS, NULL))
|
|
{
|
|
retVal = 1;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
// These two functions are stubbed from RS, but they're stubbed badly.
|
|
// See note on RandomlyGivePartyPokerus above.
|
|
static void UpdatePartyPokerusTime(void)
|
|
{
|
|
u8 foo;
|
|
&foo;
|
|
}
|
|
|
|
void PartySpreadPokerus(struct Pokemon *party)
|
|
{
|
|
u8 foo;
|
|
&foo;
|
|
}
|
|
|
|
static void SetMonExpWithMaxLevelCheck(struct Pokemon *mon, int species, u8 unused, u32 data)
|
|
{
|
|
if (data > gExperienceTables[gSpeciesInfo[species].growthRate][MAX_LEVEL])
|
|
{
|
|
data = gExperienceTables[gSpeciesInfo[species].growthRate][MAX_LEVEL];
|
|
SetMonData(mon, MON_DATA_EXP, &data);
|
|
}
|
|
}
|
|
|
|
bool8 TryIncrementMonLevel(struct Pokemon *mon)
|
|
{
|
|
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
u8 level = GetMonData(mon, MON_DATA_LEVEL, NULL);
|
|
u8 newLevel = level + 1;
|
|
u32 exp = GetMonData(mon, MON_DATA_EXP, NULL);
|
|
|
|
if (level < MAX_LEVEL)
|
|
{
|
|
if (exp > gExperienceTables[gSpeciesInfo[species].growthRate][newLevel])
|
|
{
|
|
SetMonData(mon, MON_DATA_LEVEL, &newLevel);
|
|
SetMonExpWithMaxLevelCheck(mon, species, newLevel, exp);
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
SetMonExpWithMaxLevelCheck(mon, species, level, exp);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
u32 CanMonLearnTMHM(struct Pokemon *mon, u8 tm)
|
|
{
|
|
u16 i;
|
|
u16 species = GetMonData(mon, MON_DATA_SPECIES_OR_EGG, NULL);
|
|
if (species == SPECIES_EGG)
|
|
{
|
|
return 0;
|
|
}
|
|
i = 0;
|
|
while (gSpeciesInfo[species].teachableLearnset[i] != MOVE_UNAVAILABLE)
|
|
{
|
|
if (gTMHMMoves[tm] == gSpeciesInfo[species].teachableLearnset[i])
|
|
{
|
|
return TRUE;
|
|
}
|
|
i++;
|
|
}
|
|
return FALSE;
|
|
// else if (tm < 32)
|
|
// {
|
|
// u32 mask = 1 << tm;
|
|
// return sTMHMLearnsets[species][0] & mask;
|
|
// }
|
|
// else
|
|
// {
|
|
// u32 mask = 1 << (tm - 32);
|
|
// return sTMHMLearnsets[species][1] & mask;
|
|
// }
|
|
}
|
|
|
|
u8 GetMoveRelearnerMoves(struct Pokemon *mon, u16 *moves)
|
|
{
|
|
u16 learnedMoves[MAX_MON_MOVES];
|
|
u8 numMoves = 0;
|
|
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
u8 level = GetMonData(mon, MON_DATA_LEVEL, NULL);
|
|
int i, j, k;
|
|
|
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
learnedMoves[i] = GetMonData(mon, MON_DATA_MOVE1 + i, NULL);
|
|
|
|
for (i = 0; i < MAX_LEVEL_UP_MOVES; i++)
|
|
{
|
|
u16 moveLevel;
|
|
|
|
if (gSpeciesInfo[species].levelUpLearnset[i].move == LEVEL_UP_MOVE_END)
|
|
break;
|
|
|
|
moveLevel = gSpeciesInfo[species].levelUpLearnset[i].level;
|
|
|
|
if (moveLevel <= level)
|
|
{
|
|
for (j = 0; j < MAX_MON_MOVES && learnedMoves[j] != (gSpeciesInfo[species].levelUpLearnset[i].move); j++)
|
|
;
|
|
|
|
if (j == MAX_MON_MOVES)
|
|
{
|
|
for (k = 0; k < numMoves && moves[k] != (gSpeciesInfo[species].levelUpLearnset[i].move); k++)
|
|
;
|
|
|
|
if (k == numMoves)
|
|
moves[numMoves++] = gSpeciesInfo[species].levelUpLearnset[i].move;
|
|
}
|
|
}
|
|
}
|
|
|
|
return numMoves;
|
|
}
|
|
|
|
u8 GetLevelUpMovesBySpecies(u16 species, u16 *moves)
|
|
{
|
|
u8 numMoves = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_LEVEL_UP_MOVES && gSpeciesInfo[species].levelUpLearnset[i].move != LEVEL_UP_MOVE_END; i++)
|
|
moves[numMoves++] = gSpeciesInfo[species].levelUpLearnset[i].move;
|
|
|
|
return numMoves;
|
|
}
|
|
|
|
u8 GetNumberOfRelearnableMoves(struct Pokemon *mon)
|
|
{
|
|
u16 learnedMoves[MAX_MON_MOVES];
|
|
u16 moves[MAX_LEVEL_UP_MOVES];
|
|
u8 numMoves = 0;
|
|
u16 species = GetMonData(mon, MON_DATA_SPECIES_OR_EGG, NULL);
|
|
u8 level = GetMonData(mon, MON_DATA_LEVEL, NULL);
|
|
int i, j, k;
|
|
|
|
if (species == SPECIES_EGG)
|
|
return 0;
|
|
|
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
learnedMoves[i] = GetMonData(mon, MON_DATA_MOVE1 + i, NULL);
|
|
|
|
for (i = 0; i < MAX_LEVEL_UP_MOVES; i++)
|
|
{
|
|
u16 moveLevel;
|
|
|
|
if (gSpeciesInfo[species].levelUpLearnset[i].move == LEVEL_UP_MOVE_END)
|
|
break;
|
|
|
|
moveLevel = gSpeciesInfo[species].levelUpLearnset[i].level;
|
|
|
|
if (moveLevel <= level)
|
|
{
|
|
for (j = 0; j < MAX_MON_MOVES && learnedMoves[j] != (gSpeciesInfo[species].levelUpLearnset[i].move); j++)
|
|
;
|
|
|
|
if (j == MAX_MON_MOVES)
|
|
{
|
|
for (k = 0; k < numMoves && moves[k] != (gSpeciesInfo[species].levelUpLearnset[i].move); k++)
|
|
;
|
|
|
|
if (k == numMoves)
|
|
moves[numMoves++] = gSpeciesInfo[species].levelUpLearnset[i].move;
|
|
}
|
|
}
|
|
}
|
|
|
|
return numMoves;
|
|
}
|
|
|
|
u16 SpeciesToPokedexNum(u16 species)
|
|
{
|
|
species = SpeciesToNationalPokedexNum(species);
|
|
if (!IsNationalPokedexEnabled())
|
|
{
|
|
species = NationalToKantoOrder(species);
|
|
}
|
|
return species > 0 ? species : 0xFFFF;
|
|
}
|
|
|
|
void ClearBattleMonForms(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
|
|
gBattleMonForms[i] = 0;
|
|
}
|
|
|
|
static u16 GetBattleBGM(void)
|
|
{
|
|
if (gBattleTypeFlags & BATTLE_TYPE_KYOGRE_GROUDON)
|
|
return MUS_VS_WILD;
|
|
if (gBattleTypeFlags & BATTLE_TYPE_REGI)
|
|
return MUS_RS_VS_TRAINER;
|
|
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
|
|
return MUS_RS_VS_TRAINER;
|
|
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
|
|
{
|
|
switch (gTrainers[gTrainerBattleOpponent_A].trainerClass)
|
|
{
|
|
case TRAINER_CLASS_CHAMPION:
|
|
return MUS_VS_CHAMPION;
|
|
case TRAINER_CLASS_LEADER:
|
|
case TRAINER_CLASS_ELITE_FOUR:
|
|
return MUS_VS_GYM_LEADER;
|
|
case TRAINER_CLASS_BOSS:
|
|
case TRAINER_CLASS_TEAM_ROCKET:
|
|
case TRAINER_CLASS_COOLTRAINER:
|
|
case TRAINER_CLASS_GENTLEMAN:
|
|
case TRAINER_CLASS_RIVAL_LATE:
|
|
default:
|
|
return MUS_VS_TRAINER;
|
|
}
|
|
}
|
|
return MUS_VS_WILD;
|
|
}
|
|
|
|
void PlayBattleBGM(void)
|
|
{
|
|
ResetMapMusic();
|
|
m4aMPlayAllStop();
|
|
PlayBGM(GetBattleBGM());
|
|
}
|
|
|
|
void PlayMapChosenOrBattleBGM(u16 songId)
|
|
{
|
|
ResetMapMusic();
|
|
m4aMPlayAllStop();
|
|
if (songId)
|
|
PlayNewMapMusic(songId);
|
|
else
|
|
PlayNewMapMusic(GetBattleBGM());
|
|
}
|
|
|
|
const u32 *GetMonFrontSpritePal(struct Pokemon *mon)
|
|
{
|
|
u16 species = GetMonData(mon, MON_DATA_SPECIES_OR_EGG, NULL);
|
|
u32 otId = GetMonData(mon, MON_DATA_OT_ID, NULL);
|
|
u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, NULL);
|
|
return GetMonSpritePalFromSpeciesAndPersonality(species, otId, personality);
|
|
}
|
|
|
|
const u32 *GetMonSpritePalFromSpeciesAndPersonality(u16 species, u32 otId, u32 personality)
|
|
{
|
|
u32 shinyValue;
|
|
|
|
if (species > SPECIES_EGG)
|
|
return gSpeciesInfo[SPECIES_NONE].palette;
|
|
|
|
shinyValue = GET_SHINY_VALUE(otId, personality);
|
|
if (shinyValue < SHINY_ODDS)
|
|
return gSpeciesInfo[species].shinyPalette;
|
|
else
|
|
return gSpeciesInfo[species].palette;
|
|
}
|
|
|
|
const u32 *GetMonSpritePalStruct(struct Pokemon *mon)
|
|
{
|
|
u16 species = GetMonData(mon, MON_DATA_SPECIES_OR_EGG, NULL);
|
|
u32 otId = GetMonData(mon, MON_DATA_OT_ID, NULL);
|
|
u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, NULL);
|
|
return GetMonSpritePalStructFromOtIdPersonality(species, otId, personality);
|
|
}
|
|
|
|
const u32 *GetMonSpritePalStructFromOtIdPersonality(u16 species, u32 otId , u32 personality)
|
|
{
|
|
u32 shinyValue;
|
|
|
|
species = SanitizeSpeciesId(species);
|
|
|
|
shinyValue = GET_SHINY_VALUE(otId, personality);
|
|
if (shinyValue < SHINY_ODDS)
|
|
{
|
|
if (gSpeciesInfo[species].shinyPalette != NULL)
|
|
{
|
|
return gSpeciesInfo[species].shinyPalette;
|
|
}
|
|
else
|
|
{
|
|
return gSpeciesInfo[SPECIES_NONE].shinyPalette;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (gSpeciesInfo[species].palette != NULL)
|
|
{
|
|
return gSpeciesInfo[species].palette;
|
|
}
|
|
else
|
|
{
|
|
return gSpeciesInfo[SPECIES_NONE].palette;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
bool32 IsMoveHM(u16 move)
|
|
{
|
|
int i = 0;
|
|
while (sHMMoves[i] != HM_MOVES_END)
|
|
{
|
|
if (sHMMoves[i++] == move)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
// u8 i;
|
|
|
|
// for (i = 0; i < NUM_HIDDEN_MACHINES - 1; ++i) // no dive
|
|
// if (gTMHMMoves[i + NUM_TECHNICAL_MACHINES] == move)
|
|
// return TRUE;
|
|
// return FALSE;
|
|
}
|
|
|
|
bool8 IsMonSpriteNotFlipped(u16 species)
|
|
{
|
|
return gSpeciesInfo[species].noFlip;
|
|
}
|
|
|
|
static s8 GetMonFlavorRelation(struct Pokemon *mon, u8 flavor)
|
|
{
|
|
u8 nature = GetNature(mon);
|
|
return sPokeblockFlavorCompatibilityTable[nature * FLAVOR_COUNT + flavor];
|
|
}
|
|
|
|
s8 GetFlavorRelationByPersonality(u32 personality, u8 flavor)
|
|
{
|
|
u8 nature = GetNatureFromPersonality(personality);
|
|
return sPokeblockFlavorCompatibilityTable[nature * FLAVOR_COUNT + flavor];
|
|
}
|
|
|
|
bool8 IsTradedMon(struct Pokemon *mon)
|
|
{
|
|
u8 otName[PLAYER_NAME_LENGTH + 1];
|
|
u32 otId;
|
|
GetMonData(mon, MON_DATA_OT_NAME, otName);
|
|
otId = GetMonData(mon, MON_DATA_OT_ID, NULL);
|
|
return IsOtherTrainer(otId, otName);
|
|
}
|
|
|
|
bool8 IsOtherTrainer(u32 otId, u8 *otName)
|
|
{
|
|
if (otId ==
|
|
(gSaveBlock2Ptr->playerTrainerId[0]
|
|
| (gSaveBlock2Ptr->playerTrainerId[1] << 8)
|
|
| (gSaveBlock2Ptr->playerTrainerId[2] << 16)
|
|
| (gSaveBlock2Ptr->playerTrainerId[3] << 24)))
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; otName[i] != EOS; i++)
|
|
if (otName[i] != gSaveBlock2Ptr->playerName[i])
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void MonRestorePP(struct Pokemon *mon)
|
|
{
|
|
BoxMonRestorePP(&mon->box);
|
|
}
|
|
|
|
void BoxMonRestorePP(struct BoxPokemon *boxMon)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
{
|
|
if (GetBoxMonData(boxMon, MON_DATA_MOVE1 + i, 0))
|
|
{
|
|
u16 move = GetBoxMonData(boxMon, MON_DATA_MOVE1 + i, 0);
|
|
u16 bonus = GetBoxMonData(boxMon, MON_DATA_PP_BONUSES, 0);
|
|
u8 pp = CalculatePPWithBonus(move, bonus, i);
|
|
SetBoxMonData(boxMon, MON_DATA_PP1 + i, &pp);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SetMonPreventsSwitchingString(void)
|
|
{
|
|
gLastUsedAbility = gBattleStruct -> abilityPreventingSwitchout;
|
|
gBattleTextBuff1[0] = B_BUFF_PLACEHOLDER_BEGIN;
|
|
gBattleTextBuff1[1] = B_BUFF_MON_NICK_WITH_PREFIX;
|
|
gBattleTextBuff1[2] = gBattleStruct->battlerPreventingSwitchout;
|
|
gBattleTextBuff1[4] = B_BUFF_EOS;
|
|
|
|
if (GetBattlerSide(gBattleStruct->battlerPreventingSwitchout) == B_SIDE_PLAYER)
|
|
gBattleTextBuff1[3] = GetPartyIdFromBattlePartyId(gBattlerPartyIndexes[gBattleStruct->battlerPreventingSwitchout]);
|
|
else
|
|
gBattleTextBuff1[3] = gBattlerPartyIndexes[gBattleStruct->battlerPreventingSwitchout];
|
|
|
|
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff2, gBattlerInMenuId, GetPartyIdFromBattlePartyId(gBattlerPartyIndexes[gBattlerInMenuId]))
|
|
|
|
BattleStringExpandPlaceholders(gText_PkmnsXPreventsSwitching, gStringVar4);
|
|
}
|
|
|
|
void SetWildMonHeldItem(void)
|
|
{
|
|
if (!(gBattleTypeFlags & (BATTLE_TYPE_POKEDUDE | BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_TRAINER)))
|
|
{
|
|
u16 rnd = Random() % 100;
|
|
u16 species = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES, NULL);
|
|
if (gSpeciesInfo[species].itemCommon == gSpeciesInfo[species].itemRare)
|
|
{
|
|
// Both held items are the same, 100% chance to hold item
|
|
SetMonData(&gEnemyParty[0], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemCommon);
|
|
return;
|
|
}
|
|
|
|
if (rnd > 44)
|
|
{
|
|
if (rnd <= 94)
|
|
SetMonData(&gEnemyParty[0], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemCommon);
|
|
else
|
|
SetMonData(&gEnemyParty[0], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemRare);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool8 IsMonShiny(struct Pokemon *mon)
|
|
{
|
|
u32 otId = GetMonData(mon, MON_DATA_OT_ID, NULL);
|
|
u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, NULL);
|
|
return IsShinyOtIdPersonality(otId, personality);
|
|
}
|
|
|
|
static bool8 IsShinyOtIdPersonality(u32 otId, u32 personality)
|
|
{
|
|
bool8 retVal = FALSE;
|
|
u32 shinyValue = GET_SHINY_VALUE(otId, personality);
|
|
if (shinyValue < SHINY_ODDS)
|
|
retVal = TRUE;
|
|
return retVal;
|
|
}
|
|
|
|
u8 *GetTrainerPartnerName(void)
|
|
{
|
|
u8 id = GetMultiplayerId();
|
|
return gLinkPlayers[GetBattlerMultiplayerId(gLinkPlayers[id].id ^ 2)].name;
|
|
}
|
|
|
|
u8 GetPlayerPartyHighestLevel(void)
|
|
{
|
|
s32 slot;
|
|
u8 level, monLevel;
|
|
|
|
level = 1;
|
|
for (slot = 0; slot < PARTY_SIZE; ++slot)
|
|
{
|
|
if (GetMonData(&gPlayerParty[slot], MON_DATA_SANITY_HAS_SPECIES, NULL) == 1 && !GetMonData(&gPlayerParty[slot], MON_DATA_SANITY_IS_EGG, NULL))
|
|
{
|
|
monLevel = GetMonData(&gPlayerParty[slot], MON_DATA_LEVEL, NULL);
|
|
if (monLevel > level)
|
|
level = monLevel;
|
|
}
|
|
}
|
|
return level;
|
|
}
|
|
|
|
u16 FacilityClassToPicIndex(u16 facilityClass)
|
|
{
|
|
return gFacilityClassToPicIndex[facilityClass];
|
|
}
|
|
|
|
// If FALSE, should load this game's Deoxys form. If TRUE, should load normal Deoxys form
|
|
bool8 ShouldIgnoreDeoxysForm(u8 caseId, u8 battlerId)
|
|
{
|
|
switch (caseId)
|
|
{
|
|
case 0:
|
|
default:
|
|
return FALSE;
|
|
case DEOXYS_CHECK_BATTLE_SPRITE:
|
|
if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI))
|
|
return FALSE;
|
|
if (!gMain.inBattle)
|
|
return FALSE;
|
|
if (gLinkPlayers[GetMultiplayerId()].id == battlerId)
|
|
return FALSE;
|
|
break;
|
|
case 2:
|
|
break;
|
|
case DEOXYS_CHECK_TRADE_MAIN:
|
|
if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI))
|
|
return FALSE;
|
|
if (!gMain.inBattle)
|
|
return FALSE;
|
|
if (battlerId == 1 || battlerId == 4 || battlerId == 5)
|
|
return TRUE;
|
|
return FALSE;
|
|
case 4:
|
|
break;
|
|
case DEOXYS_CHECK_BATTLE_ANIM:
|
|
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
|
|
{
|
|
if (!gMain.inBattle)
|
|
return FALSE;
|
|
if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
|
|
{
|
|
if (gLinkPlayers[GetMultiplayerId()].id == battlerId)
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!gMain.inBattle)
|
|
return FALSE;
|
|
if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static u16 GetDeoxysStat(struct Pokemon *mon, s32 statId)
|
|
{
|
|
s32 ivVal, evVal;
|
|
u16 statValue = 0;
|
|
u8 nature;
|
|
|
|
if (gBattleTypeFlags & BATTLE_TYPE_LINK_IN_BATTLE || GetMonData(mon, MON_DATA_SPECIES, NULL) != SPECIES_DEOXYS)
|
|
return 0;
|
|
|
|
ivVal = GetMonData(mon, MON_DATA_HP_IV + statId, NULL);
|
|
evVal = GetMonData(mon, MON_DATA_HP_EV + statId, NULL);
|
|
statValue = ((sDeoxysBaseStats[statId] * 2 + ivVal + evVal / 4) * mon->level) / 100 + 5;
|
|
nature = GetNature(mon);
|
|
statValue = ModifyStatByNature(nature, statValue, (u8)statId);
|
|
return statValue;
|
|
}
|
|
|
|
void SetDeoxysStats(void)
|
|
{
|
|
s32 i, value;
|
|
|
|
for (i = 0; i < PARTY_SIZE; i++)
|
|
{
|
|
struct Pokemon *mon = &gPlayerParty[i];
|
|
|
|
if (GetMonData(mon, MON_DATA_SPECIES, NULL) != SPECIES_DEOXYS)
|
|
continue;
|
|
value = GetMonData(mon, MON_DATA_ATK, NULL);
|
|
SetMonData(mon, MON_DATA_ATK, &value);
|
|
value = GetMonData(mon, MON_DATA_DEF, NULL);
|
|
SetMonData(mon, MON_DATA_DEF, &value);
|
|
value = GetMonData(mon, MON_DATA_SPEED, NULL);
|
|
SetMonData(mon, MON_DATA_SPEED, &value);
|
|
value = GetMonData(mon, MON_DATA_SPATK, NULL);
|
|
SetMonData(mon, MON_DATA_SPATK, &value);
|
|
value = GetMonData(mon, MON_DATA_SPDEF, NULL);
|
|
SetMonData(mon, MON_DATA_SPDEF, &value);
|
|
}
|
|
}
|
|
|
|
u16 GetUnionRoomTrainerPic(void)
|
|
{
|
|
u8 linkId = GetMultiplayerId() ^ 1;
|
|
|
|
u32 arrId = gLinkPlayers[linkId].trainerId % NUM_UNION_ROOM_CLASSES;
|
|
arrId |= gLinkPlayers[linkId].gender * NUM_UNION_ROOM_CLASSES;
|
|
return FacilityClassToPicIndex(gUnionRoomFacilityClasses[arrId]);
|
|
}
|
|
|
|
u16 GetUnionRoomTrainerClass(void)
|
|
{
|
|
u8 linkId = GetMultiplayerId() ^ 1;
|
|
|
|
u32 arrId = gLinkPlayers[linkId].trainerId % NUM_UNION_ROOM_CLASSES;
|
|
arrId |= gLinkPlayers[linkId].gender * NUM_UNION_ROOM_CLASSES;
|
|
return gFacilityClassToTrainerClass[gUnionRoomFacilityClasses[arrId]];
|
|
}
|
|
|
|
void CreateEnemyEventMon(void)
|
|
{
|
|
s32 species = gSpecialVar_0x8004;
|
|
s32 level = gSpecialVar_0x8005;
|
|
s32 itemId = gSpecialVar_0x8006;
|
|
|
|
ZeroEnemyPartyMons();
|
|
CreateEventMon(&gEnemyParty[0], species, level, USE_RANDOM_IVS, FALSE, 0, OT_ID_PLAYER_ID, 0);
|
|
if (itemId)
|
|
{
|
|
u8 heldItem[2];
|
|
|
|
heldItem[0] = itemId;
|
|
heldItem[1] = itemId >> 8;
|
|
SetMonData(&gEnemyParty[0], MON_DATA_HELD_ITEM, heldItem);
|
|
}
|
|
}
|
|
|
|
void HandleSetPokedexFlag(u16 nationalNum, u8 caseId, u32 personality)
|
|
{
|
|
u8 getFlagCaseId = (caseId == FLAG_SET_SEEN) ? FLAG_GET_SEEN : FLAG_GET_CAUGHT;
|
|
|
|
if (!GetSetPokedexFlag(nationalNum, getFlagCaseId))
|
|
{
|
|
GetSetPokedexFlag(nationalNum, caseId);
|
|
if (NationalPokedexNumToSpecies(nationalNum) == SPECIES_UNOWN)
|
|
gSaveBlock2Ptr->pokedex.unownPersonality = personality;
|
|
if (NationalPokedexNumToSpecies(nationalNum) == SPECIES_SPINDA)
|
|
gSaveBlock2Ptr->pokedex.spindaPersonality = personality;
|
|
}
|
|
}
|
|
|
|
bool8 CheckBattleTypeGhost(struct Pokemon *mon, u8 battlerId)
|
|
{
|
|
u8 nickname[POKEMON_NAME_LENGTH + 1];
|
|
|
|
if (gBattleTypeFlags & BATTLE_TYPE_GHOST && GetBattlerSide(battlerId) != B_SIDE_PLAYER)
|
|
{
|
|
GetMonData(mon, MON_DATA_NICKNAME, nickname);
|
|
StringGet_Nickname(nickname);
|
|
if (!StringCompare(nickname, gText_Ghost))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
#define ALLOC_FAIL_BUFFER (1 << 0)
|
|
#define ALLOC_FAIL_STRUCT (1 << 1)
|
|
#define GFX_MANAGER_ACTIVE 0xA3 // Arbitrary value
|
|
|
|
static void InitMonSpritesGfx_Mode1(struct MonSpritesGfxManager *structPtr, u8 battlePosition)
|
|
{
|
|
u16 i = 0, j = 0;
|
|
|
|
if (battlePosition >= MAX_BATTLERS_COUNT)
|
|
{
|
|
for (i = 0; i < (s8)structPtr->numSprites; ++i)
|
|
{
|
|
structPtr->templates[i] = gSpriteTemplates_Battlers[i];
|
|
for (j = 0; j < structPtr->numFrames; ++j)
|
|
structPtr->frameImages[i * structPtr->numFrames + j].data = &structPtr->spritePointers[i][j * MON_PIC_SIZE];
|
|
structPtr->templates[i].images = &structPtr->frameImages[i * structPtr->numFrames];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const struct SpriteTemplate *template = &gSpriteTemplates_Battlers[battlePosition];
|
|
|
|
structPtr->templates[0] = *template;
|
|
for (j = 0; j < structPtr->numFrames; ++j)
|
|
structPtr->frameImages[j].data = &structPtr->spritePointers[0][j * MON_PIC_SIZE];
|
|
structPtr->templates[0].images = structPtr->frameImages;
|
|
}
|
|
}
|
|
|
|
// not used
|
|
static void InitMonSpritesGfx_Mode0(struct MonSpritesGfxManager *structPtr)
|
|
{
|
|
u16 i, j;
|
|
|
|
for (i = 0; i < (s8)structPtr->numSprites; ++i)
|
|
{
|
|
structPtr->templates[i] = sSpriteTemplate_64x64;
|
|
for (j = 0; j < structPtr->numFrames; ++j)
|
|
structPtr->frameImages[i * structPtr->numSprites + j].data = &structPtr->spritePointers[i][j * MON_PIC_SIZE];
|
|
structPtr->templates[i].images = &structPtr->frameImages[i * structPtr->numSprites]; // should be numFrames logically
|
|
structPtr->templates[i].anims = gAnims_MonPic;
|
|
structPtr->templates[i].paletteTag = i;
|
|
}
|
|
}
|
|
|
|
struct MonSpritesGfxManager *CreateMonSpritesGfxManager(u8 battlePosition, u8 mode)
|
|
{
|
|
s32 size;
|
|
u8 i, failureFlags = 0;
|
|
|
|
if (sMonSpritesGfxManager != NULL)
|
|
{
|
|
if (sMonSpritesGfxManager->active == GFX_MANAGER_ACTIVE)
|
|
return NULL;
|
|
memset(sMonSpritesGfxManager, 0, sizeof(*sMonSpritesGfxManager));
|
|
sMonSpritesGfxManager = NULL;
|
|
}
|
|
sMonSpritesGfxManager = AllocZeroed(sizeof(*sMonSpritesGfxManager));
|
|
if (sMonSpritesGfxManager == NULL)
|
|
return NULL;
|
|
|
|
switch (mode)
|
|
{
|
|
case 1:
|
|
if (battlePosition == MAX_BATTLERS_COUNT)
|
|
{
|
|
sMonSpritesGfxManager->numSprites = MAX_BATTLERS_COUNT;
|
|
sMonSpritesGfxManager->battlePosition = MAX_BATTLERS_COUNT;
|
|
}
|
|
else
|
|
{
|
|
if (battlePosition > MAX_BATTLERS_COUNT)
|
|
battlePosition = 0;
|
|
sMonSpritesGfxManager->numSprites = 1;
|
|
sMonSpritesGfxManager->battlePosition = 1;
|
|
}
|
|
sMonSpritesGfxManager->numFrames = MAX_MON_PIC_FRAMES;
|
|
sMonSpritesGfxManager->mode = 1;
|
|
break;
|
|
case 0:
|
|
default:
|
|
if (!battlePosition)
|
|
battlePosition = 1;
|
|
if (battlePosition > 8)
|
|
battlePosition = 8;
|
|
sMonSpritesGfxManager->numSprites = (battlePosition << 16) >> 16;
|
|
sMonSpritesGfxManager->battlePosition = battlePosition;
|
|
sMonSpritesGfxManager->numFrames = 4;
|
|
sMonSpritesGfxManager->mode = 0;
|
|
break;
|
|
}
|
|
|
|
// Set up sprite / sprite pointer buffers
|
|
size = sMonSpritesGfxManager->numFrames * MON_PIC_SIZE;
|
|
sMonSpritesGfxManager->dataSize = size;
|
|
sMonSpritesGfxManager->spriteBuffer = AllocZeroed(sMonSpritesGfxManager->numSprites * size);
|
|
sMonSpritesGfxManager->spritePointers = AllocZeroed(sMonSpritesGfxManager->numSprites * 32); // ? Only * 4 is necessary, perhaps they were thinking bits.
|
|
if (sMonSpritesGfxManager->spriteBuffer == NULL || sMonSpritesGfxManager->spritePointers == NULL)
|
|
{
|
|
failureFlags |= ALLOC_FAIL_BUFFER;
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
for (i = 0; i < (s8)sMonSpritesGfxManager->numSprites; ++i)
|
|
sMonSpritesGfxManager->spritePointers[i] = &sMonSpritesGfxManager->spriteBuffer[sMonSpritesGfxManager->dataSize * i];
|
|
} while (0);
|
|
}
|
|
|
|
// Set up sprite structs
|
|
sMonSpritesGfxManager->templates = AllocZeroed(sizeof(struct SpriteTemplate) * sMonSpritesGfxManager->numSprites);
|
|
sMonSpritesGfxManager->frameImages = AllocZeroed(sMonSpritesGfxManager->numSprites * sizeof(struct SpriteFrameImage) * sMonSpritesGfxManager->numFrames);
|
|
if (sMonSpritesGfxManager->templates == NULL || sMonSpritesGfxManager->frameImages == NULL)
|
|
{
|
|
failureFlags |= ALLOC_FAIL_STRUCT;
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < sMonSpritesGfxManager->numFrames * sMonSpritesGfxManager->numSprites; i++)
|
|
sMonSpritesGfxManager->frameImages[i].size = MON_PIC_SIZE;
|
|
switch (sMonSpritesGfxManager->mode)
|
|
{
|
|
case 1:
|
|
InitMonSpritesGfx_Mode1(sMonSpritesGfxManager, battlePosition);
|
|
break;
|
|
case 0:
|
|
default:
|
|
InitMonSpritesGfx_Mode0(sMonSpritesGfxManager);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If either of the allocations failed free their respective members
|
|
if (failureFlags & ALLOC_FAIL_STRUCT)
|
|
{
|
|
TRY_FREE_AND_SET_NULL(sMonSpritesGfxManager->frameImages);
|
|
TRY_FREE_AND_SET_NULL(sMonSpritesGfxManager->templates);
|
|
}
|
|
if (failureFlags & ALLOC_FAIL_BUFFER)
|
|
{
|
|
TRY_FREE_AND_SET_NULL(sMonSpritesGfxManager->spritePointers);
|
|
TRY_FREE_AND_SET_NULL(sMonSpritesGfxManager->spriteBuffer);
|
|
}
|
|
|
|
if (failureFlags)
|
|
{
|
|
// Clear, something failed to allocate
|
|
memset(sMonSpritesGfxManager, 0, sizeof(*sMonSpritesGfxManager));
|
|
FREE_AND_SET_NULL(sMonSpritesGfxManager);
|
|
}
|
|
else
|
|
{
|
|
sMonSpritesGfxManager->active = GFX_MANAGER_ACTIVE;
|
|
}
|
|
return sMonSpritesGfxManager;
|
|
}
|
|
|
|
void DestroyMonSpritesGfxManager(void)
|
|
{
|
|
if (sMonSpritesGfxManager != NULL)
|
|
{
|
|
if (sMonSpritesGfxManager->active != GFX_MANAGER_ACTIVE)
|
|
{
|
|
memset(sMonSpritesGfxManager, 0, sizeof(*sMonSpritesGfxManager));
|
|
sMonSpritesGfxManager = NULL;
|
|
}
|
|
else
|
|
{
|
|
TRY_FREE_AND_SET_NULL(sMonSpritesGfxManager->frameImages);
|
|
TRY_FREE_AND_SET_NULL(sMonSpritesGfxManager->templates);
|
|
TRY_FREE_AND_SET_NULL(sMonSpritesGfxManager->spritePointers);
|
|
TRY_FREE_AND_SET_NULL(sMonSpritesGfxManager->spriteBuffer);
|
|
memset(sMonSpritesGfxManager, 0, sizeof(*sMonSpritesGfxManager));
|
|
FREE_AND_SET_NULL(sMonSpritesGfxManager);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
u8 *MonSpritesGfxManager_GetSpritePtr(u8 spriteNum)
|
|
{
|
|
if (sMonSpritesGfxManager->active != GFX_MANAGER_ACTIVE)
|
|
{
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
if (spriteNum >= (s8)sMonSpritesGfxManager->numSprites)
|
|
spriteNum = 0;
|
|
return sMonSpritesGfxManager->spritePointers[spriteNum];
|
|
}
|
|
}
|
|
|
|
|
|
|
|
u16 SanitizeSpeciesId(u16 species)
|
|
{
|
|
if (species > NUM_SPECIES || !IsSpeciesEnabled(species))
|
|
return SPECIES_NONE;
|
|
else
|
|
return species;
|
|
}
|
|
|
|
bool32 IsSpeciesEnabled(u16 species)
|
|
{
|
|
return gSpeciesInfo[species].baseHP > 0 || species == SPECIES_EGG;
|
|
}
|
|
|
|
u32 GetUnownSpeciesId(u32 personality)
|
|
{
|
|
u16 unownLetter = GetUnownLetterByPersonality(personality);
|
|
|
|
if (unownLetter == 0)
|
|
return SPECIES_UNOWN;
|
|
return unownLetter + SPECIES_UNOWN_B - 1;
|
|
}
|
|
|
|
static bool8 ShouldSkipFriendshipChange(void)
|
|
{
|
|
if (gMain.inBattle && gBattleTypeFlags & (BATTLE_TYPE_BATTLE_TOWER))
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
static void RemoveIVIndexFromList(u8 *ivs, u8 selectedIv)
|
|
{
|
|
s32 i, j;
|
|
u8 temp[NUM_STATS];
|
|
|
|
ivs[selectedIv] = 0xFF;
|
|
for (i = 0; i < NUM_STATS; i++)
|
|
{
|
|
temp[i] = ivs[i];
|
|
}
|
|
|
|
j = 0;
|
|
for (i = 0; i < NUM_STATS; i++)
|
|
{
|
|
if (temp[i] != 0xFF)
|
|
ivs[j++] = temp[i];
|
|
}
|
|
}
|
|
|
|
u16 GetFormSpeciesId(u16 speciesId, u8 formId)
|
|
{
|
|
if (GetSpeciesFormTable(speciesId) != NULL)
|
|
return GetSpeciesFormTable(speciesId)[formId];
|
|
else
|
|
return speciesId;
|
|
}
|
|
|
|
u8 GetFormIdFromFormSpeciesId(u16 formSpeciesId)
|
|
{
|
|
u8 targetFormId = 0;
|
|
|
|
if (GetSpeciesFormTable(formSpeciesId) != NULL)
|
|
{
|
|
for (targetFormId = 0; GetSpeciesFormTable(formSpeciesId)[targetFormId] != FORM_SPECIES_END; targetFormId++)
|
|
{
|
|
if (formSpeciesId == GetSpeciesFormTable(formSpeciesId)[targetFormId])
|
|
break;
|
|
}
|
|
}
|
|
return targetFormId;
|
|
}
|
|
|
|
u16 GetFormChangeTargetSpecies(struct Pokemon *mon, u16 method, u32 arg)
|
|
{
|
|
return GetFormChangeTargetSpeciesBoxMon(&mon->box, method, arg);
|
|
}
|
|
|
|
// Returns SPECIES_NONE if no form change is possible
|
|
u16 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, u16 method, u32 arg)
|
|
{
|
|
u32 i;
|
|
u16 targetSpecies = SPECIES_NONE;
|
|
u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
|
|
const struct FormChange *formChanges = GetSpeciesFormChanges(species);
|
|
u16 heldItem;
|
|
u32 ability;
|
|
|
|
if (formChanges != NULL)
|
|
{
|
|
heldItem = GetBoxMonData(boxMon, MON_DATA_HELD_ITEM, NULL);
|
|
ability = GetAbilityBySpecies(species, GetBoxMonData(boxMon, MON_DATA_ABILITY_NUM, NULL));
|
|
|
|
for (i = 0; formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
|
|
{
|
|
if (method == formChanges[i].method && species != formChanges[i].targetSpecies)
|
|
{
|
|
switch (method)
|
|
{
|
|
case FORM_CHANGE_ITEM_HOLD:
|
|
if ((heldItem == formChanges[i].param1 || formChanges[i].param1 == ITEM_NONE)
|
|
&& (ability == formChanges[i].param2 || formChanges[i].param2 == ABILITY_NONE))
|
|
targetSpecies = formChanges[i].targetSpecies;
|
|
break;
|
|
case FORM_CHANGE_ITEM_USE:
|
|
if (arg == formChanges[i].param1)
|
|
{
|
|
// TODO: time
|
|
switch (formChanges[i].param2)
|
|
{
|
|
case DAY:
|
|
// if (GetTimeOfDay() != TIME_NIGHT)
|
|
// targetSpecies = formChanges[i].targetSpecies;
|
|
targetSpecies = SPECIES_NONE;
|
|
break;
|
|
case NIGHT:
|
|
// if (GetTimeOfDay() == TIME_NIGHT)
|
|
// targetSpecies = formChanges[i].targetSpecies;
|
|
targetSpecies = SPECIES_NONE;
|
|
break;
|
|
default:
|
|
targetSpecies = formChanges[i].targetSpecies;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case FORM_CHANGE_ITEM_USE_MULTICHOICE:
|
|
if (arg == formChanges[i].param1)
|
|
{
|
|
if (formChanges[i].param2 == gSpecialVar_Result)
|
|
targetSpecies = formChanges[i].targetSpecies;
|
|
}
|
|
break;
|
|
case FORM_CHANGE_MOVE:
|
|
if (BoxMonKnowsMove(boxMon, formChanges[i].param1) != formChanges[i].param2)
|
|
targetSpecies = formChanges[i].targetSpecies;
|
|
break;
|
|
case FORM_CHANGE_BEGIN_BATTLE:
|
|
case FORM_CHANGE_END_BATTLE:
|
|
if (heldItem == formChanges[i].param1 || formChanges[i].param1 == ITEM_NONE)
|
|
targetSpecies = formChanges[i].targetSpecies;
|
|
break;
|
|
case FORM_CHANGE_END_BATTLE_TERRAIN:
|
|
if (gBattleTerrain == formChanges[i].param1)
|
|
targetSpecies = formChanges[i].targetSpecies;
|
|
break;
|
|
case FORM_CHANGE_WITHDRAW:
|
|
case FORM_CHANGE_FAINT:
|
|
case FORM_CHANGE_STATUS:
|
|
targetSpecies = formChanges[i].targetSpecies;
|
|
break;
|
|
case FORM_CHANGE_TIME_OF_DAY:
|
|
// TODO: time
|
|
// switch (formChanges[i].param1)
|
|
// {
|
|
// case DAY:
|
|
// if (GetTimeOfDay() != TIME_NIGHT)
|
|
// targetSpecies = formChanges[i].targetSpecies;
|
|
// break;
|
|
// case NIGHT:
|
|
// if (GetTimeOfDay() == TIME_NIGHT)
|
|
// targetSpecies = formChanges[i].targetSpecies;
|
|
// break;
|
|
// }
|
|
// break;
|
|
targetSpecies = SPECIES_NONE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return targetSpecies;
|
|
}
|
|
|
|
bool32 DoesSpeciesHaveFormChangeMethod(u16 species, u16 method)
|
|
{
|
|
u32 i;
|
|
const struct FormChange *formChanges = GetSpeciesFormChanges(species);
|
|
|
|
if (formChanges != NULL)
|
|
{
|
|
for (i = 0; formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
|
|
{
|
|
if (method == formChanges[i].method && species != formChanges[i].targetSpecies)
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
u32 GetMonAffectionHearts(struct Pokemon *pokemon)
|
|
{
|
|
u32 friendship = GetMonData(pokemon, MON_DATA_FRIENDSHIP, NULL);
|
|
|
|
if (friendship == MAX_FRIENDSHIP)
|
|
return AFFECTION_FIVE_HEARTS;
|
|
if (friendship >= 220)
|
|
return AFFECTION_FOUR_HEARTS;
|
|
if (friendship >= 180)
|
|
return AFFECTION_THREE_HEARTS;
|
|
if (friendship >= 130)
|
|
return AFFECTION_TWO_HEARTS;
|
|
if (friendship >= 80)
|
|
return AFFECTION_ONE_HEART;
|
|
|
|
return AFFECTION_NO_HEARTS;
|
|
}
|
|
|
|
void TryToSetBattleFormChangeMoves(struct Pokemon *mon, u16 method)
|
|
{
|
|
int i, j;
|
|
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
const struct FormChange *formChanges = GetSpeciesFormChanges(species);
|
|
|
|
if (formChanges == NULL
|
|
|| (method != FORM_CHANGE_BEGIN_BATTLE && method != FORM_CHANGE_END_BATTLE))
|
|
return;
|
|
|
|
for (i = 0; formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
|
|
{
|
|
if (formChanges[i].method == method
|
|
&& formChanges[i].param2
|
|
&& formChanges[i].param3
|
|
&& formChanges[i].targetSpecies != species)
|
|
{
|
|
u16 originalMove = formChanges[i].param2;
|
|
u16 newMove = formChanges[i].param3;
|
|
|
|
for (j = 0; j < MAX_MON_MOVES; j++)
|
|
{
|
|
u16 currMove = GetMonData(mon, MON_DATA_MOVE1 + j, NULL);
|
|
if (currMove == originalMove)
|
|
SetMonMoveSlot_KeepPP(mon, newMove, j);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool8 IsMonPastEvolutionLevel(struct Pokemon *mon)
|
|
{
|
|
int i;
|
|
u16 species = GetMonData(mon, MON_DATA_SPECIES, 0);
|
|
u8 level = GetMonData(mon, MON_DATA_LEVEL, 0);
|
|
const struct Evolution *evolutions = GetSpeciesEvolutions(species);
|
|
|
|
if (evolutions == NULL)
|
|
return FALSE;
|
|
|
|
for (i = 0; evolutions[i].method != EVOLUTIONS_END; i++)
|
|
{
|
|
if (SanitizeSpeciesId(evolutions[i].targetSpecies) == SPECIES_NONE)
|
|
continue;
|
|
|
|
switch (evolutions[i].method)
|
|
{
|
|
case EVO_LEVEL:
|
|
if (evolutions[i].param <= level)
|
|
return TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
bool32 IsPersonalityFemale(u16 species, u32 personality)
|
|
{
|
|
return GetGenderFromSpeciesAndPersonality(species, personality) == MON_FEMALE;
|
|
}
|