mirror of
https://github.com/pret/pokeheartgold.git
synced 2026-06-02 21:54:45 -05:00
5047 lines
150 KiB
C
5047 lines
150 KiB
C
#include "pokemon.h"
|
|
#include "math_util.h"
|
|
#include "seal_case.h"
|
|
#include "move.h"
|
|
#include "gf_rtc.h"
|
|
#include "item.h"
|
|
#include "msgdata.h"
|
|
#include "party.h"
|
|
#include "mail.h"
|
|
#include "trainer_data.h"
|
|
#include "map_section.h"
|
|
#include "unk_0200CF18.h"
|
|
#include "unk_02023694.h"
|
|
#include "unk_02078834.h"
|
|
#include "unk_02016EDC.h"
|
|
#include "constants/items.h"
|
|
#include "constants/moves.h"
|
|
#include "constants/trainer_class.h"
|
|
#include "constants/balls.h"
|
|
#include "constants/abilities.h"
|
|
#include "constants/map_sections.h"
|
|
#include "sound_02004A44.h"
|
|
|
|
void MonEncryptSegment(void *data, u32 size, u32 key);
|
|
void MonDecryptSegment(void *data, u32 size, u32 key);
|
|
u32 CalcMonChecksum(void *data, u32 size);
|
|
void InitBoxMonMoveset(BoxPokemon *boxMon);
|
|
void LoadMonBaseStats_HandleAlternateForme(int species, int forme, BASE_STATS *dest);
|
|
u16 ModifyStatByNature(u8 nature, u16 stat, u8 statID);
|
|
static u32 GetMonDataInternal(Pokemon *mon, int attr, void * dest);
|
|
static u32 GetBoxMonDataInternal(BoxPokemon *boxMon, int attr, void * dest);
|
|
static void SetMonDataInternal(Pokemon *mon, int attr, const void * data);
|
|
static void SetBoxMonDataInternal(BoxPokemon *boxMon, int attr, const void * data);
|
|
static void AddMonDataInternal(Pokemon *mon, int attr, int value);
|
|
static void AddBoxMonDataInternal(BoxPokemon *boxMon, int attr, int value);
|
|
PokemonDataBlock *GetSubstruct(BoxPokemon *boxMon, u32 pid, u8 which_struct);
|
|
void LoadMonPersonal(int species, BASE_STATS *dest);
|
|
int ResolveMonForme(int species, int forme);
|
|
u8 GetGenderBySpeciesAndPersonality_PreloadedPersonal(const BASE_STATS *personal, u16 species, u32 pid);
|
|
u32 MaskOfFlagNo(int flagno);
|
|
void GetBoxmonSpriteCharAndPlttNarcIds(struct SomeDrawPokemonStruct *a0, BoxPokemon *boxMon, u8 whichFacing, BOOL a3);
|
|
void DP_GetMonSpriteCharAndPlttNarcIdsEx(struct SomeDrawPokemonStruct *a0, u16 species, u8 gender, u8 whichFacing, u8 shiny, u8 forme, u32 pid);
|
|
void GetMonSpriteCharAndPlttNarcIdsEx(struct SomeDrawPokemonStruct *a0, u16 species, u8 gender, u8 whichFacing, u8 shiny, u8 forme, u32 pid);
|
|
u8 sub_02070438(u16 species, u8 forme);
|
|
u8 sub_02070854(BoxPokemon *boxMon, u8 whichFacing, BOOL a2);
|
|
u8 sub_02070A64(u16 species, u8 gender, u8 whichFacing, u8 forme, u32 pid);
|
|
u8 sub_020708D8(u16 species, u8 gender, u8 whichFacing, u8 forme, u32 pid);
|
|
void sub_02070D3C(s32 trainer_class, s32 a1, s32 a2, struct UnkStruct_02070D3C *a3);
|
|
int TrainerClassToBackpicID(int trainer_class, int a1);
|
|
void LoadMonEvolutionTable(u16 species, struct Evolution *evoTable);
|
|
BOOL MonHasMove(Pokemon *mon, u16 move_id);
|
|
void sub_0207213C(BoxPokemon *boxMon, PlayerProfile *playerProfile, u32 pokeball, u32 a3, u32 encounterType, HeapID heap_id);
|
|
void sub_02072190(BoxPokemon *boxMon, PlayerProfile *a1, u32 pokeball, u32 a3, u32 encounterType, HeapID heap_id);
|
|
|
|
#define ENCRY_ARGS_PTY(mon) (u16 *)&(mon)->party, sizeof((mon)->party), (mon)->box.pid
|
|
#define ENCRY_ARGS_BOX(boxMon) (u16 *)&(boxMon)->substructs, sizeof((boxMon)->substructs), (boxMon)->checksum
|
|
#define ENCRYPT_PTY(mon) MonEncryptSegment(ENCRY_ARGS_PTY(mon))
|
|
#define ENCRYPT_BOX(boxMon) MonEncryptSegment(ENCRY_ARGS_BOX(boxMon))
|
|
#define DECRYPT_PTY(mon) MonDecryptSegment(ENCRY_ARGS_PTY(mon))
|
|
#define DECRYPT_BOX(boxMon) MonDecryptSegment(ENCRY_ARGS_BOX(boxMon))
|
|
#define CHECKSUM(boxMon) CalcMonChecksum((u16 *)(boxMon)->substructs, sizeof((boxMon)->substructs))
|
|
#define SHINY_CHECK(otid, pid) (( \
|
|
((((otid) & 0xFFFF0000u) >> 16u)) ^ \
|
|
(((otid) & 0xFFFFu)) ^ \
|
|
((((pid) & 0xFFFF0000u) >> 16u))^ \
|
|
(((pid) & 0xFFFFu))) \
|
|
< 8u)
|
|
#define CALC_UNOWN_LETTER(pid) ((u32)((((pid) & 0x3000000) >> 18) | (((pid) & 0x30000) >> 12) | (((pid) & 0x300) >> 6) | (((pid) & 0x3) >> 0)) % 28u)
|
|
|
|
static const s8 sFlavorPreferencesByNature[NATURE_NUM][FLAVOR_MAX] = {
|
|
{ 0, 0, 0, 0, 0},
|
|
{ 1, 0, 0, 0, -1},
|
|
{ 1, 0, -1, 0, 0},
|
|
{ 1, -1, 0, 0, 0},
|
|
{ 1, 0, 0, -1, 0},
|
|
{-1, 0, 0, 0, 1},
|
|
{ 0, 0, 0, 0, 0},
|
|
{ 0, 0, -1, 0, 1},
|
|
{ 0, -1, 0, 0, 1},
|
|
{ 0, 0, 0, -1, 1},
|
|
{-1, 0, 1, 0, 0},
|
|
{ 0, 0, 1, 0, -1},
|
|
{ 0, 0, 0, 0, 0},
|
|
{ 0, -1, 1, 0, 0},
|
|
{ 0, 0, 1, -1, 0},
|
|
{-1, 1, 0, 0, 0},
|
|
{ 0, 1, 0, 0, -1},
|
|
{ 0, 1, -1, 0, 0},
|
|
{ 0, 0, 0, 0, 0},
|
|
{ 0, 1, 0, -1, 0},
|
|
{-1, 0, 0, 1, 0},
|
|
{ 0, 0, 0, 1, -1},
|
|
{ 0, 0, -1, 1, 0},
|
|
{ 0, -1, 0, 1, 0},
|
|
{ 0, 0, 0, 0, 0},
|
|
};
|
|
|
|
void ZeroMonData(Pokemon *mon) {
|
|
MI_CpuClearFast(mon, sizeof(Pokemon));
|
|
ENCRYPT_BOX(&mon->box);
|
|
ENCRYPT_PTY(mon);
|
|
}
|
|
|
|
void ZeroBoxMonData(BoxPokemon *boxMon) {
|
|
MI_CpuClearFast(boxMon, sizeof(BoxPokemon));
|
|
ENCRYPT_BOX(boxMon);
|
|
}
|
|
|
|
u32 SizeOfStructPokemon(void) {
|
|
return sizeof(Pokemon);
|
|
}
|
|
|
|
Pokemon *AllocMonZeroed(HeapID heapId) {
|
|
Pokemon *mon = (Pokemon *)AllocFromHeap(heapId, sizeof(Pokemon));
|
|
ZeroMonData(mon);
|
|
return mon;
|
|
}
|
|
|
|
BOOL AcquireMonLock(Pokemon *mon) {
|
|
BOOL locked = FALSE;
|
|
if (!mon->box.party_lock) {
|
|
locked = TRUE;
|
|
GF_ASSERT(!mon->box.box_lock);
|
|
mon->box.party_lock = TRUE;
|
|
mon->box.box_lock = TRUE;
|
|
DECRYPT_PTY(mon);
|
|
DECRYPT_BOX(&mon->box);
|
|
}
|
|
return locked;
|
|
}
|
|
|
|
BOOL ReleaseMonLock(Pokemon *mon, BOOL locked) {
|
|
BOOL prev = FALSE;
|
|
if (mon->box.party_lock == TRUE && locked == TRUE) {
|
|
prev = TRUE;
|
|
mon->box.party_lock = FALSE;
|
|
mon->box.box_lock = FALSE;
|
|
ENCRYPT_PTY(mon);
|
|
mon->box.checksum = CHECKSUM(&mon->box);
|
|
ENCRYPT_BOX(&mon->box);
|
|
}
|
|
return prev;
|
|
}
|
|
|
|
BOOL AcquireBoxMonLock(BoxPokemon *boxMon) {
|
|
BOOL locked = FALSE;
|
|
if (!boxMon->box_lock) {
|
|
locked = TRUE;
|
|
boxMon->box_lock = TRUE;
|
|
DECRYPT_BOX(boxMon);
|
|
}
|
|
return locked;
|
|
}
|
|
|
|
BOOL ReleaseBoxMonLock(BoxPokemon *boxMon, BOOL locked) {
|
|
BOOL prev = FALSE;
|
|
if (boxMon->box_lock == TRUE && locked == TRUE) {
|
|
prev = TRUE;
|
|
boxMon->box_lock = FALSE;
|
|
boxMon->checksum = CHECKSUM(boxMon);
|
|
ENCRYPT_BOX(boxMon);
|
|
}
|
|
return prev;
|
|
}
|
|
void CreateMon(Pokemon *mon, int species, int level, int fixedIV, int hasFixedPersonality, int fixedPersonality, int otIdType, int fixedOtId) {
|
|
MAIL * mail;
|
|
u32 capsule;
|
|
CAPSULE seal_coords;
|
|
ZeroMonData(mon);
|
|
CreateBoxMon(&mon->box, species, level, fixedIV, hasFixedPersonality, fixedPersonality, otIdType, fixedOtId);
|
|
// Not your average encryption call
|
|
MonEncryptSegment((u16 *)&mon->party, sizeof(mon->party), 0);
|
|
ENCRYPT_PTY(mon);
|
|
SetMonData(mon, MON_DATA_LEVEL, &level);
|
|
mail = Mail_New(HEAP_ID_0);
|
|
SetMonData(mon, MON_DATA_MAIL_STRUCT, mail);
|
|
FreeToHeap(mail);
|
|
capsule = 0;
|
|
SetMonData(mon, MON_DATA_CAPSULE, &capsule);
|
|
MI_CpuClearFast(&seal_coords, sizeof(seal_coords));
|
|
SetMonData(mon, MON_DATA_SEAL_COORDS, &seal_coords);
|
|
CalcMonLevelAndStats(mon);
|
|
}
|
|
|
|
void CreateBoxMon(BoxPokemon *boxMon, int species, int level, int fixedIV, int hasFixedPersonality, int fixedPersonality, int otIdType, int fixedOtId) {
|
|
BOOL decry;
|
|
u32 exp;
|
|
u32 iv;
|
|
ZeroBoxMonData(boxMon);
|
|
decry = AcquireBoxMonLock(boxMon);
|
|
if (hasFixedPersonality == 0) {
|
|
fixedPersonality = (LCRandom() | (LCRandom() << 16));
|
|
}
|
|
SetBoxMonData(boxMon, MON_DATA_PERSONALITY, &fixedPersonality);
|
|
if (otIdType == 2) {
|
|
do {
|
|
fixedOtId = (LCRandom() | (LCRandom() << 16));
|
|
} while (SHINY_CHECK(fixedOtId, fixedPersonality));
|
|
} else if (otIdType != 1) {
|
|
fixedOtId = 0;
|
|
}
|
|
SetBoxMonData(boxMon, MON_DATA_OTID, &fixedOtId);
|
|
SetBoxMonData(boxMon, MON_DATA_GAME_LANGUAGE, (void *)&gGameLanguage);
|
|
SetBoxMonData(boxMon, MON_DATA_SPECIES, &species);
|
|
SetBoxMonData(boxMon, MON_DATA_SPECIES_NAME, NULL);
|
|
exp = GetMonExpBySpeciesAndLevel(species, level);
|
|
SetBoxMonData(boxMon, MON_DATA_EXPERIENCE, &exp);
|
|
exp = (u32)GetMonBaseStat(species, BASE_FRIENDSHIP);
|
|
SetBoxMonData(boxMon, MON_DATA_FRIENDSHIP, &exp);
|
|
SetBoxMonData(boxMon, MON_DATA_MET_LEVEL, &level);
|
|
SetBoxMonData(boxMon, MON_DATA_GAME_VERSION, (void *)&gGameVersion);
|
|
exp = ITEM_POKE_BALL;
|
|
SetBoxMonData(boxMon, MON_DATA_POKEBALL, &exp);
|
|
SetBoxMonData(boxMon, MON_DATA_DP_POKEBALL, &exp);
|
|
if (fixedIV < 0x20) {
|
|
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 {
|
|
exp = LCRandom();
|
|
iv = exp & 0x1F;
|
|
SetBoxMonData(boxMon, MON_DATA_HP_IV, &iv);
|
|
iv = (exp & 0x3E0) >> 5;
|
|
SetBoxMonData(boxMon, MON_DATA_ATK_IV, &iv);
|
|
iv = (exp & 0x7C00) >> 10;
|
|
SetBoxMonData(boxMon, MON_DATA_DEF_IV, &iv);
|
|
exp = LCRandom();
|
|
iv = exp & 0x1F;
|
|
SetBoxMonData(boxMon, MON_DATA_SPEED_IV, &iv);
|
|
iv = (exp & 0x3E0) >> 5;
|
|
SetBoxMonData(boxMon, MON_DATA_SPATK_IV, &iv);
|
|
iv = (exp & 0x7C00) >> 10;
|
|
SetBoxMonData(boxMon, MON_DATA_SPDEF_IV, &iv);
|
|
}
|
|
exp = (u32)GetMonBaseStat(species, BASE_ABILITY_1);
|
|
iv = (u32)GetMonBaseStat(species, BASE_ABILITY_2);
|
|
if (iv != 0) {
|
|
if (fixedPersonality & 1) {
|
|
SetBoxMonData(boxMon, MON_DATA_ABILITY, &iv);
|
|
} else {
|
|
SetBoxMonData(boxMon, MON_DATA_ABILITY, &exp);
|
|
}
|
|
} else {
|
|
SetBoxMonData(boxMon, MON_DATA_ABILITY, &exp);
|
|
}
|
|
exp = GetBoxMonGender(boxMon);
|
|
SetBoxMonData(boxMon, MON_DATA_GENDER, &exp);
|
|
InitBoxMonMoveset(boxMon);
|
|
ReleaseBoxMonLock(boxMon, decry);
|
|
}
|
|
|
|
void CreateMonWithNature(Pokemon *mon, u16 species, u8 level, u8 fixedIv, u8 nature) {
|
|
u32 personality;
|
|
do {
|
|
personality = (u32)(LCRandom() | (LCRandom() << 16));
|
|
} while (nature != GetNatureFromPersonality(personality));
|
|
CreateMon(mon, (int)species, (int)level, (int)fixedIv, TRUE, (int)personality, (int)0, (int)0);
|
|
}
|
|
|
|
void CreateMonWithGenderNatureLetter(Pokemon *mon, u16 species, u8 level, u8 fixedIv, u8 gender, u8 nature, u8 letter) {
|
|
u32 pid = 0;
|
|
u16 test = 0;
|
|
if (letter != 0 && letter < 29) {
|
|
do {
|
|
pid = (u32)(LCRandom() | (LCRandom() << 16));
|
|
test = (u16)CALC_UNOWN_LETTER(pid);
|
|
} while (nature != GetNatureFromPersonality(pid) || gender != GetGenderBySpeciesAndPersonality(species, pid) || test != letter - 1);
|
|
} else {
|
|
pid = GenPersonalityByGenderAndNature(species, gender, nature);
|
|
}
|
|
CreateMon(mon, (int)species, (int)level, (int)fixedIv, 1, (int)pid, 0, 0);
|
|
}
|
|
|
|
u32 GenPersonalityByGenderAndNature(u16 species, u8 gender, u8 nature) {
|
|
int pid = nature;
|
|
u8 ratio = (u8)GetMonBaseStat(species, BASE_GENDER_RATIO);
|
|
switch (ratio) {
|
|
case MON_RATIO_MALE:
|
|
case MON_RATIO_FEMALE:
|
|
case MON_RATIO_UNKNOWN:
|
|
break;
|
|
default:
|
|
if (gender == MON_MALE) {
|
|
// Smallest increment that forces the low byte to exceed the
|
|
// gender ratio, thus making the mon male
|
|
pid = 25 * ((ratio / 25) + 1);
|
|
pid += nature;
|
|
}
|
|
break;
|
|
}
|
|
return (u32)pid;
|
|
}
|
|
|
|
void CreateMonWithFixedIVs(Pokemon *mon, int species, int level, int ivs, int personality) {
|
|
CreateMon(mon, species, level, 0, 1, personality, 0, 0);
|
|
SetMonData(mon, MON_DATA_IVS_WORD, &ivs);
|
|
CalcMonLevelAndStats(mon);
|
|
}
|
|
|
|
void CalcMonLevelAndStats(Pokemon *mon) {
|
|
BOOL decry = AcquireMonLock(mon);
|
|
u32 level = (u32)CalcMonLevel(mon);
|
|
SetMonData(mon, MON_DATA_LEVEL, &level);
|
|
CalcMonStats(mon);
|
|
ReleaseMonLock(mon, decry);
|
|
}
|
|
|
|
void CalcMonStats(Pokemon *mon) {
|
|
BASE_STATS * baseStats;
|
|
int level;
|
|
int maxHp;
|
|
int hpIv;
|
|
int hpEv;
|
|
int atkIv;
|
|
int defIv;
|
|
int speedIv;
|
|
int spatkIv;
|
|
int spdefIv;
|
|
int atkEv;
|
|
int defEv;
|
|
int speedEv;
|
|
int spatkEv;
|
|
int spdefEv;
|
|
int forme;
|
|
int hp;
|
|
int species;
|
|
int newMaxHp;
|
|
int newAtk;
|
|
int newDef;
|
|
int newSpeed;
|
|
int newSpatk;
|
|
int newSpdef;
|
|
|
|
BOOL decry = AcquireMonLock(mon);
|
|
level = (int)GetMonData(mon, MON_DATA_LEVEL, NULL);
|
|
maxHp = (int)GetMonData(mon, MON_DATA_MAXHP, NULL);
|
|
hp = (int)GetMonData(mon, MON_DATA_HP, NULL);
|
|
hpIv = (int)GetMonData(mon, MON_DATA_HP_IV, NULL);
|
|
hpEv = (int)GetMonData(mon, MON_DATA_HP_EV, NULL);
|
|
atkIv = (int)GetMonData(mon, MON_DATA_ATK_IV, NULL);
|
|
atkEv = (int)GetMonData(mon, MON_DATA_ATK_EV, NULL);
|
|
defIv = (int)GetMonData(mon, MON_DATA_DEF_IV, NULL);
|
|
defEv = (int)GetMonData(mon, MON_DATA_DEF_EV, NULL);
|
|
speedIv = (int)GetMonData(mon, MON_DATA_SPEED_IV, NULL);
|
|
speedEv = (int)GetMonData(mon, MON_DATA_SPEED_EV, NULL);
|
|
spatkIv = (int)GetMonData(mon, MON_DATA_SPATK_IV, NULL);
|
|
spatkEv = (int)GetMonData(mon, MON_DATA_SPATK_EV, NULL);
|
|
spdefIv = (int)GetMonData(mon, MON_DATA_SPDEF_IV, NULL);
|
|
spdefEv = (int)GetMonData(mon, MON_DATA_SPDEF_EV, NULL);
|
|
forme = (int)GetMonData(mon, MON_DATA_FORME, NULL);
|
|
species = (int)GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
|
|
baseStats = (BASE_STATS *)AllocFromHeap(HEAP_ID_0, sizeof(BASE_STATS));
|
|
LoadMonBaseStats_HandleAlternateForme(species, forme, baseStats);
|
|
|
|
if (species == SPECIES_SHEDINJA) {
|
|
newMaxHp = 1;
|
|
} else {
|
|
newMaxHp = (baseStats->hp * 2 + hpIv + hpEv / 4) * level / 100 + level + 10;
|
|
}
|
|
SetMonData(mon, MON_DATA_MAXHP, &newMaxHp);
|
|
|
|
newAtk = (baseStats->atk * 2 + atkIv + atkEv / 4) * level / 100 + 5;
|
|
newAtk = ModifyStatByNature(GetMonNature(mon), (u16)newAtk, STAT_ATK);
|
|
SetMonData(mon, MON_DATA_ATK, &newAtk);
|
|
|
|
newDef = (baseStats->def * 2 + defIv + defEv / 4) * level / 100 + 5;
|
|
newDef = ModifyStatByNature(GetMonNature(mon), (u16)newDef, STAT_DEF);
|
|
SetMonData(mon, MON_DATA_DEF, &newDef);
|
|
|
|
newSpeed = (baseStats->speed * 2 + speedIv + speedEv / 4) * level / 100 + 5;
|
|
newSpeed = ModifyStatByNature(GetMonNature(mon), (u16)newSpeed, STAT_SPEED);
|
|
SetMonData(mon, MON_DATA_SPEED, &newSpeed);
|
|
|
|
newSpatk = (baseStats->spatk * 2 + spatkIv + spatkEv / 4) * level / 100 + 5;
|
|
newSpatk = ModifyStatByNature(GetMonNature(mon), (u16)newSpatk, STAT_SPATK);
|
|
SetMonData(mon, MON_DATA_SPATK, &newSpatk);
|
|
|
|
newSpdef = (baseStats->spdef * 2 + spdefIv + spdefEv / 4) * level / 100 + 5;
|
|
newSpdef = ModifyStatByNature(GetMonNature(mon), (u16)newSpdef, STAT_SPDEF);
|
|
SetMonData(mon, MON_DATA_SPDEF, &newSpdef);
|
|
|
|
FreeToHeap(baseStats);
|
|
|
|
if (hp != 0 || maxHp == 0) {
|
|
if (species == SPECIES_SHEDINJA) {
|
|
hp = 1;
|
|
} else if (hp == 0) {
|
|
hp = newMaxHp;
|
|
} else if (newMaxHp - maxHp < 0) {
|
|
if (hp > newMaxHp) {
|
|
hp = newMaxHp;
|
|
}
|
|
} else {
|
|
hp += newMaxHp - maxHp;
|
|
}
|
|
}
|
|
if (hp != 0) {
|
|
SetMonData(mon, MON_DATA_HP, &hp);
|
|
}
|
|
ReleaseMonLock(mon, decry);
|
|
}
|
|
|
|
u32 GetMonData(Pokemon *mon, int attr, void * dest) {
|
|
u32 ret;
|
|
u32 checksum;
|
|
if (!mon->box.party_lock) {
|
|
DECRYPT_PTY(mon);
|
|
DECRYPT_BOX(&mon->box);
|
|
checksum = CHECKSUM(&mon->box);
|
|
if (checksum != mon->box.checksum) {
|
|
GF_ASSERT(checksum == mon->box.checksum);
|
|
mon->box.checksum_fail = TRUE;
|
|
}
|
|
}
|
|
ret = GetMonDataInternal(mon, attr, dest);
|
|
if (!mon->box.party_lock) {
|
|
ENCRYPT_PTY(mon);
|
|
ENCRYPT_BOX(&mon->box);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static u32 GetMonDataInternal(Pokemon *mon, int attr, void * dest) {
|
|
switch (attr) {
|
|
case MON_DATA_STATUS:
|
|
return mon->party.status;
|
|
case MON_DATA_LEVEL:
|
|
return mon->party.level;
|
|
case MON_DATA_CAPSULE:
|
|
return mon->party.capsule;
|
|
case MON_DATA_HP:
|
|
return mon->party.hp;
|
|
case MON_DATA_MAXHP:
|
|
return mon->party.maxHp;
|
|
case MON_DATA_ATK:
|
|
return mon->party.atk;
|
|
case MON_DATA_DEF:
|
|
return mon->party.def;
|
|
case MON_DATA_SPEED:
|
|
return mon->party.speed;
|
|
case MON_DATA_SPATK:
|
|
return mon->party.spatk;
|
|
case MON_DATA_SPDEF:
|
|
return mon->party.spdef;
|
|
case MON_DATA_MAIL_STRUCT:
|
|
Mail_Copy(&mon->party.mail, dest);
|
|
return 1;
|
|
case MON_DATA_SEAL_COORDS:
|
|
CopyCapsule(&mon->party.sealCoords, dest);
|
|
return 1;
|
|
default:
|
|
return GetBoxMonDataInternal(&mon->box, attr, dest);
|
|
}
|
|
}
|
|
|
|
u32 GetBoxMonData(BoxPokemon *boxMon, int attr, void * dest) {
|
|
u32 ret;
|
|
u32 checksum;
|
|
if (!boxMon->box_lock) {
|
|
DECRYPT_BOX(boxMon);
|
|
checksum = CHECKSUM(boxMon);
|
|
if (checksum != boxMon->checksum) {
|
|
GF_ASSERT(checksum == boxMon->checksum);
|
|
boxMon->checksum_fail = TRUE;
|
|
}
|
|
}
|
|
ret = GetBoxMonDataInternal(boxMon, attr, dest);
|
|
if (!boxMon->box_lock) {
|
|
ENCRYPT_BOX(boxMon);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static u32 GetBoxMonDataInternal(BoxPokemon *boxMon, int attr, void * dest) {
|
|
u32 ret = 0;
|
|
PokemonDataBlockA * blockA = &GetSubstruct(boxMon, boxMon->pid, 0)->blockA;
|
|
PokemonDataBlockB * blockB = &GetSubstruct(boxMon, boxMon->pid, 1)->blockB;
|
|
PokemonDataBlockC * blockC = &GetSubstruct(boxMon, boxMon->pid, 2)->blockC;
|
|
PokemonDataBlockD * blockD = &GetSubstruct(boxMon, boxMon->pid, 3)->blockD;
|
|
|
|
switch (attr) {
|
|
default:
|
|
ret = 0;
|
|
break;
|
|
case MON_DATA_PERSONALITY:
|
|
ret = boxMon->pid;
|
|
break;
|
|
case MON_DATA_PARTY_LOCK:
|
|
ret = boxMon->party_lock;
|
|
break;
|
|
case MON_DATA_BOX_LOCK:
|
|
ret = boxMon->box_lock;
|
|
break;
|
|
case MON_DATA_CHECKSUM_FAILED:
|
|
ret = boxMon->checksum_fail;
|
|
break;
|
|
case MON_DATA_CHECKSUM:
|
|
ret = boxMon->checksum;
|
|
break;
|
|
case MON_DATA_SPECIES_EXISTS:
|
|
if (blockA->species != SPECIES_NONE) {
|
|
ret = TRUE;
|
|
} else {
|
|
ret = FALSE;
|
|
}
|
|
break;
|
|
case MON_DATA_SANITY_IS_EGG:
|
|
ret = boxMon->checksum_fail;
|
|
if (!ret) {
|
|
ret = blockB->isEgg;
|
|
}
|
|
break;
|
|
case MON_DATA_SPECIES2:
|
|
ret = blockA->species;
|
|
if (ret != SPECIES_NONE && (blockB->isEgg || boxMon->checksum_fail)) {
|
|
ret = SPECIES_EGG;
|
|
}
|
|
break;
|
|
case MON_DATA_LEVEL:
|
|
ret = (u32)CalcLevelBySpeciesAndExp(blockA->species, blockA->exp);
|
|
break;
|
|
case MON_DATA_SPECIES:
|
|
if (boxMon->checksum_fail) {
|
|
ret = SPECIES_EGG;
|
|
} else {
|
|
ret = blockA->species;
|
|
}
|
|
break;
|
|
case MON_DATA_HELD_ITEM:
|
|
ret = blockA->heldItem;
|
|
break;
|
|
case MON_DATA_OTID:
|
|
ret = blockA->otID;
|
|
break;
|
|
case MON_DATA_EXPERIENCE:
|
|
ret = blockA->exp;
|
|
break;
|
|
case MON_DATA_FRIENDSHIP:
|
|
ret = blockA->friendship;
|
|
break;
|
|
case MON_DATA_ABILITY:
|
|
ret = blockA->ability;
|
|
break;
|
|
case MON_DATA_MARKINGS:
|
|
ret = blockA->markings;
|
|
break;
|
|
case MON_DATA_GAME_LANGUAGE:
|
|
ret = blockA->originLanguage;
|
|
break;
|
|
case MON_DATA_HP_EV:
|
|
ret = blockA->hpEV;
|
|
break;
|
|
case MON_DATA_ATK_EV:
|
|
ret = blockA->atkEV;
|
|
break;
|
|
case MON_DATA_DEF_EV:
|
|
ret = blockA->defEV;
|
|
break;
|
|
case MON_DATA_SPEED_EV:
|
|
ret = blockA->spdEV;
|
|
break;
|
|
case MON_DATA_SPATK_EV:
|
|
ret = blockA->spatkEV;
|
|
break;
|
|
case MON_DATA_SPDEF_EV:
|
|
ret = blockA->spdefEV;
|
|
break;
|
|
case MON_DATA_COOL:
|
|
ret = blockA->coolStat;
|
|
break;
|
|
case MON_DATA_BEAUTY:
|
|
ret = blockA->beautyStat;
|
|
break;
|
|
case MON_DATA_CUTE:
|
|
ret = blockA->cuteStat;
|
|
break;
|
|
case MON_DATA_SMART:
|
|
ret = blockA->smartStat;
|
|
break;
|
|
case MON_DATA_TOUGH:
|
|
ret = blockA->toughStat;
|
|
break;
|
|
case MON_DATA_SHEEN:
|
|
ret = blockA->sheen;
|
|
break;
|
|
case MON_DATA_SINNOH_CHAMP_RIBBON:
|
|
case MON_DATA_ABILITY_RIBBON:
|
|
case MON_DATA_GREAT_ABILITY_RIBBON:
|
|
case MON_DATA_DOUBLE_ABILITY_RIBBON:
|
|
case MON_DATA_MULTI_ABILITY_RIBBON:
|
|
case MON_DATA_PAIR_ABILITY_RIBBON:
|
|
case MON_DATA_WORLD_ABILITY_RIBBON:
|
|
case MON_DATA_ALERT_RIBBON:
|
|
case MON_DATA_SHOCK_RIBBON:
|
|
case MON_DATA_DOWNCAST_RIBBON:
|
|
case MON_DATA_CARELESS_RIBBON:
|
|
case MON_DATA_RELAX_RIBBON:
|
|
case MON_DATA_SNOOZE_RIBBON:
|
|
case MON_DATA_SMILE_RIBBON:
|
|
case MON_DATA_GORGEOUS_RIBBON:
|
|
case MON_DATA_ROYAL_RIBBON:
|
|
case MON_DATA_GORGEOUS_ROYAL_RIBBON:
|
|
case MON_DATA_FOOTPRINT_RIBBON:
|
|
case MON_DATA_RECORD_RIBBON:
|
|
case MON_DATA_HISTORY_RIBBON:
|
|
case MON_DATA_LEGEND_RIBBON:
|
|
case MON_DATA_RED_RIBBON:
|
|
case MON_DATA_GREEN_RIBBON:
|
|
case MON_DATA_BLUE_RIBBON:
|
|
case MON_DATA_FESTIVAL_RIBBON:
|
|
case MON_DATA_CARNIVAL_RIBBON:
|
|
case MON_DATA_CLASSIC_RIBBON:
|
|
case MON_DATA_PREMIER_RIBBON:
|
|
case MON_DATA_SINNOH_RIBBON_53: {
|
|
if (blockA->sinnohRibbons & (1ll << (attr - MON_DATA_SINNOH_CHAMP_RIBBON))) {
|
|
ret = TRUE;
|
|
} else {
|
|
ret = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
case MON_DATA_MOVE1:
|
|
case MON_DATA_MOVE2:
|
|
case MON_DATA_MOVE3:
|
|
case MON_DATA_MOVE4:
|
|
ret = blockB->moves[attr - MON_DATA_MOVE1];
|
|
break;
|
|
case MON_DATA_MOVE1PP:
|
|
case MON_DATA_MOVE2PP:
|
|
case MON_DATA_MOVE3PP:
|
|
case MON_DATA_MOVE4PP:
|
|
ret = blockB->movePP[attr - MON_DATA_MOVE1PP];
|
|
break;
|
|
case MON_DATA_MOVE1PPUP:
|
|
case MON_DATA_MOVE2PPUP:
|
|
case MON_DATA_MOVE3PPUP:
|
|
case MON_DATA_MOVE4PPUP:
|
|
ret = blockB->movePpUps[attr - MON_DATA_MOVE1PPUP];
|
|
break;
|
|
case MON_DATA_MOVE1MAXPP:
|
|
case MON_DATA_MOVE2MAXPP:
|
|
case MON_DATA_MOVE3MAXPP:
|
|
case MON_DATA_MOVE4MAXPP:
|
|
ret = (u32)GetMoveMaxPP(blockB->moves[attr - MON_DATA_MOVE1MAXPP], blockB->movePpUps[attr - MON_DATA_MOVE1MAXPP]);
|
|
break;
|
|
case MON_DATA_HP_IV:
|
|
ret = blockB->hpIV;
|
|
break;
|
|
case MON_DATA_ATK_IV:
|
|
ret = blockB->atkIV;
|
|
break;
|
|
case MON_DATA_DEF_IV:
|
|
ret = blockB->defIV;
|
|
break;
|
|
case MON_DATA_SPEED_IV:
|
|
ret = blockB->spdIV;
|
|
break;
|
|
case MON_DATA_SPATK_IV:
|
|
ret = blockB->spatkIV;
|
|
break;
|
|
case MON_DATA_SPDEF_IV:
|
|
ret = blockB->spdefIV;
|
|
break;
|
|
case MON_DATA_IS_EGG:
|
|
ret = boxMon->checksum_fail;
|
|
if (!ret) {
|
|
ret = blockB->isEgg;
|
|
}
|
|
break;
|
|
case MON_DATA_HAS_NICKNAME:
|
|
ret = blockB->isNicknamed;
|
|
break;
|
|
case MON_DATA_HOENN_COOL_RIBBON:
|
|
case MON_DATA_HOENN_COOL_RIBBON_SUPER:
|
|
case MON_DATA_HOENN_COOL_RIBBON_HYPER:
|
|
case MON_DATA_HOENN_COOL_RIBBON_MASTER:
|
|
case MON_DATA_HOENN_BEAUTY_RIBBON:
|
|
case MON_DATA_HOENN_BEAUTY_RIBBON_SUPER:
|
|
case MON_DATA_HOENN_BEAUTY_RIBBON_HYPER:
|
|
case MON_DATA_HOENN_BEAUTY_RIBBON_MASTER:
|
|
case MON_DATA_HOENN_CUTE_RIBBON:
|
|
case MON_DATA_HOENN_CUTE_RIBBON_SUPER:
|
|
case MON_DATA_HOENN_CUTE_RIBBON_HYPER:
|
|
case MON_DATA_HOENN_CUTE_RIBBON_MASTER:
|
|
case MON_DATA_HOENN_SMART_RIBBON:
|
|
case MON_DATA_HOENN_SMART_RIBBON_SUPER:
|
|
case MON_DATA_HOENN_SMART_RIBBON_HYPER:
|
|
case MON_DATA_HOENN_SMART_RIBBON_MASTER:
|
|
case MON_DATA_HOENN_TOUGH_RIBBON:
|
|
case MON_DATA_HOENN_TOUGH_RIBBON_SUPER:
|
|
case MON_DATA_HOENN_TOUGH_RIBBON_HYPER:
|
|
case MON_DATA_HOENN_TOUGH_RIBBON_MASTER:
|
|
case MON_DATA_HOENN_CHAMPION_RIBBON:
|
|
case MON_DATA_HOENN_WINNING_RIBBON:
|
|
case MON_DATA_HOENN_VICTORY_RIBBON:
|
|
case MON_DATA_HOENN_ARTIST_RIBBON:
|
|
case MON_DATA_HOENN_EFFORT_RIBBON:
|
|
case MON_DATA_HOENN_MARINE_RIBBON:
|
|
case MON_DATA_HOENN_LAND_RIBBON:
|
|
case MON_DATA_HOENN_SKY_RIBBON:
|
|
case MON_DATA_HOENN_COUNTRY_RIBBON:
|
|
case MON_DATA_HOENN_NATIONAL_RIBBON:
|
|
case MON_DATA_HOENN_EARTH_RIBBON:
|
|
case MON_DATA_HOENN_WORLD_RIBBON:
|
|
if (blockB->ribbonFlags & (1ll << (attr - MON_DATA_HOENN_COOL_RIBBON))) {
|
|
ret = TRUE;
|
|
} else {
|
|
ret = FALSE;
|
|
}
|
|
break;
|
|
case MON_DATA_FATEFUL_ENCOUNTER:
|
|
ret = blockB->fatefulEncounter;
|
|
break;
|
|
case MON_DATA_GENDER:
|
|
ret = GetGenderBySpeciesAndPersonality(blockA->species, boxMon->pid);
|
|
blockB->gender = (u8)ret;
|
|
boxMon->checksum = CHECKSUM(boxMon);
|
|
break;
|
|
case MON_DATA_FORME:
|
|
ret = blockB->alternateForm;
|
|
break;
|
|
case MON_DATA_RESERVED_113:
|
|
ret = blockB->unk_19_6;
|
|
break;
|
|
case MON_DATA_RESERVED_114:
|
|
ret = blockB->Unused;
|
|
break;
|
|
case MON_DATA_NICKNAME:
|
|
if (boxMon->checksum_fail) {
|
|
GetSpeciesNameIntoArray(SPECIES_MANAPHY_EGG, HEAP_ID_0, dest);
|
|
} else {
|
|
u16 * dest16 = (u16 *)dest;
|
|
for (ret = 0; ret < POKEMON_NAME_LENGTH; ret++) {
|
|
dest16[ret] = blockC->nickname[ret];
|
|
}
|
|
dest16[ret] = EOS;
|
|
}
|
|
break;
|
|
case MON_DATA_NICKNAME_4:
|
|
ret = blockB->isNicknamed;
|
|
// fallthrough
|
|
case MON_DATA_NICKNAME_3:
|
|
if (boxMon->checksum_fail) {
|
|
String * buffer = GetSpeciesName(SPECIES_MANAPHY_EGG, HEAP_ID_0);
|
|
String_Copy(dest, buffer);
|
|
String_Delete(buffer);
|
|
} else {
|
|
CopyU16ArrayToString(dest, blockC->nickname);
|
|
}
|
|
break;
|
|
case MON_DATA_UNK_121:
|
|
ret = blockC->Unused;
|
|
break;
|
|
case MON_DATA_GAME_VERSION:
|
|
ret = blockC->originGame;
|
|
break;
|
|
case MON_DATA_COOL_RIBBON:
|
|
case MON_DATA_COOL_RIBBON_GREAT:
|
|
case MON_DATA_COOL_RIBBON_ULTRA:
|
|
case MON_DATA_COOL_RIBBON_MASTER:
|
|
case MON_DATA_BEAUTY_RIBBON:
|
|
case MON_DATA_BEAUTY_RIBBON_GREAT:
|
|
case MON_DATA_BEAUTY_RIBBON_ULTRA:
|
|
case MON_DATA_BEAUTY_RIBBON_MASTER:
|
|
case MON_DATA_CUTE_RIBBON:
|
|
case MON_DATA_CUTE_RIBBON_GREAT:
|
|
case MON_DATA_CUTE_RIBBON_ULTRA:
|
|
case MON_DATA_CUTE_RIBBON_MASTER:
|
|
case MON_DATA_SMART_RIBBON:
|
|
case MON_DATA_SMART_RIBBON_GREAT:
|
|
case MON_DATA_SMART_RIBBON_ULTRA:
|
|
case MON_DATA_SMART_RIBBON_MASTER:
|
|
case MON_DATA_TOUGH_RIBBON:
|
|
case MON_DATA_TOUGH_RIBBON_GREAT:
|
|
case MON_DATA_TOUGH_RIBBON_ULTRA:
|
|
case MON_DATA_TOUGH_RIBBON_MASTER:
|
|
case MON_DATA_SINNOH_RIBBON_143:
|
|
if (blockC->sinnohRibbons2 & (1ll << (attr - MON_DATA_COOL_RIBBON))) {
|
|
ret = TRUE;
|
|
} else {
|
|
ret = FALSE;
|
|
}
|
|
break;
|
|
case MON_DATA_OT_NAME: {
|
|
u16 * dest16 = (u16 *)dest;
|
|
for (ret = 0; ret < PLAYER_NAME_LENGTH; ret++) {
|
|
dest16[ret] = blockD->otTrainerName[ret];
|
|
}
|
|
dest16[ret] = EOS;
|
|
}
|
|
break;
|
|
case MON_DATA_OT_NAME_2:
|
|
CopyU16ArrayToString(dest, blockD->otTrainerName);
|
|
break;
|
|
case MON_DATA_EGG_MET_YEAR:
|
|
ret = blockD->dateEggReceived[0];
|
|
break;
|
|
case MON_DATA_EGG_MET_MONTH:
|
|
ret = blockD->dateEggReceived[1];
|
|
break;
|
|
case MON_DATA_EGG_MET_DAY:
|
|
ret = blockD->dateEggReceived[2];
|
|
break;
|
|
case MON_DATA_MET_YEAR:
|
|
ret = blockD->dateMet[0];
|
|
break;
|
|
case MON_DATA_MET_MONTH:
|
|
ret = blockD->dateMet[1];
|
|
break;
|
|
case MON_DATA_MET_DAY:
|
|
ret = blockD->dateMet[2];
|
|
break;
|
|
case MON_DATA_EGG_MET_LOCATION:
|
|
case MON_DATA_HGSS_EGG_MET_LOCATION:
|
|
if (blockD->DP_EggLocation != METLOC_FARAWAY_PLACE || (ret = blockB->Platinum_EggLocation) == 0) {
|
|
ret = blockD->DP_EggLocation;
|
|
}
|
|
break;
|
|
case MON_DATA_MET_LOCATION:
|
|
case MON_DATA_HGSS_MET_LOCATION:
|
|
if (blockD->DP_MetLocation != METLOC_FARAWAY_PLACE || (ret = blockB->Platinum_MetLocation) == 0) {
|
|
ret = blockD->DP_MetLocation;
|
|
}
|
|
break;
|
|
case MON_DATA_POKERUS:
|
|
ret = blockD->pokerus;
|
|
break;
|
|
case MON_DATA_POKEBALL:
|
|
if (!(blockC->originGame == VERSION_HEARTGOLD || blockC->originGame == VERSION_SOULSILVER) || (ret = blockD->HGSS_Pokeball) == 0) {
|
|
ret = blockD->pokeball;
|
|
}
|
|
break;
|
|
case MON_DATA_DP_POKEBALL:
|
|
ret = blockD->pokeball;
|
|
break;
|
|
case MON_DATA_MET_LEVEL:
|
|
ret = blockD->metLevel;
|
|
break;
|
|
case MON_DATA_MET_GENDER:
|
|
ret = blockD->metGender;
|
|
break;
|
|
case MON_DATA_ENCOUNTER_TYPE:
|
|
ret = blockD->encounterType;
|
|
break;
|
|
case MON_DATA_IVS_WORD:
|
|
ret = (blockB->hpIV) | \
|
|
(blockB->atkIV << 5) | \
|
|
(blockB->defIV << 10) | \
|
|
(blockB->spdIV << 15) | \
|
|
(blockB->spatkIV << 20) | \
|
|
(blockB->spdefIV << 25);
|
|
break;
|
|
case MON_DATA_UNK_176:
|
|
if ((blockA->species == SPECIES_NIDORAN_F || blockA->species == SPECIES_NIDORAN_M) && !blockB->isNicknamed) {
|
|
ret = FALSE;
|
|
} else {
|
|
ret = TRUE;
|
|
}
|
|
break;
|
|
case MON_DATA_TYPE_1:
|
|
case MON_DATA_TYPE_2:
|
|
if (blockA->species == SPECIES_ARCEUS && blockA->ability == ABILITY_MULTITYPE) {
|
|
ret = (u32)GetArceusTypeByHeldItemEffect((u16) GetItemAttr(blockA->heldItem, ITEMATTR_HOLD_EFFECT, HEAP_ID_0));
|
|
} else {
|
|
ret = (u32)GetMonBaseStat_HandleAlternateForme(blockA->species, blockB->alternateForm, (enum BaseStat)(attr - MON_DATA_TYPE_1 + BASE_TYPE1));
|
|
}
|
|
break;
|
|
case MON_DATA_SPECIES_NAME:
|
|
GetSpeciesNameIntoArray(blockA->species, HEAP_ID_0, dest);
|
|
break;
|
|
case MON_DATA_SHINY_LEAF_A:
|
|
case MON_DATA_SHINY_LEAF_B:
|
|
case MON_DATA_SHINY_LEAF_C:
|
|
case MON_DATA_SHINY_LEAF_D:
|
|
case MON_DATA_SHINY_LEAF_E:
|
|
case MON_DATA_SHINY_LEAF_CROWN:
|
|
ret = (blockB->HGSS_shinyLeaves >> (attr - MON_DATA_SHINY_LEAF_A)) & 1;
|
|
break;
|
|
case MON_DATA_MOOD:
|
|
ret = blockD->mood;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void SetMonData(Pokemon *mon, int attr, void * value) {
|
|
u32 checksum;
|
|
if (!mon->box.party_lock) {
|
|
DECRYPT_PTY(mon);
|
|
DECRYPT_BOX(&mon->box);
|
|
checksum = CHECKSUM(&mon->box);
|
|
if (checksum != mon->box.checksum) {
|
|
GF_ASSERT(checksum == mon->box.checksum);
|
|
mon->box.checksum_fail = TRUE;
|
|
ENCRYPT_BOX(&mon->box);
|
|
return;
|
|
}
|
|
}
|
|
SetMonDataInternal(mon, attr, value);
|
|
if (!mon->box.party_lock) {
|
|
ENCRYPT_PTY(mon);
|
|
mon->box.checksum = CHECKSUM(&mon->box);
|
|
ENCRYPT_BOX(&mon->box);
|
|
}
|
|
}
|
|
|
|
static void SetMonDataInternal(Pokemon *mon, int attr, const void * value) {
|
|
#define VALUE(type) (*(const type *)value)
|
|
switch (attr) {
|
|
case MON_DATA_STATUS:
|
|
mon->party.status = VALUE(u32);
|
|
break;
|
|
case MON_DATA_LEVEL:
|
|
mon->party.level = VALUE(u8);
|
|
break;
|
|
case MON_DATA_CAPSULE:
|
|
mon->party.capsule = VALUE(u8);
|
|
break;
|
|
case MON_DATA_HP:
|
|
mon->party.hp = VALUE(u16);
|
|
break;
|
|
case MON_DATA_MAXHP:
|
|
mon->party.maxHp = VALUE(u16);
|
|
break;
|
|
case MON_DATA_ATK:
|
|
mon->party.atk = VALUE(u16);
|
|
break;
|
|
case MON_DATA_DEF:
|
|
mon->party.def = VALUE(u16);
|
|
break;
|
|
case MON_DATA_SPEED:
|
|
mon->party.speed = VALUE(u16);
|
|
break;
|
|
case MON_DATA_SPATK:
|
|
mon->party.spatk = VALUE(u16);
|
|
break;
|
|
case MON_DATA_SPDEF:
|
|
mon->party.spdef = VALUE(u16);
|
|
break;
|
|
case MON_DATA_MAIL_STRUCT:
|
|
Mail_Copy((const MAIL *)value, &mon->party.mail);
|
|
break;
|
|
case MON_DATA_SEAL_COORDS:
|
|
CopyCapsule((const CAPSULE *)value, &mon->party.sealCoords);
|
|
break;
|
|
default:
|
|
SetBoxMonDataInternal(&mon->box, attr, value);
|
|
break;
|
|
}
|
|
#undef VALUE
|
|
}
|
|
|
|
void SetBoxMonData(BoxPokemon *boxMon, int attr, void * value) {
|
|
u32 checksum;
|
|
if (!boxMon->box_lock) {
|
|
DECRYPT_BOX(boxMon);
|
|
checksum = CHECKSUM(boxMon);
|
|
if (checksum != boxMon->checksum) {
|
|
GF_ASSERT(checksum == boxMon->checksum);
|
|
boxMon->checksum_fail = TRUE;
|
|
ENCRYPT_BOX(boxMon);
|
|
return;
|
|
}
|
|
}
|
|
SetBoxMonDataInternal(boxMon, attr, value);
|
|
if (!boxMon->box_lock) {
|
|
boxMon->checksum = CHECKSUM(boxMon);
|
|
ENCRYPT_BOX(boxMon);
|
|
}
|
|
}
|
|
|
|
|
|
static void SetBoxMonDataInternal(BoxPokemon *boxMon, int attr, const void * value) {
|
|
#define VALUE(type) (*(const type *)value)
|
|
u64 mask;
|
|
u32 i;
|
|
u16 namebuf[POKEMON_NAME_LENGTH + 1];
|
|
u16 namebuf2[POKEMON_NAME_LENGTH + 1];
|
|
u16 namebuf3[POKEMON_NAME_LENGTH + 1];
|
|
String * speciesName;
|
|
|
|
PokemonDataBlockA *blockA = &GetSubstruct(boxMon, boxMon->pid, 0)->blockA;
|
|
PokemonDataBlockB *blockB = &GetSubstruct(boxMon, boxMon->pid, 1)->blockB;
|
|
PokemonDataBlockC *blockC = &GetSubstruct(boxMon, boxMon->pid, 2)->blockC;
|
|
PokemonDataBlockD *blockD = &GetSubstruct(boxMon, boxMon->pid, 3)->blockD;
|
|
|
|
switch (attr) {
|
|
case MON_DATA_PERSONALITY:
|
|
boxMon->pid = VALUE(u32);
|
|
break;
|
|
case MON_DATA_PARTY_LOCK:
|
|
GF_ASSERT(0);
|
|
boxMon->party_lock = VALUE(u8);
|
|
break;
|
|
case MON_DATA_BOX_LOCK:
|
|
GF_ASSERT(0);
|
|
boxMon->box_lock = VALUE(u8);
|
|
break;
|
|
case MON_DATA_CHECKSUM_FAILED:
|
|
boxMon->checksum_fail = VALUE(u8);
|
|
break;
|
|
case MON_DATA_CHECKSUM:
|
|
boxMon->checksum = VALUE(u16);
|
|
break;
|
|
case MON_DATA_SPECIES:
|
|
blockA->species = VALUE(u16);
|
|
break;
|
|
case MON_DATA_HELD_ITEM:
|
|
blockA->heldItem = VALUE(u16);
|
|
break;
|
|
case MON_DATA_OTID:
|
|
blockA->otID = VALUE(u32);
|
|
break;
|
|
case MON_DATA_EXPERIENCE:
|
|
blockA->exp = VALUE(u32);
|
|
break;
|
|
case MON_DATA_FRIENDSHIP:
|
|
blockA->friendship = VALUE(u8);
|
|
break;
|
|
case MON_DATA_ABILITY:
|
|
blockA->ability = VALUE(u8);
|
|
break;
|
|
case MON_DATA_MARKINGS:
|
|
blockA->markings = VALUE(u8);
|
|
break;
|
|
case MON_DATA_GAME_LANGUAGE:
|
|
blockA->originLanguage = VALUE(u8);
|
|
break;
|
|
case MON_DATA_HP_EV:
|
|
blockA->hpEV = VALUE(u8);
|
|
break;
|
|
case MON_DATA_ATK_EV:
|
|
blockA->atkEV = VALUE(u8);
|
|
break;
|
|
case MON_DATA_DEF_EV:
|
|
blockA->defEV = VALUE(u8);
|
|
break;
|
|
case MON_DATA_SPEED_EV:
|
|
blockA->spdEV = VALUE(u8);
|
|
break;
|
|
case MON_DATA_SPATK_EV:
|
|
blockA->spatkEV = VALUE(u8);
|
|
break;
|
|
case MON_DATA_SPDEF_EV:
|
|
blockA->spdefEV = VALUE(u8);
|
|
break;
|
|
case MON_DATA_COOL:
|
|
blockA->coolStat = VALUE(u8);
|
|
break;
|
|
case MON_DATA_BEAUTY:
|
|
blockA->beautyStat = VALUE(u8);
|
|
break;
|
|
case MON_DATA_CUTE:
|
|
blockA->cuteStat = VALUE(u8);
|
|
break;
|
|
case MON_DATA_SMART:
|
|
blockA->smartStat = VALUE(u8);
|
|
break;
|
|
case MON_DATA_TOUGH:
|
|
blockA->toughStat = VALUE(u8);
|
|
break;
|
|
case MON_DATA_SHEEN:
|
|
blockA->sheen = VALUE(u8);
|
|
break;
|
|
case MON_DATA_SINNOH_CHAMP_RIBBON:
|
|
case MON_DATA_ABILITY_RIBBON:
|
|
case MON_DATA_GREAT_ABILITY_RIBBON:
|
|
case MON_DATA_DOUBLE_ABILITY_RIBBON:
|
|
case MON_DATA_MULTI_ABILITY_RIBBON:
|
|
case MON_DATA_PAIR_ABILITY_RIBBON:
|
|
case MON_DATA_WORLD_ABILITY_RIBBON:
|
|
case MON_DATA_ALERT_RIBBON:
|
|
case MON_DATA_SHOCK_RIBBON:
|
|
case MON_DATA_DOWNCAST_RIBBON:
|
|
case MON_DATA_CARELESS_RIBBON:
|
|
case MON_DATA_RELAX_RIBBON:
|
|
case MON_DATA_SNOOZE_RIBBON:
|
|
case MON_DATA_SMILE_RIBBON:
|
|
case MON_DATA_GORGEOUS_RIBBON:
|
|
case MON_DATA_ROYAL_RIBBON:
|
|
case MON_DATA_GORGEOUS_ROYAL_RIBBON:
|
|
case MON_DATA_FOOTPRINT_RIBBON:
|
|
case MON_DATA_RECORD_RIBBON:
|
|
case MON_DATA_HISTORY_RIBBON:
|
|
case MON_DATA_LEGEND_RIBBON:
|
|
case MON_DATA_RED_RIBBON:
|
|
case MON_DATA_GREEN_RIBBON:
|
|
case MON_DATA_BLUE_RIBBON:
|
|
case MON_DATA_FESTIVAL_RIBBON:
|
|
case MON_DATA_CARNIVAL_RIBBON:
|
|
case MON_DATA_CLASSIC_RIBBON:
|
|
case MON_DATA_PREMIER_RIBBON:
|
|
case MON_DATA_SINNOH_RIBBON_53:
|
|
mask = 1 << (attr - MON_DATA_SINNOH_CHAMP_RIBBON);
|
|
if (VALUE(u8)) {
|
|
blockA->sinnohRibbons |= mask;
|
|
} else {
|
|
blockA->sinnohRibbons &= mask ^ 0xFFFFFFFF;
|
|
}
|
|
break;
|
|
case MON_DATA_MOVE1:
|
|
case MON_DATA_MOVE2:
|
|
case MON_DATA_MOVE3:
|
|
case MON_DATA_MOVE4:
|
|
blockB->moves[attr - MON_DATA_MOVE1] = VALUE(u16);
|
|
break;
|
|
case MON_DATA_MOVE1PP:
|
|
case MON_DATA_MOVE2PP:
|
|
case MON_DATA_MOVE3PP:
|
|
case MON_DATA_MOVE4PP:
|
|
blockB->movePP[attr - MON_DATA_MOVE1PP] = VALUE(u8);
|
|
break;
|
|
case MON_DATA_MOVE1PPUP:
|
|
case MON_DATA_MOVE2PPUP:
|
|
case MON_DATA_MOVE3PPUP:
|
|
case MON_DATA_MOVE4PPUP:
|
|
blockB->movePpUps[attr - MON_DATA_MOVE1PPUP] = VALUE(u8);
|
|
break;
|
|
case MON_DATA_HP_IV:
|
|
blockB->hpIV = VALUE(u8);
|
|
break;
|
|
case MON_DATA_ATK_IV:
|
|
blockB->atkIV = VALUE(u8);
|
|
break;
|
|
case MON_DATA_DEF_IV:
|
|
blockB->defIV = VALUE(u8);
|
|
break;
|
|
case MON_DATA_SPEED_IV:
|
|
blockB->spdIV = VALUE(u8);
|
|
break;
|
|
case MON_DATA_SPATK_IV:
|
|
blockB->spatkIV = VALUE(u8);
|
|
break;
|
|
case MON_DATA_SPDEF_IV:
|
|
blockB->spdefIV = VALUE(u8);
|
|
break;
|
|
case MON_DATA_IS_EGG:
|
|
blockB->isEgg = VALUE(u8);
|
|
break;
|
|
case MON_DATA_HAS_NICKNAME:
|
|
blockB->isNicknamed = VALUE(u8);
|
|
break;
|
|
case MON_DATA_HOENN_COOL_RIBBON:
|
|
case MON_DATA_HOENN_COOL_RIBBON_SUPER:
|
|
case MON_DATA_HOENN_COOL_RIBBON_HYPER:
|
|
case MON_DATA_HOENN_COOL_RIBBON_MASTER:
|
|
case MON_DATA_HOENN_BEAUTY_RIBBON:
|
|
case MON_DATA_HOENN_BEAUTY_RIBBON_SUPER:
|
|
case MON_DATA_HOENN_BEAUTY_RIBBON_HYPER:
|
|
case MON_DATA_HOENN_BEAUTY_RIBBON_MASTER:
|
|
case MON_DATA_HOENN_CUTE_RIBBON:
|
|
case MON_DATA_HOENN_CUTE_RIBBON_SUPER:
|
|
case MON_DATA_HOENN_CUTE_RIBBON_HYPER:
|
|
case MON_DATA_HOENN_CUTE_RIBBON_MASTER:
|
|
case MON_DATA_HOENN_SMART_RIBBON:
|
|
case MON_DATA_HOENN_SMART_RIBBON_SUPER:
|
|
case MON_DATA_HOENN_SMART_RIBBON_HYPER:
|
|
case MON_DATA_HOENN_SMART_RIBBON_MASTER:
|
|
case MON_DATA_HOENN_TOUGH_RIBBON:
|
|
case MON_DATA_HOENN_TOUGH_RIBBON_SUPER:
|
|
case MON_DATA_HOENN_TOUGH_RIBBON_HYPER:
|
|
case MON_DATA_HOENN_TOUGH_RIBBON_MASTER:
|
|
case MON_DATA_HOENN_CHAMPION_RIBBON:
|
|
case MON_DATA_HOENN_WINNING_RIBBON:
|
|
case MON_DATA_HOENN_VICTORY_RIBBON:
|
|
case MON_DATA_HOENN_ARTIST_RIBBON:
|
|
case MON_DATA_HOENN_EFFORT_RIBBON:
|
|
case MON_DATA_HOENN_MARINE_RIBBON:
|
|
case MON_DATA_HOENN_LAND_RIBBON:
|
|
case MON_DATA_HOENN_SKY_RIBBON:
|
|
case MON_DATA_HOENN_COUNTRY_RIBBON:
|
|
case MON_DATA_HOENN_NATIONAL_RIBBON:
|
|
case MON_DATA_HOENN_EARTH_RIBBON:
|
|
case MON_DATA_HOENN_WORLD_RIBBON:
|
|
mask = 1 << (attr - MON_DATA_HOENN_COOL_RIBBON);
|
|
if (VALUE(u8)) {
|
|
blockB->ribbonFlags |= mask;
|
|
} else {
|
|
blockB->ribbonFlags &= mask ^ 0xFFFFFFFF;
|
|
}
|
|
break;
|
|
case MON_DATA_FATEFUL_ENCOUNTER:
|
|
blockB->fatefulEncounter = VALUE(u8);
|
|
break;
|
|
case MON_DATA_GENDER:
|
|
blockB->gender = GetGenderBySpeciesAndPersonality(blockA->species, boxMon->pid);
|
|
break;
|
|
case MON_DATA_FORME:
|
|
blockB->alternateForm = VALUE(u8);
|
|
break;
|
|
case MON_DATA_RESERVED_113:
|
|
blockB->unk_19_6 = VALUE(u8);
|
|
break;
|
|
case MON_DATA_RESERVED_114:
|
|
blockB->Unused = VALUE(u16);
|
|
break;
|
|
case MON_DATA_NICKNAME_2:
|
|
GetSpeciesNameIntoArray(blockA->species, HEAP_ID_0, namebuf);
|
|
blockB->isNicknamed = StringNotEqual(namebuf, value);
|
|
// fallthrough
|
|
case MON_DATA_NICKNAME:
|
|
for (i = 0; i < POKEMON_NAME_LENGTH + 1; i++) {
|
|
blockC->nickname[i] = VALUE(u16); value = (void *const )((char *)value + 2);
|
|
}
|
|
break;
|
|
case MON_DATA_NICKNAME_4:
|
|
GetSpeciesNameIntoArray(blockA->species, HEAP_ID_0, namebuf2);
|
|
CopyStringToU16Array(value, namebuf3, POKEMON_NAME_LENGTH + 1);
|
|
blockB->isNicknamed = StringNotEqual(namebuf2, namebuf3);
|
|
// fallthrough
|
|
case MON_DATA_NICKNAME_3:
|
|
CopyStringToU16Array(value, blockC->nickname, POKEMON_NAME_LENGTH + 1);
|
|
break;
|
|
case MON_DATA_UNK_121:
|
|
blockC->Unused = VALUE(u8);
|
|
break;
|
|
case MON_DATA_GAME_VERSION:
|
|
blockC->originGame = VALUE(u8);
|
|
break;
|
|
case MON_DATA_COOL_RIBBON:
|
|
case MON_DATA_COOL_RIBBON_GREAT:
|
|
case MON_DATA_COOL_RIBBON_ULTRA:
|
|
case MON_DATA_COOL_RIBBON_MASTER:
|
|
case MON_DATA_BEAUTY_RIBBON:
|
|
case MON_DATA_BEAUTY_RIBBON_GREAT:
|
|
case MON_DATA_BEAUTY_RIBBON_ULTRA:
|
|
case MON_DATA_BEAUTY_RIBBON_MASTER:
|
|
case MON_DATA_CUTE_RIBBON:
|
|
case MON_DATA_CUTE_RIBBON_GREAT:
|
|
case MON_DATA_CUTE_RIBBON_ULTRA:
|
|
case MON_DATA_CUTE_RIBBON_MASTER:
|
|
case MON_DATA_SMART_RIBBON:
|
|
case MON_DATA_SMART_RIBBON_GREAT:
|
|
case MON_DATA_SMART_RIBBON_ULTRA:
|
|
case MON_DATA_SMART_RIBBON_MASTER:
|
|
case MON_DATA_TOUGH_RIBBON:
|
|
case MON_DATA_TOUGH_RIBBON_GREAT:
|
|
case MON_DATA_TOUGH_RIBBON_ULTRA:
|
|
case MON_DATA_TOUGH_RIBBON_MASTER:
|
|
case MON_DATA_SINNOH_RIBBON_143:
|
|
mask = 1 << (attr - MON_DATA_COOL_RIBBON);
|
|
if (VALUE(u8)) {
|
|
blockC->sinnohRibbons2 |= mask;
|
|
} else {
|
|
blockC->sinnohRibbons2 &= mask ^ 0xFFFFFFFFFFFFFFFF;
|
|
}
|
|
break;
|
|
case MON_DATA_OT_NAME:
|
|
for (i = 0; i < PLAYER_NAME_LENGTH + 1; i++) {
|
|
blockD->otTrainerName[i] = VALUE(u16); value = (void *)((char *)value + 2);
|
|
}
|
|
break;
|
|
case MON_DATA_OT_NAME_2:
|
|
CopyStringToU16Array(value, blockD->otTrainerName, PLAYER_NAME_LENGTH + 1);
|
|
break;
|
|
case MON_DATA_EGG_MET_YEAR:
|
|
blockD->dateEggReceived[0] = VALUE(u8);
|
|
break;
|
|
case MON_DATA_EGG_MET_MONTH:
|
|
blockD->dateEggReceived[1] = VALUE(u8);
|
|
break;
|
|
case MON_DATA_EGG_MET_DAY:
|
|
blockD->dateEggReceived[2] = VALUE(u8);
|
|
break;
|
|
case MON_DATA_MET_YEAR:
|
|
blockD->dateMet[0] = VALUE(u8);
|
|
break;
|
|
case MON_DATA_MET_MONTH:
|
|
blockD->dateMet[1] = VALUE(u8);
|
|
break;
|
|
case MON_DATA_MET_DAY:
|
|
blockD->dateMet[2] = VALUE(u8);
|
|
break;
|
|
case MON_DATA_EGG_MET_LOCATION:
|
|
case MON_DATA_HGSS_EGG_MET_LOCATION:
|
|
if (VALUE(u16) == MAPSEC_MYSTERY_ZONE || LocationIsDiamondPearlCompatible(VALUE(u16)) == TRUE) {
|
|
blockD->DP_EggLocation = VALUE(u16);
|
|
blockB->Platinum_EggLocation = VALUE(u16);
|
|
} else {
|
|
blockD->DP_EggLocation = METLOC_FARAWAY_PLACE;
|
|
blockB->Platinum_EggLocation = VALUE(u16);
|
|
}
|
|
break;
|
|
case MON_DATA_MET_LOCATION:
|
|
case MON_DATA_HGSS_MET_LOCATION:
|
|
if (VALUE(u16) == MAPSEC_MYSTERY_ZONE || LocationIsDiamondPearlCompatible(VALUE(u16)) == TRUE) {
|
|
blockD->DP_MetLocation = VALUE(u16);
|
|
blockB->Platinum_MetLocation = VALUE(u16);
|
|
} else {
|
|
blockD->DP_MetLocation = METLOC_FARAWAY_PLACE;
|
|
blockB->Platinum_MetLocation = VALUE(u16);
|
|
}
|
|
break;
|
|
case MON_DATA_POKERUS:
|
|
blockD->pokerus = VALUE(u8);
|
|
break;
|
|
case MON_DATA_POKEBALL:
|
|
if (VALUE(u16) >= ITEM_MASTER_BALL && VALUE(u16) <= ITEM_CHERISH_BALL) {
|
|
blockD->HGSS_Pokeball = VALUE(u16);
|
|
blockD->pokeball = VALUE(u16);
|
|
} else if (VALUE(u16) >= ITEM_FAST_BALL && VALUE(u16) <= ITEM_SPORT_BALL) {
|
|
blockD->HGSS_Pokeball = VALUE(u16) - (ITEM_FAST_BALL - BALL_FAST);
|
|
blockD->pokeball = BALL_POKE;
|
|
} else {
|
|
GF_ASSERT(VALUE(u16) != 0);
|
|
}
|
|
break;
|
|
case MON_DATA_MET_LEVEL:
|
|
blockD->metLevel = VALUE(u8);
|
|
break;
|
|
case MON_DATA_MET_GENDER:
|
|
blockD->metGender = VALUE(u8);
|
|
break;
|
|
case MON_DATA_ENCOUNTER_TYPE:
|
|
blockD->encounterType = VALUE(u8);
|
|
break;
|
|
case MON_DATA_IVS_WORD:
|
|
blockB->hpIV = (VALUE(u32) >> 0) & 0x1F;
|
|
blockB->atkIV = (VALUE(u32) >> 5) & 0x1F;
|
|
blockB->defIV = (VALUE(u32) >> 10) & 0x1F;
|
|
blockB->spdIV = (VALUE(u32) >> 15) & 0x1F;
|
|
blockB->spatkIV = (VALUE(u32) >> 20) & 0x1F;
|
|
blockB->spdefIV = (VALUE(u32) >> 25) & 0x1F;
|
|
break;
|
|
case MON_DATA_SPECIES_NAME:
|
|
speciesName = GetSpeciesName(blockA->species, HEAP_ID_0);
|
|
CopyStringToU16Array(speciesName, blockC->nickname, POKEMON_NAME_LENGTH + 1);
|
|
String_Delete(speciesName);
|
|
break;
|
|
case MON_DATA_SHINY_LEAF_A:
|
|
case MON_DATA_SHINY_LEAF_B:
|
|
case MON_DATA_SHINY_LEAF_C:
|
|
case MON_DATA_SHINY_LEAF_D:
|
|
case MON_DATA_SHINY_LEAF_E:
|
|
case MON_DATA_SHINY_LEAF_CROWN:
|
|
if (value == NULL) {
|
|
blockB->HGSS_shinyLeaves |= 1 << (attr - MON_DATA_SHINY_LEAF_A);
|
|
} else if (VALUE(u8) != 0) {
|
|
blockB->HGSS_shinyLeaves |= 1 << (attr - MON_DATA_SHINY_LEAF_A);
|
|
} else {
|
|
blockB->HGSS_shinyLeaves &= (1 << (attr - MON_DATA_SHINY_LEAF_A)) ^ 0x3F;
|
|
}
|
|
break;
|
|
case MON_DATA_MOOD:
|
|
blockD->mood = VALUE(u8);
|
|
break;
|
|
}
|
|
#undef VALUE
|
|
}
|
|
|
|
void AddMonData(Pokemon *mon, int attr, int value) {
|
|
u32 checksum;
|
|
if (!mon->box.party_lock) {
|
|
DECRYPT_PTY(mon);
|
|
DECRYPT_BOX(&mon->box);
|
|
checksum = CHECKSUM(&mon->box);
|
|
if (checksum != mon->box.checksum) {
|
|
GF_ASSERT(checksum == mon->box.checksum);
|
|
ENCRYPT_BOX(&mon->box);
|
|
return;
|
|
}
|
|
}
|
|
AddMonDataInternal(mon, attr, value);
|
|
if (!mon->box.party_lock) {
|
|
ENCRYPT_PTY(mon);
|
|
mon->box.checksum = CHECKSUM(&mon->box);
|
|
ENCRYPT_BOX(&mon->box);
|
|
}
|
|
}
|
|
|
|
static void AddMonDataInternal(Pokemon *mon, int attr, int value) {
|
|
s32 maxHp;
|
|
switch (attr) {
|
|
case MON_DATA_HP:
|
|
maxHp = mon->party.maxHp;
|
|
if ((s32)(mon->party.hp + value) > maxHp) {
|
|
mon->party.hp = (u16)maxHp;
|
|
} else {
|
|
mon->party.hp += value;
|
|
}
|
|
break;
|
|
case MON_DATA_STATUS:
|
|
case MON_DATA_LEVEL:
|
|
case MON_DATA_CAPSULE:
|
|
case MON_DATA_MAXHP:
|
|
case MON_DATA_ATK:
|
|
case MON_DATA_DEF:
|
|
case MON_DATA_SPEED:
|
|
case MON_DATA_SPATK:
|
|
case MON_DATA_SPDEF:
|
|
case MON_DATA_MAIL_STRUCT:
|
|
// case MON_DATA_SEAL_COORDS:
|
|
GF_ASSERT(0);
|
|
break;
|
|
default:
|
|
AddBoxMonDataInternal(&mon->box, attr, value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void AddBoxMonDataInternal(BoxPokemon *boxMon, int attr, int value) {
|
|
PokemonDataBlockA *blockA = &GetSubstruct(boxMon, boxMon->pid, 0)->blockA;
|
|
PokemonDataBlockB *blockB = &GetSubstruct(boxMon, boxMon->pid, 1)->blockB;
|
|
PokemonDataBlockC *blockC = &GetSubstruct(boxMon, boxMon->pid, 2)->blockC;
|
|
PokemonDataBlockD *blockD = &GetSubstruct(boxMon, boxMon->pid, 3)->blockD;
|
|
|
|
switch (attr) {
|
|
case MON_DATA_EXPERIENCE:
|
|
if (blockA->exp + value > GetMonExpBySpeciesAndLevel(blockA->species, 100)) {
|
|
blockA->exp = GetMonExpBySpeciesAndLevel(blockA->species, 100);
|
|
} else {
|
|
blockA->exp += value;
|
|
}
|
|
break;
|
|
case MON_DATA_FRIENDSHIP: {
|
|
int friendship = blockA->friendship;
|
|
if (friendship + value > FRIENDSHIP_MAX) {
|
|
friendship = FRIENDSHIP_MAX;
|
|
}
|
|
friendship += value;
|
|
if (friendship < 0) {
|
|
friendship = 0;
|
|
}
|
|
blockA->friendship = friendship;
|
|
}
|
|
break;
|
|
case MON_DATA_HP_EV:
|
|
blockA->hpEV += value;
|
|
break;
|
|
case MON_DATA_ATK_EV:
|
|
blockA->atkEV += value;
|
|
break;
|
|
case MON_DATA_DEF_EV:
|
|
blockA->defEV += value;
|
|
break;
|
|
case MON_DATA_SPEED_EV:
|
|
blockA->spdEV += value;
|
|
break;
|
|
case MON_DATA_SPATK_EV:
|
|
blockA->spatkEV += value;
|
|
break;
|
|
case MON_DATA_SPDEF_EV:
|
|
blockA->spdefEV += value;
|
|
break;
|
|
case MON_DATA_COOL:
|
|
if (blockA->coolStat + value > 255) {
|
|
blockA->coolStat = 255;
|
|
} else {
|
|
blockA->coolStat += value;
|
|
}
|
|
break;
|
|
case MON_DATA_BEAUTY:
|
|
if (blockA->beautyStat + value > 255) {
|
|
blockA->beautyStat = 255;
|
|
} else {
|
|
blockA->beautyStat += value;
|
|
}
|
|
break;
|
|
case MON_DATA_CUTE:
|
|
if (blockA->cuteStat + value > 255) {
|
|
blockA->cuteStat = 255;
|
|
} else {
|
|
blockA->cuteStat += value;
|
|
}
|
|
break;
|
|
case MON_DATA_SMART:
|
|
if (blockA->smartStat + value > 255) {
|
|
blockA->smartStat = 255;
|
|
} else {
|
|
blockA->smartStat += value;
|
|
}
|
|
break;
|
|
case MON_DATA_TOUGH:
|
|
if (blockA->toughStat + value > 255) {
|
|
blockA->toughStat = 255;
|
|
} else {
|
|
blockA->toughStat += value;
|
|
}
|
|
break;
|
|
case MON_DATA_SHEEN:
|
|
if (blockA->sheen + value > 255) {
|
|
blockA->sheen = 255;
|
|
} else {
|
|
blockA->sheen += value;
|
|
}
|
|
break;
|
|
case MON_DATA_MOVE1PP:
|
|
case MON_DATA_MOVE2PP:
|
|
case MON_DATA_MOVE3PP:
|
|
case MON_DATA_MOVE4PP:
|
|
if (blockB->movePP[attr - MON_DATA_MOVE1PP] + value > GetMoveMaxPP(blockB->moves[attr - MON_DATA_MOVE1PP], blockB->movePpUps[attr - MON_DATA_MOVE1PP])) {
|
|
blockB->movePP[attr - MON_DATA_MOVE1PP] = (u8)GetMoveMaxPP(blockB->moves[attr - MON_DATA_MOVE1PP],
|
|
blockB->movePpUps[attr - MON_DATA_MOVE1PP]);
|
|
} else {
|
|
blockB->movePP[attr - MON_DATA_MOVE1PP] += value;
|
|
}
|
|
break;
|
|
case MON_DATA_MOVE1PPUP:
|
|
case MON_DATA_MOVE2PPUP:
|
|
case MON_DATA_MOVE3PPUP:
|
|
case MON_DATA_MOVE4PPUP:
|
|
if (blockB->movePpUps[attr - MON_DATA_MOVE1PPUP] + value > 3) {
|
|
blockB->movePpUps[attr - MON_DATA_MOVE1PPUP] = 3;
|
|
} else {
|
|
blockB->movePpUps[attr - MON_DATA_MOVE1PPUP] += value;
|
|
}
|
|
break;
|
|
case MON_DATA_MOVE1MAXPP:
|
|
case MON_DATA_MOVE2MAXPP:
|
|
case MON_DATA_MOVE3MAXPP:
|
|
case MON_DATA_MOVE4MAXPP:
|
|
break;
|
|
case MON_DATA_HP_IV:
|
|
if (blockB->hpIV + value > 31) {
|
|
blockB->hpIV = 31;
|
|
} else {
|
|
blockB->hpIV += value;
|
|
}
|
|
break;
|
|
case MON_DATA_ATK_IV:
|
|
if (blockB->atkIV + value > 31) {
|
|
blockB->atkIV = 31;
|
|
} else {
|
|
blockB->atkIV += value;
|
|
}
|
|
break;
|
|
case MON_DATA_DEF_IV:
|
|
if (blockB->defIV + value > 31) {
|
|
blockB->defIV = 31;
|
|
} else {
|
|
blockB->defIV += value;
|
|
}
|
|
break;
|
|
case MON_DATA_SPEED_IV:
|
|
if (blockB->spdIV + value > 31) {
|
|
blockB->spdIV = 31;
|
|
} else {
|
|
blockB->spdIV += value;
|
|
}
|
|
break;
|
|
case MON_DATA_SPATK_IV:
|
|
if (blockB->spatkIV + value > 31) {
|
|
blockB->spatkIV = 31;
|
|
} else {
|
|
blockB->spatkIV += value;
|
|
}
|
|
break;
|
|
case MON_DATA_SPDEF_IV:
|
|
if (blockB->spdefIV + value > 31) {
|
|
blockB->spdefIV = 31;
|
|
} else {
|
|
blockB->spdefIV += value;
|
|
}
|
|
break;
|
|
case MON_DATA_PERSONALITY:
|
|
case MON_DATA_PARTY_LOCK:
|
|
case MON_DATA_BOX_LOCK:
|
|
case MON_DATA_CHECKSUM_FAILED:
|
|
case MON_DATA_CHECKSUM:
|
|
case MON_DATA_SPECIES:
|
|
case MON_DATA_HELD_ITEM:
|
|
case MON_DATA_OTID:
|
|
case MON_DATA_ABILITY:
|
|
case MON_DATA_MARKINGS:
|
|
case MON_DATA_GAME_LANGUAGE:
|
|
case MON_DATA_SINNOH_CHAMP_RIBBON:
|
|
case MON_DATA_ABILITY_RIBBON:
|
|
case MON_DATA_GREAT_ABILITY_RIBBON:
|
|
case MON_DATA_DOUBLE_ABILITY_RIBBON:
|
|
case MON_DATA_MULTI_ABILITY_RIBBON:
|
|
case MON_DATA_PAIR_ABILITY_RIBBON:
|
|
case MON_DATA_WORLD_ABILITY_RIBBON:
|
|
case MON_DATA_ALERT_RIBBON:
|
|
case MON_DATA_SHOCK_RIBBON:
|
|
case MON_DATA_DOWNCAST_RIBBON:
|
|
case MON_DATA_CARELESS_RIBBON:
|
|
case MON_DATA_RELAX_RIBBON:
|
|
case MON_DATA_SNOOZE_RIBBON:
|
|
case MON_DATA_SMILE_RIBBON:
|
|
case MON_DATA_GORGEOUS_RIBBON:
|
|
case MON_DATA_ROYAL_RIBBON:
|
|
case MON_DATA_GORGEOUS_ROYAL_RIBBON:
|
|
case MON_DATA_FOOTPRINT_RIBBON:
|
|
case MON_DATA_RECORD_RIBBON:
|
|
case MON_DATA_HISTORY_RIBBON:
|
|
case MON_DATA_LEGEND_RIBBON:
|
|
case MON_DATA_RED_RIBBON:
|
|
case MON_DATA_GREEN_RIBBON:
|
|
case MON_DATA_BLUE_RIBBON:
|
|
case MON_DATA_FESTIVAL_RIBBON:
|
|
case MON_DATA_CARNIVAL_RIBBON:
|
|
case MON_DATA_CLASSIC_RIBBON:
|
|
case MON_DATA_PREMIER_RIBBON:
|
|
case MON_DATA_SINNOH_RIBBON_53:
|
|
case MON_DATA_MOVE1:
|
|
case MON_DATA_MOVE2:
|
|
case MON_DATA_MOVE3:
|
|
case MON_DATA_MOVE4:
|
|
case MON_DATA_IS_EGG:
|
|
case MON_DATA_HAS_NICKNAME:
|
|
case MON_DATA_HOENN_COOL_RIBBON:
|
|
case MON_DATA_HOENN_COOL_RIBBON_SUPER:
|
|
case MON_DATA_HOENN_COOL_RIBBON_HYPER:
|
|
case MON_DATA_HOENN_COOL_RIBBON_MASTER:
|
|
case MON_DATA_HOENN_BEAUTY_RIBBON:
|
|
case MON_DATA_HOENN_BEAUTY_RIBBON_SUPER:
|
|
case MON_DATA_HOENN_BEAUTY_RIBBON_HYPER:
|
|
case MON_DATA_HOENN_BEAUTY_RIBBON_MASTER:
|
|
case MON_DATA_HOENN_CUTE_RIBBON:
|
|
case MON_DATA_HOENN_CUTE_RIBBON_SUPER:
|
|
case MON_DATA_HOENN_CUTE_RIBBON_HYPER:
|
|
case MON_DATA_HOENN_CUTE_RIBBON_MASTER:
|
|
case MON_DATA_HOENN_SMART_RIBBON:
|
|
case MON_DATA_HOENN_SMART_RIBBON_SUPER:
|
|
case MON_DATA_HOENN_SMART_RIBBON_HYPER:
|
|
case MON_DATA_HOENN_SMART_RIBBON_MASTER:
|
|
case MON_DATA_HOENN_TOUGH_RIBBON:
|
|
case MON_DATA_HOENN_TOUGH_RIBBON_SUPER:
|
|
case MON_DATA_HOENN_TOUGH_RIBBON_HYPER:
|
|
case MON_DATA_HOENN_TOUGH_RIBBON_MASTER:
|
|
case MON_DATA_HOENN_CHAMPION_RIBBON:
|
|
case MON_DATA_HOENN_WINNING_RIBBON:
|
|
case MON_DATA_HOENN_VICTORY_RIBBON:
|
|
case MON_DATA_HOENN_ARTIST_RIBBON:
|
|
case MON_DATA_HOENN_EFFORT_RIBBON:
|
|
case MON_DATA_HOENN_MARINE_RIBBON:
|
|
case MON_DATA_HOENN_LAND_RIBBON:
|
|
case MON_DATA_HOENN_SKY_RIBBON:
|
|
case MON_DATA_HOENN_COUNTRY_RIBBON:
|
|
case MON_DATA_HOENN_NATIONAL_RIBBON:
|
|
case MON_DATA_HOENN_EARTH_RIBBON:
|
|
case MON_DATA_HOENN_WORLD_RIBBON:
|
|
case MON_DATA_FATEFUL_ENCOUNTER:
|
|
case MON_DATA_GENDER:
|
|
case MON_DATA_FORME:
|
|
case MON_DATA_RESERVED_113:
|
|
case MON_DATA_RESERVED_114:
|
|
case MON_DATA_NICKNAME:
|
|
case MON_DATA_NICKNAME_2:
|
|
case MON_DATA_NICKNAME_3:
|
|
case MON_DATA_NICKNAME_4:
|
|
case MON_DATA_UNK_121:
|
|
case MON_DATA_GAME_VERSION:
|
|
case MON_DATA_COOL_RIBBON:
|
|
case MON_DATA_COOL_RIBBON_GREAT:
|
|
case MON_DATA_COOL_RIBBON_ULTRA:
|
|
case MON_DATA_COOL_RIBBON_MASTER:
|
|
case MON_DATA_BEAUTY_RIBBON:
|
|
case MON_DATA_BEAUTY_RIBBON_GREAT:
|
|
case MON_DATA_BEAUTY_RIBBON_ULTRA:
|
|
case MON_DATA_BEAUTY_RIBBON_MASTER:
|
|
case MON_DATA_CUTE_RIBBON:
|
|
case MON_DATA_CUTE_RIBBON_GREAT:
|
|
case MON_DATA_CUTE_RIBBON_ULTRA:
|
|
case MON_DATA_CUTE_RIBBON_MASTER:
|
|
case MON_DATA_SMART_RIBBON:
|
|
case MON_DATA_SMART_RIBBON_GREAT:
|
|
case MON_DATA_SMART_RIBBON_ULTRA:
|
|
case MON_DATA_SMART_RIBBON_MASTER:
|
|
case MON_DATA_TOUGH_RIBBON:
|
|
case MON_DATA_TOUGH_RIBBON_GREAT:
|
|
case MON_DATA_TOUGH_RIBBON_ULTRA:
|
|
case MON_DATA_TOUGH_RIBBON_MASTER:
|
|
case MON_DATA_SINNOH_RIBBON_143:
|
|
case MON_DATA_OT_NAME:
|
|
case MON_DATA_OT_NAME_2:
|
|
case MON_DATA_EGG_MET_YEAR:
|
|
case MON_DATA_EGG_MET_MONTH:
|
|
case MON_DATA_EGG_MET_DAY:
|
|
case MON_DATA_MET_YEAR:
|
|
case MON_DATA_MET_MONTH:
|
|
case MON_DATA_MET_DAY:
|
|
case MON_DATA_EGG_MET_LOCATION:
|
|
case MON_DATA_HGSS_EGG_MET_LOCATION:
|
|
case MON_DATA_MET_LOCATION:
|
|
case MON_DATA_HGSS_MET_LOCATION:
|
|
case MON_DATA_POKERUS:
|
|
case MON_DATA_POKEBALL:
|
|
case MON_DATA_MET_LEVEL:
|
|
case MON_DATA_MET_GENDER:
|
|
case MON_DATA_ENCOUNTER_TYPE:
|
|
case MON_DATA_RESERVED_159:
|
|
case MON_DATA_STATUS:
|
|
case MON_DATA_LEVEL:
|
|
case MON_DATA_CAPSULE:
|
|
case MON_DATA_HP:
|
|
case MON_DATA_MAXHP:
|
|
case MON_DATA_ATK:
|
|
case MON_DATA_DEF:
|
|
case MON_DATA_SPEED:
|
|
case MON_DATA_SPATK:
|
|
case MON_DATA_SPDEF:
|
|
case MON_DATA_MAIL_STRUCT:
|
|
case MON_DATA_SEAL_COORDS:
|
|
case MON_DATA_SPECIES_EXISTS:
|
|
case MON_DATA_SANITY_IS_EGG:
|
|
case MON_DATA_SPECIES2:
|
|
case MON_DATA_IVS_WORD:
|
|
case MON_DATA_UNK_176:
|
|
case MON_DATA_TYPE_1:
|
|
case MON_DATA_TYPE_2:
|
|
case MON_DATA_SPECIES_NAME:
|
|
case MON_DATA_DP_POKEBALL:
|
|
case MON_DATA_SHINY_LEAF_A:
|
|
case MON_DATA_SHINY_LEAF_B:
|
|
case MON_DATA_SHINY_LEAF_C:
|
|
case MON_DATA_SHINY_LEAF_D:
|
|
case MON_DATA_SHINY_LEAF_E:
|
|
case MON_DATA_SHINY_LEAF_CROWN:
|
|
default:
|
|
GF_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
BASE_STATS *AllocAndLoadMonPersonal_HandleAlternateForme(int species, int forme, HeapID heap_id) {
|
|
BASE_STATS *ret = AllocFromHeap(heap_id, sizeof(BASE_STATS));
|
|
LoadMonBaseStats_HandleAlternateForme(species, forme, ret);
|
|
return ret;
|
|
}
|
|
|
|
BASE_STATS *AllocAndLoadMonPersonal(int species, HeapID heap_id) {
|
|
BASE_STATS *ret = AllocFromHeap(heap_id, sizeof(BASE_STATS));
|
|
LoadMonPersonal(species, ret);
|
|
return ret;
|
|
}
|
|
|
|
int GetPersonalAttr(const BASE_STATS *baseStats, BaseStat attr) {
|
|
int ret;
|
|
GF_ASSERT(baseStats != NULL);
|
|
switch (attr) {
|
|
case BASE_HP:
|
|
ret = baseStats->hp;
|
|
break;
|
|
case BASE_ATK:
|
|
ret = baseStats->atk;
|
|
break;
|
|
case BASE_DEF:
|
|
ret = baseStats->def;
|
|
break;
|
|
case BASE_SPEED:
|
|
ret = baseStats->speed;
|
|
break;
|
|
case BASE_SPATK:
|
|
ret = baseStats->spatk;
|
|
break;
|
|
case BASE_SPDEF:
|
|
ret = baseStats->spdef;
|
|
break;
|
|
case BASE_TYPE1:
|
|
ret = baseStats->types[0];
|
|
break;
|
|
case BASE_TYPE2:
|
|
ret = baseStats->types[1];
|
|
break;
|
|
case BASE_CATCH_RATE:
|
|
ret = baseStats->catchRate;
|
|
break;
|
|
case BASE_EXP_YIELD:
|
|
ret = baseStats->expYield;
|
|
break;
|
|
case BASE_HP_YIELD:
|
|
ret = baseStats->hp_yield;
|
|
break;
|
|
case BASE_ATK_YIELD:
|
|
ret = baseStats->atk_yield;
|
|
break;
|
|
case BASE_DEF_YIELD:
|
|
ret = baseStats->def_yield;
|
|
break;
|
|
case BASE_SPEED_YIELD:
|
|
ret = baseStats->speed_yield;
|
|
break;
|
|
case BASE_SPATK_YIELD:
|
|
ret = baseStats->spatk_yield;
|
|
break;
|
|
case BASE_SPDEF_YIELD:
|
|
ret = baseStats->spdef_yield;
|
|
break;
|
|
case BASE_ITEM_1:
|
|
ret = baseStats->item1;
|
|
break;
|
|
case BASE_ITEM_2:
|
|
ret = baseStats->item2;
|
|
break;
|
|
case BASE_GENDER_RATIO:
|
|
ret = baseStats->genderRatio;
|
|
break;
|
|
case BASE_EGG_CYCLES:
|
|
ret = baseStats->eggCycles;
|
|
break;
|
|
case BASE_FRIENDSHIP:
|
|
ret = baseStats->friendship;
|
|
break;
|
|
case BASE_GROWTH_RATE:
|
|
ret = baseStats->growthRate;
|
|
break;
|
|
case BASE_EGG_GROUP_1:
|
|
ret = baseStats->eggGroups[0];
|
|
break;
|
|
case BASE_EGG_GROUP_2:
|
|
ret = baseStats->eggGroups[1];
|
|
break;
|
|
case BASE_ABILITY_1:
|
|
ret = baseStats->abilities[0];
|
|
break;
|
|
case BASE_ABILITY_2:
|
|
ret = baseStats->abilities[1];
|
|
break;
|
|
case BASE_GREAT_MARSH_RATE:
|
|
ret = baseStats->greatMarshRate;
|
|
break;
|
|
case BASE_COLOR:
|
|
ret = baseStats->color;
|
|
break;
|
|
case BASE_FLIP:
|
|
ret = baseStats->flip;
|
|
break;
|
|
case BASE_TMHM_1:
|
|
ret = (int)baseStats->unk1C;
|
|
break;
|
|
case BASE_TMHM_2:
|
|
ret = (int)baseStats->unk20;
|
|
break;
|
|
case BASE_TMHM_3:
|
|
ret = (int)baseStats->unk24;
|
|
break;
|
|
case BASE_TMHM_4:
|
|
ret = (int)baseStats->unk28;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void FreeMonPersonal(BASE_STATS * personal) {
|
|
GF_ASSERT(personal != NULL);
|
|
FreeToHeap(personal);
|
|
}
|
|
|
|
int GetMonBaseStat_HandleAlternateForme(int species, int forme, BaseStat attr) {
|
|
int ret;
|
|
BASE_STATS * personal = AllocAndLoadMonPersonal(ResolveMonForme(species, forme), HEAP_ID_0);
|
|
ret = GetPersonalAttr(personal, attr);
|
|
FreeMonPersonal(personal);
|
|
return ret;
|
|
}
|
|
|
|
int GetMonBaseStat(int species, BaseStat attr) {
|
|
int ret;
|
|
BASE_STATS * personal = AllocAndLoadMonPersonal(species, HEAP_ID_0);
|
|
ret = GetPersonalAttr(personal, attr);
|
|
FreeMonPersonal(personal);
|
|
return ret;
|
|
}
|
|
|
|
int GetMonBaseStatEx_HandleAlternateForme(NARC *narc, int species, int forme, BaseStat attr) {
|
|
int resolved = ResolveMonForme(species, forme);
|
|
int ret;
|
|
BASE_STATS *buf = AllocFromHeap(HEAP_ID_0, sizeof(BASE_STATS));
|
|
NARC_ReadWholeMember(narc, resolved, buf);
|
|
ret = GetPersonalAttr(buf, attr);
|
|
FreeToHeap(buf);
|
|
return ret;
|
|
}
|
|
|
|
u8 GetPercentProgressTowardsNextLevel(Pokemon *mon) {
|
|
BOOL decry = AcquireMonLock(mon);
|
|
u16 species = (u16)GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
u8 level = (u8)GetMonData(mon, MON_DATA_LEVEL, NULL);
|
|
u32 lo = GetMonExpBySpeciesAndLevel(species, level);
|
|
u32 hi = GetMonExpBySpeciesAndLevel(species, level + 1);
|
|
u32 cur = GetMonData(mon, MON_DATA_EXPERIENCE, NULL);
|
|
ReleaseMonLock(mon, decry);
|
|
return (u8)(100 * (cur - lo) / (hi - lo));
|
|
}
|
|
|
|
u32 CalcMonExpToNextLevel(Pokemon *mon) {
|
|
return CalcBoxMonExpToNextLevel(&mon->box);
|
|
}
|
|
|
|
u32 CalcBoxMonExpToNextLevel(BoxPokemon *boxMon) {
|
|
u16 species = (u16)GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
|
|
u16 level = (u16)(CalcBoxMonLevel(boxMon) + 1);
|
|
u32 cur = GetBoxMonData(boxMon, MON_DATA_EXPERIENCE, NULL);
|
|
u32 hi = GetMonExpBySpeciesAndLevel(species, level);
|
|
return hi - cur;
|
|
}
|
|
|
|
u32 GetMonBaseExperienceAtCurrentLevel(Pokemon *mon) {
|
|
int species = (int)GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
int level = (int)GetMonData(mon, MON_DATA_LEVEL, NULL);
|
|
return GetMonExpBySpeciesAndLevel(species, level);
|
|
}
|
|
|
|
u32 GetMonExpBySpeciesAndLevel(int species, int level) {
|
|
return GetExpByGrowthRateAndLevel(GetMonBaseStat(species, BASE_GROWTH_RATE), level);
|
|
}
|
|
|
|
void LoadGrowthTable(int growthRate, u32 * dest) {
|
|
GF_ASSERT(growthRate < 8);
|
|
ReadWholeNarcMemberByIdPair(dest, NARC_poketool_personal_growtbl, growthRate);
|
|
}
|
|
|
|
u32 GetExpByGrowthRateAndLevel(int growthRate, int level) {
|
|
u32 * table;
|
|
u32 ret;
|
|
GF_ASSERT(growthRate < 8);
|
|
GF_ASSERT(level <= MAX_LEVEL + 1);
|
|
table = (u32 *)AllocFromHeap(HEAP_ID_0, (MAX_LEVEL + 1) * sizeof(u32));
|
|
LoadGrowthTable(growthRate, table);
|
|
ret = table[level];
|
|
FreeToHeap(table);
|
|
return ret;
|
|
}
|
|
|
|
int CalcMonLevel(Pokemon *mon) {
|
|
return CalcBoxMonLevel(&mon->box);
|
|
}
|
|
|
|
int CalcBoxMonLevel(BoxPokemon *boxMon) {
|
|
BOOL decry = AcquireBoxMonLock(boxMon);
|
|
int species = (int)GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
|
|
int exp = (int)GetBoxMonData(boxMon, MON_DATA_EXPERIENCE, NULL);
|
|
ReleaseBoxMonLock(boxMon, decry);
|
|
return CalcLevelBySpeciesAndExp((u16)species, (u32)exp);
|
|
}
|
|
|
|
int CalcLevelBySpeciesAndExp(u16 species, u32 exp) {
|
|
int level;
|
|
BASE_STATS * personal = AllocAndLoadMonPersonal(species, HEAP_ID_0);
|
|
level = CalcLevelBySpeciesAndExp_PreloadedPersonal(personal, species, exp);
|
|
FreeMonPersonal(personal);
|
|
return level;
|
|
}
|
|
|
|
int CalcLevelBySpeciesAndExp_PreloadedPersonal(BASE_STATS * personal, u16 species, u32 exp) {
|
|
#pragma unused(species)
|
|
static u32 table[101];
|
|
int i;
|
|
LoadGrowthTable(GetPersonalAttr(personal, BASE_GROWTH_RATE), table);
|
|
for (i = 1; i < 101; i++) {
|
|
if (table[i] > exp) {
|
|
break;
|
|
}
|
|
}
|
|
return i - 1;
|
|
}
|
|
|
|
u8 GetMonNature(Pokemon *mon) {
|
|
return GetBoxMonNature(&mon->box);
|
|
}
|
|
|
|
u8 GetBoxMonNature(BoxPokemon *boxMon) {
|
|
BOOL decry = AcquireBoxMonLock(boxMon);
|
|
u32 personality = GetBoxMonData(boxMon, MON_DATA_PERSONALITY, NULL);
|
|
ReleaseBoxMonLock(boxMon, decry);
|
|
return GetNatureFromPersonality(personality);
|
|
}
|
|
|
|
u8 GetNatureFromPersonality(u32 pid) {
|
|
return (u8)(pid % 25);
|
|
}
|
|
|
|
const s8 gNatureStatMods[NATURE_NUM][NUM_EV_STATS] = {
|
|
{ 0, 0, 0, 0, 0},
|
|
{ 1, -1, 0, 0, 0},
|
|
{ 1, 0, -1, 0, 0},
|
|
{ 1, 0, 0, -1, 0},
|
|
{ 1, 0, 0, 0, -1},
|
|
{-1, 1, 0, 0, 0},
|
|
{ 0, 0, 0, 0, 0},
|
|
{ 0, 1, -1, 0, 0},
|
|
{ 0, 1, 0, -1, 0},
|
|
{ 0, 1, 0, 0, -1},
|
|
{-1, 0, 1, 0, 0},
|
|
{ 0, -1, 1, 0, 0},
|
|
{ 0, 0, 0, 0, 0},
|
|
{ 0, 0, 1, -1, 0},
|
|
{ 0, 0, 1, 0, -1},
|
|
{-1, 0, 0, 1, 0},
|
|
{ 0, -1, 0, 1, 0},
|
|
{ 0, 0, -1, 1, 0},
|
|
{ 0, 0, 0, 0, 0},
|
|
{ 0, 0, 0, 1, -1},
|
|
{-1, 0, 0, 0, 1},
|
|
{ 0, -1, 0, 0, 1},
|
|
{ 0, 0, -1, 0, 1},
|
|
{ 0, 0, 0, -1, 1},
|
|
{ 0, 0, 0, 0, 0},
|
|
};
|
|
|
|
u16 ModifyStatByNature(u8 nature, u16 n, u8 statIndex) {
|
|
u16 retVal;
|
|
|
|
// Dont modify HP, Accuracy, or Evasion by nature
|
|
if (statIndex < STAT_ATK || statIndex > STAT_SPDEF) {
|
|
return n;
|
|
}
|
|
|
|
switch (gNatureStatMods[nature][statIndex - 1]) {
|
|
case 1:
|
|
// NOTE: will overflow for n > 595 because the intermediate value is cast to u16 before the division.
|
|
retVal = n * 110;
|
|
retVal /= 100;
|
|
break;
|
|
case -1:
|
|
// NOTE: will overflow for n > 728, see above
|
|
retVal = n * 90;
|
|
retVal /= 100;
|
|
break;
|
|
default:
|
|
retVal = n;
|
|
break;
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
static const s8 sFriendshipModTable[FRIENDSHIP_EVENT_NUM][FRIENDSHIP_TIER_NUM] = {
|
|
{ 5, 3, 2}, // FRIENDSHIP_EVENT_GROW_LEVEL
|
|
{ 5, 3, 2}, // FRIENDSHIP_EVENT_VITAMIN
|
|
{ 1, 1, 0}, // FRIENDSHIP_EVENT_BATTLE_ITEM
|
|
{ 3, 2, 1}, // FRIENDSHIP_EVENT_LEAGUE_BATTLE
|
|
{ 1, 1, 0}, // FRIENDSHIP_EVENT_LEARN_TMHM
|
|
{ 1, 1, 1}, // FRIENDSHIP_EVENT_WALKING
|
|
{ -1, -1, -1}, // FRIENDSHIP_EVENT_FAINT_SMALL
|
|
{ -5, -5, -10}, // FRIENDSHIP_EVENT_HEAL_FIELD_PSN
|
|
{ -5, -5, -10}, // FRIENDSHIP_EVENT_FAINT_LARGE
|
|
{ 3, 2, 1}, // FRIENDHSIP_EVENT_CONTEST_WIN
|
|
};
|
|
|
|
void MonApplyFriendshipMod(Pokemon *mon, u8 kind, u16 location) {
|
|
u16 species;
|
|
u8 effect;
|
|
u8 tier;
|
|
s16 friendship;
|
|
s8 mod;
|
|
|
|
if (kind == FRIENDSHIP_EVENT_WALKING && (LCRandom() & 1))
|
|
return;
|
|
|
|
species = (u16)GetMonData(mon, MON_DATA_SPECIES2, NULL);
|
|
if (species == SPECIES_NONE || species == SPECIES_EGG)
|
|
return;
|
|
|
|
effect = (u8)GetItemAttr((u16)GetMonData(mon, MON_DATA_HELD_ITEM, NULL), ITEMATTR_HOLD_EFFECT, HEAP_ID_0);
|
|
tier = FRIENDSHIP_TIER_LOW;
|
|
friendship = (s16)GetMonData(mon, MON_DATA_FRIENDSHIP, NULL);
|
|
if (friendship >= FRIENDSHIP_TIER_MID_MIN)
|
|
tier++;
|
|
if (friendship >= FRIENDSHIP_TIER_HI_MIN)
|
|
tier++;
|
|
mod = sFriendshipModTable[kind][tier];
|
|
if (mod > 0 && GetMonData(mon, MON_DATA_DP_POKEBALL, NULL) == BALL_LUXURY)
|
|
mod++;
|
|
if (mod > 0 && GetMonData(mon, MON_DATA_EGG_MET_LOCATION, NULL) == location)
|
|
mod++;
|
|
if (mod > 0 && effect == HOLD_EFFECT_FRIENDSHIP_UP)
|
|
mod = (s8)(mod * 150 / 100);
|
|
friendship += mod;
|
|
if (friendship < 0)
|
|
friendship = 0;
|
|
if (friendship > FRIENDSHIP_MAX)
|
|
friendship = FRIENDSHIP_MAX;
|
|
SetMonData(mon, MON_DATA_FRIENDSHIP, &friendship);
|
|
}
|
|
|
|
u8 GetMonGender(Pokemon *mon) {
|
|
return GetBoxMonGender(&mon->box);
|
|
}
|
|
|
|
u8 GetBoxMonGender(BoxPokemon *boxMon) {
|
|
BOOL decry = AcquireBoxMonLock(boxMon);
|
|
u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
|
|
u32 pid = GetBoxMonData(boxMon, MON_DATA_PERSONALITY, NULL);
|
|
ReleaseBoxMonLock(boxMon, decry);
|
|
return GetGenderBySpeciesAndPersonality(species, pid);
|
|
}
|
|
|
|
u8 GetGenderBySpeciesAndPersonality(u16 species, u32 pid) {
|
|
BASE_STATS *personal = AllocAndLoadMonPersonal(species, HEAP_ID_0);
|
|
u8 gender = GetGenderBySpeciesAndPersonality_PreloadedPersonal(personal, species, pid);
|
|
FreeMonPersonal(personal);
|
|
return gender;
|
|
}
|
|
|
|
u8 GetGenderBySpeciesAndPersonality_PreloadedPersonal(const BASE_STATS *personal, u16 species, u32 pid) {
|
|
#pragma unused(species)
|
|
enum MonGender gender;
|
|
u8 ratio = GetPersonalAttr(personal, BASE_GENDER_RATIO);
|
|
switch (ratio) {
|
|
case MON_RATIO_MALE:
|
|
return MON_MALE;
|
|
case MON_RATIO_FEMALE:
|
|
return MON_FEMALE;
|
|
case MON_RATIO_UNKNOWN:
|
|
return MON_GENDERLESS;
|
|
default:
|
|
if (ratio > (u8)pid) {
|
|
gender = MON_FEMALE;
|
|
} else {
|
|
gender = MON_MALE;
|
|
}
|
|
return gender;
|
|
}
|
|
}
|
|
|
|
BOOL BoxMonIsHoldingMail(BoxPokemon *boxMon) {
|
|
return ItemIdIsMail(GetBoxMonData(boxMon, MON_DATA_HELD_ITEM, NULL));
|
|
}
|
|
|
|
u8 MonIsShiny(Pokemon *mon) {
|
|
return BoxMonIsShiny(&mon->box);
|
|
}
|
|
|
|
u8 BoxMonIsShiny(BoxPokemon *boxMon) {
|
|
u32 otid = GetBoxMonData(boxMon, MON_DATA_OTID, NULL);
|
|
u32 pid = GetBoxMonData(boxMon, MON_DATA_PERSONALITY, NULL);
|
|
return CalcShininessByOtIdAndPersonality(otid, pid);
|
|
}
|
|
|
|
u8 CalcShininessByOtIdAndPersonality(u32 otid, u32 pid) {
|
|
return (u8)SHINY_CHECK(otid, pid);
|
|
}
|
|
|
|
u32 GenerateShinyPersonality(u32 otid) {
|
|
int r4;
|
|
u16 r6;
|
|
u16 r5;
|
|
otid = (u32)((((otid & 0xFFFF0000) >> 16) ^ (otid & 0xFFFF)) >> 3u);
|
|
r6 = (u16)(LCRandom() & 7);
|
|
r5 = (u16)(LCRandom() & 7);
|
|
for (r4 = 0; r4 < 13; r4++) {
|
|
if (MaskOfFlagNo(r4) & otid) {
|
|
if (LCRandom() & 1)
|
|
r6 |= MaskOfFlagNo(r4 + 3);
|
|
else
|
|
r5 |= MaskOfFlagNo(r4 + 3);
|
|
}
|
|
else if (LCRandom() & 1) {
|
|
r6 |= MaskOfFlagNo(r4 + 3);
|
|
r5 |= MaskOfFlagNo(r4 + 3);
|
|
}
|
|
}
|
|
return (u32)((r5 << 16) | r6);
|
|
}
|
|
|
|
void GetPokemonSpriteCharAndPlttNarcIds(struct SomeDrawPokemonStruct *a0, BoxPokemon *boxMon, u8 whichFacing) {
|
|
GetBoxmonSpriteCharAndPlttNarcIds(a0, boxMon, whichFacing, FALSE);
|
|
}
|
|
|
|
void sub_02070130(struct SomeDrawPokemonStruct *a0, BoxPokemon *boxMon, u8 whichFacing) {
|
|
GetBoxmonSpriteCharAndPlttNarcIds(a0, boxMon, whichFacing, TRUE);
|
|
}
|
|
|
|
void GetBoxmonSpriteCharAndPlttNarcIds(struct SomeDrawPokemonStruct *spC, BoxPokemon *boxMon, u8 whichFacing, BOOL sp14) {
|
|
BOOL decry = AcquireBoxMonLock(boxMon);
|
|
u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES2, NULL);
|
|
u8 gender = GetBoxMonGender(boxMon);
|
|
u8 shiny = BoxMonIsShiny(boxMon);
|
|
u32 pid = GetBoxMonData(boxMon, MON_DATA_PERSONALITY, NULL);
|
|
u8 forme;
|
|
if (species == SPECIES_EGG) {
|
|
if (GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL) == SPECIES_MANAPHY) {
|
|
forme = EGG_MANAPHY;
|
|
} else {
|
|
forme = EGG_STANDARD;
|
|
}
|
|
} else {
|
|
forme = GetBoxMonData(boxMon, MON_DATA_FORME, NULL);
|
|
}
|
|
if (sp14 == TRUE) {
|
|
DP_GetMonSpriteCharAndPlttNarcIdsEx(spC, species, gender, whichFacing, shiny, forme, pid);
|
|
} else {
|
|
GetMonSpriteCharAndPlttNarcIdsEx(spC, species, gender, whichFacing, shiny, forme, pid);
|
|
}
|
|
ReleaseBoxMonLock(boxMon, decry);
|
|
}
|
|
|
|
void GetMonSpriteCharAndPlttNarcIdsEx(struct SomeDrawPokemonStruct * spC, u16 species, u8 gender, u8 whichFacing, u8 shiny, u8 forme, u32 personality) {
|
|
spC->unk6 = 0;
|
|
spC->unk8 = 0;
|
|
spC->unkC = 0;
|
|
forme = sub_02070438(species, forme);
|
|
switch (species) {
|
|
case SPECIES_BURMY:
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing / 2 + 0x48 + forme * 2);
|
|
spC->palDataID = (u16)(shiny + 0xAA + forme * 2);
|
|
break;
|
|
case SPECIES_WORMADAM:
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing / 2 + 0x4E + forme * 2);
|
|
spC->palDataID = (u16)(shiny + 0xB0 + forme * 2);
|
|
break;
|
|
case SPECIES_SHELLOS:
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing + 0x54 + forme);
|
|
spC->palDataID = (u16)(shiny + 0xB6 + forme * 2);
|
|
break;
|
|
case SPECIES_GASTRODON:
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing + 0x58 + forme);
|
|
spC->palDataID = (u16)(shiny + 0xBA + forme * 2);
|
|
break;
|
|
case SPECIES_CHERRIM:
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing + 0x5C + forme);
|
|
spC->palDataID = (u16)(shiny * 2 + 0xBE + forme);
|
|
break;
|
|
case SPECIES_ARCEUS:
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing / 2 + 0x60 + forme * 2);
|
|
spC->palDataID = (u16)(shiny + 0xC2 + forme * 2);
|
|
break;
|
|
case SPECIES_CASTFORM:
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing * 2 + 0x40 + forme);
|
|
spC->palDataID = (u16)(shiny * 4 + 0xA2 + forme);
|
|
break;
|
|
case SPECIES_DEOXYS:
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing / 2 + forme * 2);
|
|
spC->palDataID = (u16)(shiny + 0x9E);
|
|
break;
|
|
case SPECIES_UNOWN:
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing / 2 + 0x8 + forme * 2);
|
|
spC->palDataID = (u16)(shiny + 0xA0);
|
|
break;
|
|
case SPECIES_EGG: // egg, manaphy egg
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = (u16)(0x84 + forme);
|
|
spC->palDataID = (u16)(0xE6 + forme);
|
|
break;
|
|
case SPECIES_MANAPHY_EGG: // bad egg
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = 0x84;
|
|
spC->palDataID = 0xE6;
|
|
break;
|
|
case SPECIES_SHAYMIN: // land, sky
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing / 2 + 0x86 + forme * 2);
|
|
spC->palDataID = (u16)(shiny + 0xE8 + forme * 2);
|
|
break;
|
|
case SPECIES_ROTOM: // normal, fan, mow, wash, heat, frost
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing / 2 + 0x8A + forme * 2);
|
|
spC->palDataID = (u16)(shiny + 0xEC + forme * 2);
|
|
break;
|
|
case SPECIES_GIRATINA: // altered, origin
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing / 2 + 0x96 + forme * 2);
|
|
spC->palDataID = (u16)(shiny + 0xF8 + forme * 2);
|
|
break;
|
|
case SPECIES_PICHU: // spiky-ear
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing / 2 + 0x9A + forme * 2);
|
|
spC->palDataID = (u16)(shiny + 0xFC + forme * 2);
|
|
break;
|
|
default:
|
|
spC->narcID = NARC_poketool_pokegra_pokegra;
|
|
spC->charDataID = (u16)(species * 6 + whichFacing + (gender == MON_FEMALE ? 0 : 1));
|
|
spC->palDataID = (u16)(shiny + (species * 6 + 4));
|
|
if (species == SPECIES_SPINDA && whichFacing == 2)
|
|
{
|
|
spC->unk6 = SPECIES_SPINDA;
|
|
spC->unk8 = 0;
|
|
spC->unkC = personality;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
u8 sub_02070438(u16 species, u8 forme) {
|
|
switch (species) {
|
|
case SPECIES_BURMY:
|
|
if (forme > BURMY_FORME_MAX - 1) {
|
|
forme = 0;
|
|
}
|
|
break;
|
|
case SPECIES_WORMADAM:
|
|
if (forme > WORMADAM_FORME_MAX - 1) {
|
|
forme = 0;
|
|
}
|
|
break;
|
|
case SPECIES_SHELLOS:
|
|
if (forme > SHELLOS_FORME_MAX - 1) {
|
|
forme = 0;
|
|
}
|
|
break;
|
|
case SPECIES_GASTRODON:
|
|
if (forme > GASTRODON_FORME_MAX - 1) {
|
|
forme = 0;
|
|
}
|
|
break;
|
|
case SPECIES_CHERRIM:
|
|
if (forme > CHERRIM_FORME_MAX - 1) {
|
|
forme = 0;
|
|
}
|
|
break;
|
|
case SPECIES_ARCEUS:
|
|
if (forme > ARCEUS_FORME_MAX - 1) {
|
|
forme = 0;
|
|
}
|
|
break;
|
|
case SPECIES_CASTFORM:
|
|
if (forme > CASTFORM_FORME_MAX - 1) {
|
|
forme = 0;
|
|
}
|
|
break;
|
|
case SPECIES_DEOXYS:
|
|
if (forme > DEOXYS_FORME_MAX - 1) {
|
|
forme = 0;
|
|
}
|
|
break;
|
|
case SPECIES_UNOWN:
|
|
if (forme >= UNOWN_FORME_MAX) {
|
|
forme = 0;
|
|
}
|
|
break;
|
|
case SPECIES_EGG:
|
|
if (forme > EGG_FORME_MAX - 1) {
|
|
forme = 0;
|
|
}
|
|
break;
|
|
case SPECIES_SHAYMIN:
|
|
if (forme > SHAYMIN_FORME_MAX - 1) {
|
|
forme = 0;
|
|
}
|
|
break;
|
|
case SPECIES_ROTOM:
|
|
if (forme > ROTOM_FORME_MAX - 1) {
|
|
forme = 0;
|
|
}
|
|
break;
|
|
case SPECIES_GIRATINA:
|
|
if (forme > GIRATINA_FORME_MAX - 1) {
|
|
forme = 0;
|
|
}
|
|
break;
|
|
case SPECIES_PICHU:
|
|
if (forme > PICHU_FORME_MAX - 1) {
|
|
forme = 0;
|
|
}
|
|
break;
|
|
}
|
|
return forme;
|
|
}
|
|
|
|
void sub_02070560(struct SomeDrawPokemonStruct * spC, u16 species, u8 whichFacing, u8 gender, u32 shiny) {
|
|
spC->narcID = NARC_pbr_pokegra;
|
|
spC->charDataID = (u16)(species * 6 + whichFacing + (gender == MON_FEMALE ? 0 : 1));
|
|
spC->palDataID = (u16)(shiny + (species * 6 + 4));
|
|
}
|
|
|
|
void DP_GetMonSpriteCharAndPlttNarcIdsEx(struct SomeDrawPokemonStruct * spC, u16 species, u8 gender, u8 whichFacing, u8 shiny, u8 forme, u32 personality) {
|
|
spC->unk6 = 0;
|
|
spC->unk8 = 0;
|
|
spC->unkC = 0;
|
|
forme = sub_02070438(species, forme);
|
|
switch (species) {
|
|
case SPECIES_BURMY:
|
|
spC->narcID = NARC_pbr_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing / 2 + 0x48 + forme * 2);
|
|
spC->palDataID = (u16)(shiny + 0x92 + forme * 2);
|
|
break;
|
|
case SPECIES_WORMADAM:
|
|
spC->narcID = NARC_pbr_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing / 2 + 0x4E + forme * 2);
|
|
spC->palDataID = (u16)(shiny + 0x98 + forme * 2);
|
|
break;
|
|
case SPECIES_SHELLOS:
|
|
spC->narcID = NARC_pbr_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing + 0x54 + forme);
|
|
spC->palDataID = (u16)(shiny + 0x9E + forme * 2);
|
|
break;
|
|
case SPECIES_GASTRODON:
|
|
spC->narcID = NARC_pbr_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing + 0x58 + forme);
|
|
spC->palDataID = (u16)(shiny + 0xA2 + forme * 2);
|
|
break;
|
|
case SPECIES_CHERRIM:
|
|
spC->narcID = NARC_pbr_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing + 0x5C + forme);
|
|
spC->palDataID = (u16)(shiny * 2 + 0xA6 + forme);
|
|
break;
|
|
case SPECIES_ARCEUS:
|
|
spC->narcID = NARC_pbr_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing / 2 + 0x60 + forme * 2);
|
|
spC->palDataID = (u16)(shiny + 0xAA + forme * 2);
|
|
break;
|
|
case SPECIES_CASTFORM:
|
|
spC->narcID = NARC_pbr_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing * 2 + 0x40 + forme);
|
|
spC->palDataID = (u16)(shiny * 4 + 0x8A + forme);
|
|
break;
|
|
case SPECIES_DEOXYS:
|
|
spC->narcID = NARC_pbr_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing / 2 + forme * 2);
|
|
spC->palDataID = (u16)(shiny + 0x86);
|
|
break;
|
|
case SPECIES_UNOWN:
|
|
spC->narcID = NARC_pbr_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing / 2 + 0x8 + forme * 2);
|
|
spC->palDataID = (u16)(shiny + 0x88);
|
|
break;
|
|
case SPECIES_EGG: // egg, manaphy egg
|
|
spC->narcID = NARC_pbr_otherpoke;
|
|
spC->charDataID = (u16)(0x84 + forme);
|
|
spC->palDataID = (u16)(0xCE + forme);
|
|
break;
|
|
case SPECIES_MANAPHY_EGG: // bad egg
|
|
spC->narcID = NARC_pbr_otherpoke;
|
|
spC->charDataID = 0x84;
|
|
spC->palDataID = 0xCE;
|
|
break;
|
|
case SPECIES_SHAYMIN: // land, sky
|
|
if (forme != 0) {
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing / 2 + 0x86 + forme * 2);
|
|
spC->palDataID = (u16)(shiny + 0xEA);
|
|
} else {
|
|
sub_02070560(spC, species, whichFacing, gender, shiny);
|
|
}
|
|
break;
|
|
case SPECIES_ROTOM: // normal, fan, mow, wash, heat, frost
|
|
if (forme != 0) {
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing / 2 + 0x8A + forme * 2);
|
|
spC->palDataID = (u16)(shiny + 0xEC + forme * 2);
|
|
} else {
|
|
sub_02070560(spC, species, whichFacing, gender, shiny);
|
|
}
|
|
break;
|
|
case SPECIES_GIRATINA: // altered, origin
|
|
if (forme != 0) {
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing / 2 + 0x96 + forme * 2);
|
|
spC->palDataID = (u16)(shiny + 0xF8 + forme * 2);
|
|
} else {
|
|
sub_02070560(spC, species, whichFacing, gender, shiny);
|
|
}
|
|
break;
|
|
case SPECIES_PICHU: // spiky-ear
|
|
if (forme != 0) {
|
|
spC->narcID = NARC_poketool_pokegra_otherpoke;
|
|
spC->charDataID = (u16)(whichFacing / 2 + 0x9C);
|
|
spC->palDataID = (u16)(shiny + 0xFE);
|
|
} else {
|
|
sub_02070560(spC, species, whichFacing, gender, shiny);
|
|
}
|
|
break;
|
|
default:
|
|
sub_02070560(spC, species, whichFacing, gender, shiny);
|
|
if (species == SPECIES_SPINDA && whichFacing == 2)
|
|
{
|
|
spC->unk6 = SPECIES_SPINDA;
|
|
spC->unk8 = 0;
|
|
spC->unkC = personality;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
u8 sub_0207083C(BoxPokemon *boxMon, u8 whichFacing) {
|
|
return sub_02070854(boxMon, whichFacing, FALSE);
|
|
}
|
|
|
|
u8 sub_02070848(BoxPokemon *boxMon, u8 whichFacing) {
|
|
return sub_02070854(boxMon, whichFacing, TRUE);
|
|
}
|
|
|
|
u8 sub_02070854(BoxPokemon *boxMon, u8 whichFacing, BOOL a2) {
|
|
u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES2, NULL);
|
|
u8 gender = GetBoxMonGender(boxMon);
|
|
u32 pid = GetBoxMonData(boxMon, MON_DATA_PERSONALITY, NULL);
|
|
u8 forme;
|
|
if (species == SPECIES_EGG) {
|
|
if (GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL) == SPECIES_MANAPHY) {
|
|
forme = EGG_MANAPHY;
|
|
} else {
|
|
forme = EGG_STANDARD;
|
|
}
|
|
} else {
|
|
forme = GetBoxMonData(boxMon, MON_DATA_FORME, NULL);
|
|
}
|
|
if (a2 == TRUE) {
|
|
return sub_02070A64(species, gender, whichFacing, forme, pid);
|
|
} else {
|
|
return sub_020708D8(species, gender, whichFacing, forme, pid);
|
|
}
|
|
}
|
|
|
|
u8 sub_020708D8(u16 species, u8 gender, u8 whichFacing, u8 forme, u32 pid) {
|
|
// species -> r5
|
|
// gender -> r6
|
|
// whichFacing -> r4
|
|
// forme -> <overwrittten>
|
|
// pid -> sp18
|
|
#pragma unused(pid)
|
|
NarcId narcId;
|
|
s32 fileId;
|
|
u8 ret;
|
|
|
|
forme = sub_02070438(species, forme);
|
|
switch (species) {
|
|
case SPECIES_BURMY:
|
|
narcId = NARC_poketool_pokegra_height_o;
|
|
fileId = 0x48 + whichFacing / 2 + forme * 2;
|
|
break;
|
|
case SPECIES_WORMADAM:
|
|
narcId = NARC_poketool_pokegra_height_o;
|
|
fileId = 0x4E + whichFacing / 2 + forme * 2;
|
|
break;
|
|
case SPECIES_SHELLOS:
|
|
narcId = NARC_poketool_pokegra_height_o;
|
|
fileId = 0x54 + whichFacing + forme;
|
|
break;
|
|
case SPECIES_GASTRODON:
|
|
narcId = NARC_poketool_pokegra_height_o;
|
|
fileId = 0x58 + whichFacing + forme;
|
|
break;
|
|
case SPECIES_CHERRIM:
|
|
narcId = NARC_poketool_pokegra_height_o;
|
|
fileId = 0x5C + whichFacing + forme;
|
|
break;
|
|
case SPECIES_ARCEUS:
|
|
narcId = NARC_poketool_pokegra_height_o;
|
|
fileId = 0x60 + whichFacing / 2 + forme * 2;
|
|
break;
|
|
case SPECIES_CASTFORM:
|
|
narcId = NARC_poketool_pokegra_height_o;
|
|
fileId = 0x40 + 2 * whichFacing + forme;
|
|
break;
|
|
case SPECIES_DEOXYS:
|
|
narcId = NARC_poketool_pokegra_height_o;
|
|
fileId = 0x00 + whichFacing / 2 + forme * 2;
|
|
break;
|
|
case SPECIES_UNOWN:
|
|
narcId = NARC_poketool_pokegra_height_o;
|
|
fileId = 0x08 + whichFacing / 2 + forme * 2;
|
|
break;
|
|
case SPECIES_EGG: // egg, manaphy egg
|
|
narcId = NARC_poketool_pokegra_height_o;
|
|
fileId = 0x84 + forme;
|
|
break;
|
|
case SPECIES_MANAPHY_EGG: // bad egg
|
|
narcId = NARC_poketool_pokegra_height_o;
|
|
fileId = 0x84;
|
|
break;
|
|
case SPECIES_SHAYMIN: // land, sky
|
|
narcId = NARC_poketool_pokegra_height_o;
|
|
fileId = 0x88 + whichFacing / 2 + forme * 2;
|
|
break;
|
|
case SPECIES_ROTOM: // normal, fan, mow, wash, heat, frost
|
|
narcId = NARC_poketool_pokegra_height_o;
|
|
fileId = 0x8C + whichFacing / 2 + forme * 2;
|
|
break;
|
|
case SPECIES_GIRATINA: // altered, origin
|
|
narcId = NARC_poketool_pokegra_height_o;
|
|
fileId = 0x98 + whichFacing / 2 + forme * 2;
|
|
break;
|
|
case SPECIES_PICHU: // spiky-ear
|
|
narcId = NARC_poketool_pokegra_height_o;
|
|
fileId = 0x9C + whichFacing / 2 + forme * 2;
|
|
break;
|
|
default:
|
|
narcId = NARC_poketool_pokegra_height;
|
|
fileId = species * 4 + whichFacing + (gender != MON_FEMALE ? 1 : 0);
|
|
break;
|
|
}
|
|
ReadWholeNarcMemberByIdPair(&ret, narcId, fileId);
|
|
return ret;
|
|
}
|
|
|
|
u8 sub_02070A64(u16 species, u8 gender, u8 whichFacing, u8 forme, u32 pid) {
|
|
// species -> r5
|
|
// gender -> r6
|
|
// whichFacing -> r4
|
|
// forme -> <overwrittten>
|
|
// pid -> sp18
|
|
#pragma unused(pid)
|
|
NarcId narcId;
|
|
s32 fileId;
|
|
u8 ret;
|
|
|
|
forme = sub_02070438(species, forme);
|
|
switch (species) {
|
|
case SPECIES_BURMY:
|
|
narcId = NARC_pbr_dp_height_o;
|
|
fileId = 0x48 + whichFacing / 2 + forme * 2;
|
|
break;
|
|
case SPECIES_WORMADAM:
|
|
narcId = NARC_pbr_dp_height_o;
|
|
fileId = 0x4E + whichFacing / 2 + forme * 2;
|
|
break;
|
|
case SPECIES_SHELLOS:
|
|
narcId = NARC_pbr_dp_height_o;
|
|
fileId = 0x54 + whichFacing + forme;
|
|
break;
|
|
case SPECIES_GASTRODON:
|
|
narcId = NARC_pbr_dp_height_o;
|
|
fileId = 0x58 + whichFacing + forme;
|
|
break;
|
|
case SPECIES_CHERRIM:
|
|
narcId = NARC_pbr_dp_height_o;
|
|
fileId = 0x5C + whichFacing + forme;
|
|
break;
|
|
case SPECIES_ARCEUS:
|
|
narcId = NARC_pbr_dp_height_o;
|
|
fileId = 0x60 + whichFacing / 2 + forme * 2;
|
|
break;
|
|
case SPECIES_CASTFORM:
|
|
narcId = NARC_pbr_dp_height_o;
|
|
fileId = 0x40 + 2 * whichFacing + forme;
|
|
break;
|
|
case SPECIES_DEOXYS:
|
|
narcId = NARC_pbr_dp_height_o;
|
|
fileId = 0x00 + whichFacing / 2 + forme * 2;
|
|
break;
|
|
case SPECIES_UNOWN:
|
|
narcId = NARC_pbr_dp_height_o;
|
|
fileId = 0x08 + whichFacing / 2 + forme * 2;
|
|
break;
|
|
case SPECIES_EGG: // egg, manaphy egg
|
|
narcId = NARC_pbr_dp_height_o;
|
|
fileId = 0x84 + forme;
|
|
break;
|
|
case SPECIES_MANAPHY_EGG: // bad egg
|
|
narcId = NARC_pbr_dp_height_o;
|
|
fileId = 0x84;
|
|
break;
|
|
case SPECIES_SHAYMIN: // land, sky
|
|
if (forme != 0) {
|
|
narcId = NARC_poketool_pokegra_height_o;
|
|
fileId = 0x88 + whichFacing / 2 + forme * 2;
|
|
} else {
|
|
narcId = NARC_pbr_dp_height;
|
|
fileId = species * 4 + whichFacing + (gender != MON_FEMALE ? 1 : 0);
|
|
}
|
|
break;
|
|
case SPECIES_ROTOM: // normal, fan, mow, wash, heat, frost
|
|
if (forme != 0) {
|
|
narcId = NARC_poketool_pokegra_height_o;
|
|
fileId = 0x8C + whichFacing / 2 + forme * 2;
|
|
} else {
|
|
narcId = NARC_pbr_dp_height;
|
|
fileId = species * 4 + whichFacing + (gender != MON_FEMALE ? 1 : 0);
|
|
}
|
|
break;
|
|
case SPECIES_GIRATINA: // altered, origin
|
|
if (forme != 0) {
|
|
narcId = NARC_poketool_pokegra_height_o;
|
|
fileId = 0x98 + whichFacing / 2 + forme * 2;
|
|
} else {
|
|
narcId = NARC_pbr_dp_height;
|
|
fileId = species * 4 + whichFacing + (gender != MON_FEMALE ? 1 : 0);
|
|
}
|
|
break;
|
|
// case SPECIES_PICHU: // spiky-ear
|
|
// if (forme != 0) {
|
|
// narcId = NARC_pbr_dp_height_o;
|
|
// fileId = 0x9C + whichFacing / 2 + forme * 2;
|
|
// } else {
|
|
// narcId = NARC_pbr_dp_height;
|
|
// fileId = species * 4 + whichFacing + (gender != MON_FEMALE ? 1 : 0);
|
|
// }
|
|
// break;
|
|
default:
|
|
narcId = NARC_pbr_dp_height;
|
|
fileId = species * 4 + whichFacing + (gender != MON_FEMALE ? 1 : 0);
|
|
break;
|
|
}
|
|
ReadWholeNarcMemberByIdPair(&ret, narcId, fileId);
|
|
return ret;
|
|
}
|
|
|
|
static const struct UnkStruct_0200D748 _020FF588 = {
|
|
0, 0, 0, 0,
|
|
0, 0, NNS_G2D_VRAM_TYPE_2DMAIN,
|
|
{0x4E2F, 0x4E2A, 0x4E27, 0x4E27, -1u, -1u},
|
|
2, 1
|
|
};
|
|
|
|
static const int _020FF50C[] = {
|
|
0, 1, 1, 2, 0, 3
|
|
};
|
|
|
|
struct UnkStruct_0200CF18 *sub_02070C24(Unk122_021E7C9C *r6, void *sp18, void *sp1C, s32 sp20, s32 sp88, s32 trainerClass, s32 sp90, s32 sp94, s32 r5, HeapID heapId) {
|
|
s32 r7;
|
|
NARC *narc_r4;
|
|
struct UnkStruct_0200CF18 *ret_r4;
|
|
struct UnkStruct_0200D748 sp3C;
|
|
struct UnkStruct_02070D3C sp24;
|
|
|
|
r7 = 1;
|
|
sub_02070D3C(trainerClass, sp90, sp94, &sp24);
|
|
if (trainerClass == TRAINERCLASS_CASTLE_VALET) {
|
|
r7 = 2;
|
|
}
|
|
narc_r4 = NARC_New(sp24.narcId, heapId);
|
|
sub_0200D504(r6, sp18, narc_r4, sp24.ncgr_id, 0, 1, r5 + 0x4E2F);
|
|
sub_0200D68C(sp1C, 2, r6, sp18, narc_r4, sp24.nclr_id, 0, r7, 1, r5 + 0x4E2A);
|
|
sub_0200D6EC(r6, sp18, narc_r4, sp24.ncer_id, 0, r5 + 0x4E27);
|
|
sub_0200D71C(r6, sp18, narc_r4, sp24.nanr_id, 0, r5 + 0x4E27);
|
|
NARC_Delete(narc_r4);
|
|
sp3C = _020FF588;
|
|
sp3C.unk_14[0] = r5 + 0x4E2F;
|
|
sp3C.unk_14[1] = r5 + 0x4E2A;
|
|
sp3C.unk_14[2] = r5 + 0x4E27;
|
|
sp3C.unk_14[3] = r5 + 0x4E27;
|
|
sp3C.unk_08 = _020FF50C[r5];
|
|
ret_r4 = sub_0200D734(r6, sp18, &sp3C);
|
|
sub_02024AA8(ret_r4->unk_00, 0);
|
|
sub_0200DDB8(ret_r4, sp20, sp88);
|
|
sub_0200DC18(ret_r4);
|
|
sub_0200DC78(ret_r4, 1);
|
|
return ret_r4;
|
|
}
|
|
|
|
void sub_02070D3C(s32 trainer_class, s32 a1, s32 a2, struct UnkStruct_02070D3C *a3) {
|
|
if (a1 == 2) {
|
|
a3->narcId = NARC_a_0_5_8;
|
|
a3->ncgr_id = trainer_class * 5 + 0;
|
|
a3->nclr_id = trainer_class * 5 + 1;
|
|
a3->ncer_id = trainer_class * 5 + 2;
|
|
a3->nanr_id = trainer_class * 5 + 3;
|
|
a3->ncbr_id = trainer_class * 5 + 4;
|
|
} else {
|
|
a3->narcId = NARC_a_0_0_6;
|
|
trainer_class = TrainerClassToBackpicID(trainer_class, a2);
|
|
a3->ncgr_id = trainer_class * 5 + 0;
|
|
a3->nclr_id = trainer_class * 5 + 1;
|
|
a3->ncer_id = trainer_class * 5 + 2;
|
|
a3->nanr_id = trainer_class * 5 + 3;
|
|
a3->ncbr_id = trainer_class * 5 + 4;
|
|
}
|
|
}
|
|
|
|
void sub_02070D84(s32 trainer_class, s32 a1, struct UnkStruct_02070D3C *a2) {
|
|
sub_02070D3C(trainer_class, a1, 0, a2);
|
|
}
|
|
|
|
u32 sub_02070D90(void) {
|
|
return sizeof(Pokemon);
|
|
}
|
|
|
|
u32 sub_02070D94(void) {
|
|
return sizeof(BoxPokemon);
|
|
}
|
|
|
|
u8 GetMonUnownLetter(Pokemon *mon) {
|
|
return GetBoxMonUnownLetter(&mon->box);
|
|
}
|
|
|
|
u8 GetBoxMonUnownLetter(BoxPokemon *boxMon) {
|
|
return (u8)GetBoxMonData(boxMon, MON_DATA_FORME, NULL);
|
|
}
|
|
|
|
BoxPokemon *Mon_GetBoxMon(Pokemon *mon) {
|
|
return &mon->box;
|
|
}
|
|
|
|
BOOL Pokemon_TryLevelUp(Pokemon *mon) {
|
|
u16 species = (u16)GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
u8 level = (u8)(GetMonData(mon, MON_DATA_LEVEL, NULL) + 1);
|
|
u32 exp = GetMonData(mon, MON_DATA_EXPERIENCE, NULL);
|
|
u32 growthrate = (u32)GetMonBaseStat(species, BASE_GROWTH_RATE);
|
|
u32 maxexp = GetExpByGrowthRateAndLevel((int)growthrate, 100);
|
|
if (exp > maxexp) {
|
|
exp = maxexp;
|
|
SetMonData(mon, MON_DATA_EXPERIENCE, &exp);
|
|
}
|
|
if (level > 100)
|
|
return FALSE;
|
|
if (exp >= GetExpByGrowthRateAndLevel((int)growthrate, level)) {
|
|
SetMonData(mon, MON_DATA_LEVEL, &level);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
u16 GetMonEvolution(PARTY *party, Pokemon *mon, u8 context, u16 usedItem, int *method_ret) {
|
|
u16 species;
|
|
u16 heldItem;
|
|
u8 level;
|
|
int i;
|
|
u16 target = SPECIES_NONE;
|
|
u16 friendship;
|
|
u32 pid;
|
|
u8 holdEffect;
|
|
u8 beauty; // for Feebas, but queried unconditionally.
|
|
u16 pid_hi = 0;
|
|
struct Evolution *evoTable;
|
|
int method_local;
|
|
|
|
species = GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, NULL);
|
|
pid = GetMonData(mon, MON_DATA_PERSONALITY, NULL);
|
|
beauty = GetMonData(mon, MON_DATA_BEAUTY, NULL);
|
|
pid_hi = (u16)((pid & 0xFFFF0000) >> 16);
|
|
holdEffect = GetItemAttr(heldItem, ITEMATTR_HOLD_EFFECT, HEAP_ID_0);
|
|
// Kadabra bypasses Everstone because he's just that broken.
|
|
if (species != SPECIES_KADABRA && holdEffect == HOLD_EFFECT_NO_EVOLVE && context != EVOCTX_ITEM_USE) {
|
|
return SPECIES_NONE;
|
|
}
|
|
// Spiky-ear Pichu cannot evolve
|
|
if (species == SPECIES_PICHU && GetMonData(mon, MON_DATA_FORME, 0) == 1) {
|
|
return SPECIES_NONE;
|
|
}
|
|
if (method_ret == NULL) {
|
|
method_ret = &method_local;
|
|
}
|
|
evoTable = AllocFromHeap(HEAP_ID_0, MAX_EVOS_PER_POKE * sizeof(struct Evolution));
|
|
LoadMonEvolutionTable(species, evoTable);
|
|
switch (context) {
|
|
case EVOCTX_LEVELUP:
|
|
level = (u8)GetMonData(mon, MON_DATA_LEVEL, NULL);
|
|
friendship = (u16)GetMonData(mon, MON_DATA_FRIENDSHIP, NULL);
|
|
for (i = 0; i < 7; i++) {
|
|
switch (evoTable[i].method) {
|
|
case EVO_NONE:
|
|
break;
|
|
case EVO_FRIENDSHIP:
|
|
if (friendship >= 220) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_FRIENDSHIP;
|
|
}
|
|
break;
|
|
case EVO_FRIENDSHIP_DAY:
|
|
if (IsNighttime() == 0 && friendship >= 220) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_FRIENDSHIP_DAY;
|
|
}
|
|
break;
|
|
case EVO_FRIENDSHIP_NIGHT:
|
|
if (IsNighttime() == 1 && friendship >= 220) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_FRIENDSHIP_NIGHT;
|
|
}
|
|
break;
|
|
case EVO_LEVEL:
|
|
if (evoTable[i].param <= level) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_LEVEL;
|
|
}
|
|
break;
|
|
case EVO_TRADE:
|
|
break;
|
|
case EVO_TRADE_ITEM:
|
|
break;
|
|
case EVO_STONE:
|
|
break;
|
|
case EVO_LEVEL_ATK_GT_DEF:
|
|
if (evoTable[i].param <= level && GetMonData(mon, MON_DATA_ATK, NULL) > GetMonData(mon, MON_DATA_DEF, NULL)) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_LEVEL_ATK_GT_DEF;
|
|
}
|
|
break;
|
|
case EVO_LEVEL_ATK_EQ_DEF:
|
|
if (evoTable[i].param <= level && GetMonData(mon, MON_DATA_ATK, NULL) == GetMonData(mon, MON_DATA_DEF, NULL)) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_LEVEL_ATK_EQ_DEF;
|
|
}
|
|
break;
|
|
case EVO_LEVEL_ATK_LT_DEF:
|
|
if (evoTable[i].param <= level && GetMonData(mon, MON_DATA_ATK, NULL) < GetMonData(mon, MON_DATA_DEF, NULL)) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_LEVEL_ATK_LT_DEF;
|
|
}
|
|
break;
|
|
case EVO_LEVEL_PID_LO:
|
|
if (evoTable[i].param <= level && pid_hi % 10 < 5) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_LEVEL_PID_LO;
|
|
}
|
|
break;
|
|
case EVO_LEVEL_PID_HI:
|
|
if (evoTable[i].param <= level && pid_hi % 10 >= 5) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_LEVEL_PID_HI;
|
|
}
|
|
break;
|
|
case EVO_LEVEL_NINJASK:
|
|
if (evoTable[i].param <= level) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_LEVEL_NINJASK;
|
|
}
|
|
break;
|
|
case EVO_LEVEL_SHEDINJA:
|
|
*method_ret = EVO_LEVEL_SHEDINJA;
|
|
break;
|
|
case EVO_BEAUTY:
|
|
if (evoTable[i].param <= beauty) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_BEAUTY;
|
|
}
|
|
break;
|
|
case EVO_STONE_MALE:
|
|
break;
|
|
case EVO_STONE_FEMALE:
|
|
break;
|
|
case EVO_ITEM_DAY:
|
|
if (IsNighttime() == 0 && evoTable[i].param == heldItem) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_ITEM_DAY;
|
|
}
|
|
break;
|
|
case EVO_ITEM_NIGHT:
|
|
if (IsNighttime() == 1 && evoTable[i].param == heldItem) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_ITEM_NIGHT;
|
|
}
|
|
break;
|
|
case EVO_HAS_MOVE:
|
|
if (MonHasMove(mon, evoTable[i].param) == TRUE) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_HAS_MOVE;
|
|
}
|
|
break;
|
|
case EVO_OTHER_PARTY_MON:
|
|
if (party != NULL && PartyHasMon(party, evoTable[i].param) == 1) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_OTHER_PARTY_MON;
|
|
}
|
|
break;
|
|
case EVO_LEVEL_MALE:
|
|
if (GetMonData(mon, MON_DATA_GENDER, NULL) == MON_MALE && evoTable[i].param <= level) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_LEVEL_MALE;
|
|
}
|
|
break;
|
|
case EVO_LEVEL_FEMALE:
|
|
if (GetMonData(mon, MON_DATA_GENDER, NULL) == MON_FEMALE && evoTable[i].param <= level) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_LEVEL_FEMALE;
|
|
}
|
|
break;
|
|
case EVO_CORONET:
|
|
if (usedItem == evoTable[i].method) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_CORONET;
|
|
}
|
|
break;
|
|
case EVO_ETERNA:
|
|
if (usedItem == evoTable[i].method) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_ETERNA;
|
|
}
|
|
break;
|
|
case EVO_ROUTE217:
|
|
if (usedItem == evoTable[i].method) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_ROUTE217;
|
|
}
|
|
break;
|
|
}
|
|
if (target != SPECIES_NONE) {
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case EVOCTX_TRADE:
|
|
for (i = 0; i < 7; i++) {
|
|
switch (evoTable[i].method) {
|
|
case EVO_TRADE:
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_TRADE;
|
|
break;
|
|
case EVO_TRADE_ITEM:
|
|
if (heldItem == evoTable[i].param) {
|
|
target = evoTable[i].target;
|
|
*method_ret = EVO_TRADE_ITEM;
|
|
}
|
|
break;
|
|
}
|
|
if (target != SPECIES_NONE) {
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case EVOCTX_ITEM_CHECK:
|
|
case EVOCTX_ITEM_USE:
|
|
for (i = 0; i < 7; i++) {
|
|
if (evoTable[i].method == EVO_STONE && usedItem == evoTable[i].param) {
|
|
target = evoTable[i].target;
|
|
*method_ret = 0;
|
|
break;
|
|
}
|
|
if (evoTable[i].method == EVO_STONE_MALE && GetMonData(mon, MON_DATA_GENDER, NULL) == MON_MALE && usedItem == evoTable[i].param) {
|
|
target = evoTable[i].target;
|
|
*method_ret = 0;
|
|
break;
|
|
}
|
|
if (evoTable[i].method == EVO_STONE_FEMALE && GetMonData(mon, MON_DATA_GENDER, NULL) == MON_FEMALE && usedItem == evoTable[i].param) {
|
|
target = evoTable[i].target;
|
|
*method_ret = 0;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
FreeToHeap(evoTable);
|
|
return target;
|
|
}
|
|
|
|
u16 ReadFromPersonalPmsNarc(u16 species) {
|
|
u16 ret = 0;
|
|
FSFile file;
|
|
GF_ASSERT(species < SPECIES_EGG);
|
|
FS_InitFile(&file);
|
|
FS_OpenFile(&file, "poketool/personal/pms.narc");
|
|
FS_SeekFile(&file, species * 2, FS_SEEK_SET);
|
|
FS_ReadFile(&file, &ret, 2);
|
|
FS_CloseFile(&file);
|
|
return ret;
|
|
}
|
|
|
|
u16 GetEggSpecies(u16 species) {
|
|
switch (species) {
|
|
case SPECIES_SUDOWOODO:
|
|
case SPECIES_MARILL:
|
|
case SPECIES_MR_MIME:
|
|
case SPECIES_CHANSEY:
|
|
case SPECIES_SNORLAX:
|
|
case SPECIES_MANTINE:
|
|
case SPECIES_WOBBUFFET:
|
|
case SPECIES_ROSELIA:
|
|
case SPECIES_CHIMECHO:
|
|
return species;
|
|
default:
|
|
return ReadFromPersonalPmsNarc(species);
|
|
}
|
|
}
|
|
|
|
void InitBoxMonMoveset(BoxPokemon *boxMon) {
|
|
BOOL decry;
|
|
u16 * wotbl;
|
|
int i;
|
|
u16 species;
|
|
u32 forme;
|
|
u8 level;
|
|
u16 move;
|
|
wotbl = AllocFromHeap(HEAP_ID_0, 22 * sizeof(u16));
|
|
decry = AcquireBoxMonLock(boxMon);
|
|
species = (u16)GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
|
|
forme = GetBoxMonData(boxMon, MON_DATA_FORME, NULL);
|
|
level = (u8)CalcBoxMonLevel(boxMon);
|
|
LoadWotbl_HandleAlternateForme(species, (int)forme, wotbl);
|
|
for (i = 0; wotbl[i] != WOTBL_END; i++) {
|
|
if ((wotbl[i] & WOTBL_LEVEL_MASK) > (level << WOTBL_LEVEL_SHIFT))
|
|
break;
|
|
move = WOTBL_MOVE(wotbl[i]);
|
|
if (TryAppendBoxMonMove(boxMon, move) == MOVE_APPEND_FULL)
|
|
DeleteBoxMonFirstMoveAndAppend(boxMon, move);
|
|
}
|
|
FreeToHeap(wotbl);
|
|
ReleaseBoxMonLock(boxMon, decry);
|
|
}
|
|
|
|
u32 TryAppendMonMove(Pokemon *mon, u16 move) {
|
|
return TryAppendBoxMonMove(Mon_GetBoxMon(mon), move);
|
|
}
|
|
|
|
u32 TryAppendBoxMonMove(BoxPokemon *boxMon, u16 move) {
|
|
u32 ret = MOVE_APPEND_FULL;
|
|
int i;
|
|
BOOL decry = AcquireBoxMonLock(boxMon);
|
|
u16 cur_move;
|
|
for (i = 0; i < MAX_MON_MOVES; i++) {
|
|
cur_move = (u16)GetBoxMonData(boxMon, MON_DATA_MOVE1 + i, NULL);
|
|
if (cur_move == MOVE_NONE) {
|
|
BoxMonSetMoveInSlot(boxMon, move, (u8)i);
|
|
ret = move;
|
|
break;
|
|
}
|
|
if (cur_move == move) {
|
|
ret = MOVE_APPEND_KNOWN;
|
|
break;
|
|
}
|
|
}
|
|
ReleaseBoxMonLock(boxMon, decry);
|
|
return ret;
|
|
}
|
|
|
|
void DeleteMonFirstMoveAndAppend(Pokemon *mon, u16 move_id) {
|
|
DeleteBoxMonFirstMoveAndAppend(Mon_GetBoxMon(mon), move_id);
|
|
}
|
|
|
|
void DeleteBoxMonFirstMoveAndAppend(BoxPokemon *boxMon, u16 move) {
|
|
BOOL decry = AcquireBoxMonLock(boxMon);
|
|
int i;
|
|
u16 moves[MAX_MON_MOVES];
|
|
u8 pp[MAX_MON_MOVES];
|
|
u8 ppUp[MAX_MON_MOVES];
|
|
|
|
for (i = 0; i < MAX_MON_MOVES - 1; i++) {
|
|
moves[i] = (u16)GetBoxMonData(boxMon, MON_DATA_MOVE1 + i + 1, NULL);
|
|
pp[i] = (u8)GetBoxMonData(boxMon, MON_DATA_MOVE1PP + i + 1, NULL);
|
|
ppUp[i] = (u8)GetBoxMonData(boxMon, MON_DATA_MOVE1PPUP + i + 1, NULL);
|
|
}
|
|
|
|
moves[3] = move;
|
|
pp[3] = (u8)GetMoveAttr(move, MOVEATTR_PP);
|
|
ppUp[3] = 0;
|
|
|
|
for (i = 0; i < MAX_MON_MOVES; i++) {
|
|
SetBoxMonData(boxMon, MON_DATA_MOVE1 + i, &moves[i]);
|
|
SetBoxMonData(boxMon, MON_DATA_MOVE1PP + i, &pp[i]);
|
|
SetBoxMonData(boxMon, MON_DATA_MOVE1PPUP + i, &ppUp[i]);
|
|
}
|
|
|
|
ReleaseBoxMonLock(boxMon, decry);
|
|
}
|
|
|
|
void MonSetMoveInSlot_ResetPpUp(Pokemon *mon, u16 move, u8 slot) {
|
|
int pp;
|
|
int ppUp;
|
|
|
|
MonSetMoveInSlot(mon, move, slot);
|
|
ppUp = 0;
|
|
SetMonData(mon, MON_DATA_MOVE1PPUP + slot, &ppUp);
|
|
pp = GetMoveMaxPP(move, 0);
|
|
SetMonData(mon, MON_DATA_MOVE1PP + slot, &pp);
|
|
}
|
|
|
|
void MonSetMoveInSlot(Pokemon *mon, u16 move, u8 slot) {
|
|
BoxMonSetMoveInSlot(&mon->box, move, slot);
|
|
}
|
|
|
|
void BoxMonSetMoveInSlot(BoxPokemon *boxMon, u16 move, u8 slot) {
|
|
u8 ppUp;
|
|
u8 pp;
|
|
|
|
SetBoxMonData(boxMon, MON_DATA_MOVE1 + slot, &move);
|
|
ppUp = (u8)GetBoxMonData(boxMon, MON_DATA_MOVE1PPUP + slot, NULL);
|
|
pp = (u8)GetMoveMaxPP(move, ppUp);
|
|
SetBoxMonData(boxMon, MON_DATA_MOVE1PP + slot, &pp);
|
|
}
|
|
|
|
u32 MonTryLearnMoveOnLevelUp(Pokemon *mon, int * last_i, u16 * sp0) {
|
|
u32 ret = 0;
|
|
u16 * wotbl = AllocFromHeap(HEAP_ID_0, 22 * sizeof(u16));
|
|
u16 species = (u16)GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
u32 forme = GetMonData(mon, MON_DATA_FORME, NULL);
|
|
u8 level = (u8)GetMonData(mon, MON_DATA_LEVEL, NULL);
|
|
LoadWotbl_HandleAlternateForme(species, (int)forme, wotbl);
|
|
|
|
if (wotbl[*last_i] == WOTBL_END) {
|
|
FreeToHeap(wotbl);
|
|
return 0;
|
|
}
|
|
while ((wotbl[*last_i] & WOTBL_LEVEL_MASK) != (level << WOTBL_LEVEL_SHIFT)) {
|
|
(*last_i)++;
|
|
if (wotbl[*last_i] == WOTBL_END) {
|
|
FreeToHeap(wotbl);
|
|
return 0;
|
|
}
|
|
}
|
|
if ((wotbl[*last_i] & WOTBL_LEVEL_MASK) == (level << WOTBL_LEVEL_SHIFT)) {
|
|
*sp0 = WOTBL_MOVE(wotbl[*last_i]);
|
|
(*last_i)++;
|
|
ret = TryAppendMonMove(mon, *sp0);
|
|
}
|
|
FreeToHeap(wotbl);
|
|
return ret;
|
|
}
|
|
|
|
void MonSwapMoves(Pokemon *mon, int a, int b) {
|
|
BoxMonSwapMoves(&mon->box, a, b);
|
|
}
|
|
|
|
void BoxMonSwapMoves(BoxPokemon *boxMon, int slot1, int slot2) {
|
|
u16 moves[2];
|
|
u8 pp[2];
|
|
u8 ppUp[2];
|
|
|
|
moves[0] = (u16)GetBoxMonData(boxMon, MON_DATA_MOVE1 + slot1, NULL);
|
|
pp[0] = (u8)GetBoxMonData(boxMon, MON_DATA_MOVE1PP + slot1, NULL);
|
|
ppUp[0] = (u8)GetBoxMonData(boxMon, MON_DATA_MOVE1PPUP + slot1, NULL);
|
|
moves[1] = (u16)GetBoxMonData(boxMon, MON_DATA_MOVE1 + slot2, NULL);
|
|
pp[1] = (u8)GetBoxMonData(boxMon, MON_DATA_MOVE1PP + slot2, NULL);
|
|
ppUp[1] = (u8)GetBoxMonData(boxMon, MON_DATA_MOVE1PPUP + slot2, NULL);
|
|
|
|
SetBoxMonData(boxMon, MON_DATA_MOVE1 + slot1, &moves[1]);
|
|
SetBoxMonData(boxMon, MON_DATA_MOVE1PP + slot1, &pp[1]);
|
|
SetBoxMonData(boxMon, MON_DATA_MOVE1PPUP + slot1, &ppUp[1]);
|
|
SetBoxMonData(boxMon, MON_DATA_MOVE1 + slot2, &moves[0]);
|
|
SetBoxMonData(boxMon, MON_DATA_MOVE1PP + slot2, &pp[0]);
|
|
SetBoxMonData(boxMon, MON_DATA_MOVE1PPUP + slot2, &ppUp[0]);
|
|
}
|
|
|
|
void MonDeleteMoveSlot(Pokemon *mon, u32 slot) {
|
|
u16 move;
|
|
u8 pp;
|
|
u8 ppUp;
|
|
for (; slot < MAX_MON_MOVES - 1; slot++) {
|
|
move = (u16)GetMonData(mon, (int)(MON_DATA_MOVE1 + slot + 1), NULL);
|
|
pp = (u8)GetMonData(mon, (int)(MON_DATA_MOVE1PP + slot + 1), NULL);
|
|
ppUp = (u8)GetMonData(mon, (int)(MON_DATA_MOVE1PPUP + slot + 1), NULL);
|
|
SetMonData(mon, (int)(MON_DATA_MOVE1 + slot), &move);
|
|
SetMonData(mon, (int)(MON_DATA_MOVE1PP + slot), &pp);
|
|
SetMonData(mon, (int)(MON_DATA_MOVE1PPUP + slot), &ppUp);
|
|
}
|
|
move = MOVE_NONE;
|
|
pp = 0;
|
|
ppUp = 0;
|
|
SetMonData(mon, MON_DATA_MOVE1 + 3, &move);
|
|
SetMonData(mon, MON_DATA_MOVE1PP + 3, &pp);
|
|
SetMonData(mon, MON_DATA_MOVE1PPUP + 3, &ppUp);
|
|
}
|
|
|
|
BOOL MonHasMove(Pokemon *mon, u16 move) {
|
|
int i;
|
|
for (i = 0; i < MAX_MON_MOVES; i++) {
|
|
if (GetMonData(mon, MON_DATA_MOVE1 + i, NULL) == move) {
|
|
break;
|
|
}
|
|
}
|
|
if (i != MAX_MON_MOVES) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
void CopyBoxPokemonToPokemon(const BoxPokemon *src, Pokemon *dest) {
|
|
u32 sp0 = 0;
|
|
CAPSULE sp4;
|
|
struct Mail * mail;
|
|
dest->box = *src;
|
|
if (dest->box.box_lock)
|
|
dest->box.party_lock = TRUE;
|
|
SetMonData(dest, MON_DATA_STATUS, &sp0);
|
|
SetMonData(dest, MON_DATA_HP, &sp0);
|
|
SetMonData(dest, MON_DATA_MAXHP, &sp0);
|
|
mail = Mail_New(HEAP_ID_0);
|
|
SetMonData(dest, MON_DATA_MAIL_STRUCT, mail);
|
|
FreeToHeap(mail);
|
|
SetMonData(dest, MON_DATA_CAPSULE, &sp0);
|
|
MI_CpuClearFast(&sp4, sizeof(sp4));
|
|
SetMonData(dest, MON_DATA_SEAL_COORDS, &sp4);
|
|
CalcMonLevelAndStats(dest);
|
|
}
|
|
|
|
u8 Party_GetMaxLevel(PARTY * party) {
|
|
int i;
|
|
int r7 = GetPartyCount(party);
|
|
u8 ret = 1;
|
|
u8 level;
|
|
for (i = 0; i < r7; i++) {
|
|
Pokemon *mon = GetPartyMonByIndex(party, i);
|
|
if (GetMonData(mon, MON_DATA_SPECIES, NULL) != SPECIES_NONE
|
|
&& !GetMonData(mon, MON_DATA_IS_EGG, NULL)) {
|
|
level = (u8)GetMonData(mon, MON_DATA_LEVEL, NULL);
|
|
if (level > ret)
|
|
ret = level;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
u16 SpeciesToJohtoDexNo(u16 species) {
|
|
u16 ret;
|
|
ReadFromNarcMemberByIdPair(&ret, NARC_poketool_johtozukan, 0, species * sizeof(u16), sizeof(u16));
|
|
return ret;
|
|
}
|
|
|
|
u16 *LoadSpeciesToJohtoDexNoLUT(void) {
|
|
return AllocAtEndAndReadWholeNarcMemberByIdPair(NARC_poketool_johtozukan, 0, HEAP_ID_3);
|
|
}
|
|
|
|
void CopyPokemonToPokemon(const Pokemon *src, Pokemon *dest) {
|
|
*dest = *src;
|
|
}
|
|
|
|
void CopyBoxPokemonToBoxPokemon(const BoxPokemon *src, BoxPokemon *dest) {
|
|
*dest = *src;
|
|
}
|
|
|
|
void CopyPokemonToBoxPokemon(const Pokemon *src, BoxPokemon *dest) {
|
|
*dest = src->box;
|
|
}
|
|
|
|
s8 MonGetFlavorPreference(Pokemon *mon, int flavor) {
|
|
return BoxMonGetFlavorPreference(&mon->box, flavor);
|
|
}
|
|
|
|
s8 BoxMonGetFlavorPreference(BoxPokemon *boxMon, int flavor) {
|
|
u32 personality = GetBoxMonData(boxMon, MON_DATA_PERSONALITY, NULL);
|
|
return GetFlavorPreferenceFromPID(personality, flavor);
|
|
}
|
|
|
|
s8 GetFlavorPreferenceFromPID(u32 personality, int flavor) {
|
|
return sFlavorPreferencesByNature[GetNatureFromPersonality(personality)][flavor];
|
|
}
|
|
|
|
int Species_LoadLearnsetTable(u16 species, u32 forme, u16 * dest) {
|
|
int i;
|
|
u16 * wotbl = AllocFromHeap(HEAP_ID_0, 22 * sizeof(u16));
|
|
LoadWotbl_HandleAlternateForme(species, (int)forme, wotbl);
|
|
for (i = 0; wotbl[i] != WOTBL_END; i++) {
|
|
dest[i] = WOTBL_MOVE(wotbl[i]);
|
|
}
|
|
FreeToHeap(wotbl);
|
|
return i;
|
|
}
|
|
|
|
void Party_GivePokerusAtRandom(PARTY * party) {
|
|
int count = GetPartyCount(party);
|
|
int idx;
|
|
Pokemon *mon;
|
|
u8 sp0;
|
|
switch (LCRandom()) {
|
|
case 0x4000:
|
|
case 0x8000:
|
|
case 0xC000:
|
|
do {
|
|
idx = LCRandom() % count;
|
|
mon = GetPartyMonByIndex(party, idx);
|
|
} while (GetMonData(mon, MON_DATA_SPECIES, NULL) == SPECIES_NONE || GetMonData(mon, MON_DATA_IS_EGG, NULL));
|
|
if (!Party_MaskMonsWithPokerus(party, (u8)MaskOfFlagNo(idx))) {
|
|
do {
|
|
sp0 = (u8)LCRandom();
|
|
} while (!(sp0 & 7));
|
|
if (sp0 & 0xF0) {
|
|
sp0 &= 7;
|
|
}
|
|
sp0 |= sp0 << 4;
|
|
sp0 &= 0xF3;
|
|
sp0++;
|
|
SetMonData(mon, MON_DATA_POKERUS, &sp0);
|
|
}
|
|
}
|
|
}
|
|
|
|
u8 Party_MaskMonsWithPokerus(PARTY * party, u8 mask) {
|
|
int i = 0;
|
|
u32 flag = 1;
|
|
u8 ret = 0;
|
|
Pokemon *mon;
|
|
if (mask != 0) {
|
|
do {
|
|
if (mask & 1) {
|
|
mon = GetPartyMonByIndex(party, i);
|
|
if (GetMonData(mon, MON_DATA_POKERUS, NULL)) {
|
|
ret |= flag;
|
|
}
|
|
}
|
|
i++;
|
|
flag <<= 1;
|
|
mask >>= 1;
|
|
}
|
|
while (mask != 0);
|
|
} else {
|
|
mon = GetPartyMonByIndex(party, 0);
|
|
if (GetMonData(mon, MON_DATA_POKERUS, NULL))
|
|
ret++;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void Party_UpdatePokerus(PARTY * party, int r5) {
|
|
int i;
|
|
u8 pokerus;
|
|
Pokemon *mon;
|
|
int count = GetPartyCount(party);
|
|
for (i = 0; i < count; i++) {
|
|
mon = GetPartyMonByIndex(party, i);
|
|
if (GetMonData(mon, MON_DATA_SPECIES, NULL) != SPECIES_NONE) {
|
|
pokerus = (u8)GetMonData(mon, MON_DATA_POKERUS, NULL);
|
|
if (pokerus & 0xF) {
|
|
if ((pokerus & 0xF) < r5 || r5 > 4) {
|
|
pokerus &= 0xF0;
|
|
} else {
|
|
pokerus -= r5;
|
|
}
|
|
if (pokerus == 0) {
|
|
pokerus = 0x10; // immune
|
|
}
|
|
SetMonData(mon, MON_DATA_POKERUS, &pokerus);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Party_SpreadPokerus(PARTY * party) {
|
|
int count = GetPartyCount(party);
|
|
int i;
|
|
Pokemon *mon;
|
|
u8 pokerus;
|
|
if ((LCRandom() % 3) == 0) {
|
|
for (i = 0; i < count; i++) {
|
|
mon = GetPartyMonByIndex(party, i);
|
|
if (GetMonData(mon, MON_DATA_SPECIES, NULL) != SPECIES_NONE) {
|
|
pokerus = (u8)GetMonData(mon, MON_DATA_POKERUS, NULL);
|
|
if (pokerus & 0xF) {
|
|
if (i != 0) {
|
|
mon = GetPartyMonByIndex(party, i - 1);
|
|
if (!(GetMonData(mon, MON_DATA_POKERUS, NULL) & 0xF0)) {
|
|
SetMonData(mon, MON_DATA_POKERUS, &pokerus);
|
|
}
|
|
}
|
|
if (i < count - 1) {
|
|
mon = GetPartyMonByIndex(party, i + 1);
|
|
if (!(GetMonData(mon, MON_DATA_POKERUS, NULL) & 0xF0)) {
|
|
SetMonData(mon, MON_DATA_POKERUS, &pokerus);
|
|
i++; // don't infect the rest of the party
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL Pokemon_HasPokerus(Pokemon *mon) {
|
|
return BoxMon_HasPokerus(&mon->box);
|
|
}
|
|
|
|
BOOL BoxMon_HasPokerus(BoxPokemon *boxMon) {
|
|
return (GetBoxMonData(boxMon, MON_DATA_POKERUS, NULL) & 0xF) != 0;
|
|
}
|
|
|
|
BOOL Pokemon_IsImmuneToPokerus(Pokemon *mon) {
|
|
return BoxMon_IsImmuneToPokerus(&mon->box);
|
|
}
|
|
|
|
BOOL BoxMon_IsImmuneToPokerus(BoxPokemon *boxMon) {
|
|
u8 pokerus = (u8)GetBoxMonData(boxMon, MON_DATA_POKERUS, NULL);
|
|
if (pokerus & 0xF) {
|
|
return FALSE;
|
|
}
|
|
if (pokerus & 0xF0) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void Pokemon_UpdateArceusForme(Pokemon *mon) {
|
|
BoxMon_UpdateArceusForme(&mon->box);
|
|
}
|
|
|
|
void BoxMon_UpdateArceusForme(BoxPokemon *boxMon) {
|
|
u32 species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
|
|
u32 ability = GetBoxMonData(boxMon, MON_DATA_ABILITY, NULL);
|
|
u32 heldItem = GetBoxMonData(boxMon, MON_DATA_HELD_ITEM, NULL);
|
|
u32 forme;
|
|
if (species == SPECIES_ARCEUS && ability == ABILITY_MULTITYPE) {
|
|
forme = GetArceusTypeByHeldItemEffect((u16)GetItemAttr((u16)heldItem, 1, HEAP_ID_0));
|
|
SetBoxMonData(boxMon, MON_DATA_FORME, &forme);
|
|
}
|
|
}
|
|
|
|
u32 GetArceusTypeByHeldItemEffect(u16 heldEffect) {
|
|
switch (heldEffect) {
|
|
case HOLD_EFFECT_ARCEUS_FIRE:
|
|
return TYPE_FIRE;
|
|
case HOLD_EFFECT_ARCEUS_WATER:
|
|
return TYPE_WATER;
|
|
case HOLD_EFFECT_ARCEUS_ELECTRIC:
|
|
return TYPE_ELECTRIC;
|
|
case HOLD_EFFECT_ARCEUS_GRASS:
|
|
return TYPE_GRASS;
|
|
case HOLD_EFFECT_ARCEUS_ICE:
|
|
return TYPE_ICE;
|
|
case HOLD_EFFECT_ARCEUS_FIGHT:
|
|
return TYPE_FIGHTING;
|
|
case HOLD_EFFECT_ARCEUS_POISON:
|
|
return TYPE_POISON;
|
|
case HOLD_EFFECT_ARCEUS_GROUND:
|
|
return TYPE_GROUND;
|
|
case HOLD_EFFECT_ARCEUS_FLYING:
|
|
return TYPE_FLYING;
|
|
case HOLD_EFFECT_ARCEUS_PSYCHIC:
|
|
return TYPE_PSYCHIC;
|
|
case HOLD_EFFECT_ARCEUS_BUG:
|
|
return TYPE_BUG;
|
|
case HOLD_EFFECT_ARCEUS_ROCK:
|
|
return TYPE_ROCK;
|
|
case HOLD_EFFECT_ARCEUS_GHOST:
|
|
return TYPE_GHOST;
|
|
case HOLD_EFFECT_ARCEUS_DRAGON:
|
|
return TYPE_DRAGON;
|
|
case HOLD_EFFECT_ARCEUS_DARK:
|
|
return TYPE_DARK;
|
|
case HOLD_EFFECT_ARCEUS_STEEL:
|
|
return TYPE_STEEL;
|
|
default:
|
|
return TYPE_NORMAL;
|
|
}
|
|
}
|
|
|
|
s32 Mon_UpdateGiratinaForme(Pokemon *mon) {
|
|
s32 ret = BoxMon_UpdateGiratinaForme(&mon->box);
|
|
if (ret != -1) {
|
|
CalcMonLevelAndStats(mon);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
s32 BoxMon_UpdateGiratinaForme(BoxPokemon *boxMon) {
|
|
int species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
|
|
int heldItem = GetBoxMonData(boxMon, MON_DATA_HELD_ITEM, NULL);
|
|
int forme;
|
|
if (species == SPECIES_GIRATINA) {
|
|
if (heldItem == ITEM_GRISEOUS_ORB) {
|
|
forme = GIRATINA_ORIGIN;
|
|
} else {
|
|
forme = GIRATINA_ALTERED;
|
|
}
|
|
SetBoxMonData(boxMon, MON_DATA_FORME, &forme);
|
|
UpdateBoxMonAbility(boxMon);
|
|
return forme;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void Mon_ForceSetGiratinaOriginForme(Pokemon *mon) {
|
|
s32 forme = GIRATINA_ORIGIN;
|
|
if (GetMonData(mon, MON_DATA_SPECIES, NULL) == SPECIES_GIRATINA) {
|
|
SetBoxMonData(&mon->box, MON_DATA_FORME, &forme);
|
|
UpdateBoxMonAbility(&mon->box);
|
|
CalcMonLevelAndStats(mon);
|
|
}
|
|
}
|
|
|
|
void Party_UpdateAllGiratina_DistortionWorld(PARTY *party, BOOL force_origin) {
|
|
int npoke = GetPartyCount(party);
|
|
int i;
|
|
Pokemon *mon;
|
|
for (i = 0; i < npoke; i++) {
|
|
mon = GetPartyMonByIndex(party, i);
|
|
if (force_origin) {
|
|
Mon_ForceSetGiratinaOriginForme(mon);
|
|
} else {
|
|
Mon_UpdateGiratinaForme(mon);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Mon_UpdateShayminForme(Pokemon *mon, int forme) {
|
|
BoxMon_UpdateShayminForme(&mon->box, forme);
|
|
CalcMonLevelAndStats(mon);
|
|
}
|
|
|
|
void BoxMon_UpdateShayminForme(BoxPokemon *boxMon, int forme) {
|
|
if (GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL) == SPECIES_SHAYMIN) {
|
|
GF_ASSERT(forme <= 1);
|
|
SetBoxMonData(boxMon, MON_DATA_FORME, &forme);
|
|
UpdateBoxMonAbility(boxMon);
|
|
}
|
|
}
|
|
|
|
BOOL Mon_CanUseGracidea(Pokemon *mon) {
|
|
RTCTime time;
|
|
int species = GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
int forme = GetMonData(mon, MON_DATA_FORME, NULL);
|
|
int status = GetMonData(mon, MON_DATA_STATUS, NULL);
|
|
int hp = GetMonData(mon, MON_DATA_HP, NULL);
|
|
BOOL fatefulEncounter = GetMonData(mon, MON_DATA_FATEFUL_ENCOUNTER, NULL);
|
|
GF_RTC_CopyTime(&time);
|
|
|
|
if (species == SPECIES_SHAYMIN && forme == SHAYMIN_LAND && hp != 0 && fatefulEncounter == TRUE && !(status & MON_STATUS_FRZ_MASK) && (time.hour >= 4 && time.hour < 20)) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
void Party_ResetAllShayminToLandForme(PARTY *party) {
|
|
int npoke = GetPartyCount(party);
|
|
int i;
|
|
int species;
|
|
int forme;
|
|
Pokemon *mon;
|
|
for (i = 0; i < npoke; i++) {
|
|
mon = GetPartyMonByIndex(party, i);
|
|
species = GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
forme = GetMonData(mon, MON_DATA_FORME, NULL);
|
|
if (species == SPECIES_SHAYMIN && forme == SHAYMIN_SKY) {
|
|
Mon_UpdateShayminForme(mon, SHAYMIN_LAND);
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL Party_TryResetShaymin(PARTY *party, int min_max, const RTCTime *time) {
|
|
int hour, minute;
|
|
if (time->hour >= 20 || time->hour < 4) {
|
|
hour = time->hour;
|
|
if (hour < 4) {
|
|
hour += 24;
|
|
}
|
|
minute = time->minute + 60 * (hour - 20);
|
|
if (minute < min_max + 1) {
|
|
Party_ResetAllShayminToLandForme(party);
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
hour = time->hour;
|
|
minute = time->minute + 60 * (hour - 4);
|
|
if (minute < min_max) {
|
|
Party_ResetAllShayminToLandForme(party);
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL Mon_UpdateRotomForme(Pokemon *mon, int forme, int defaultSlot) {
|
|
static const u16 forme_moves[ROTOM_FORME_MAX] = {
|
|
MOVE_NONE,
|
|
MOVE_OVERHEAT,
|
|
MOVE_HYDRO_PUMP,
|
|
MOVE_BLIZZARD,
|
|
MOVE_AIR_SLASH,
|
|
MOVE_LEAF_STORM,
|
|
};
|
|
|
|
int i, j;
|
|
int cur_move;
|
|
int new_move;
|
|
if (GetMonData(mon, MON_DATA_SPECIES, NULL) != SPECIES_ROTOM) {
|
|
return FALSE;
|
|
}
|
|
GetMonData(mon, MON_DATA_FORME, NULL);
|
|
new_move = forme_moves[forme];
|
|
for (i = 0; i < MAX_MON_MOVES; i++) {
|
|
cur_move = GetMonData(mon, MON_DATA_MOVE1 + i, NULL);
|
|
for (j = ROTOM_HEAT; j < (unsigned)ROTOM_FORME_MAX; j++) {
|
|
if (cur_move != MOVE_NONE && cur_move == forme_moves[j]) {
|
|
if (new_move != MOVE_NONE) {
|
|
MonSetMoveInSlot_ResetPpUp(mon, new_move, i);
|
|
new_move = MOVE_NONE;
|
|
} else {
|
|
MonDeleteMoveSlot(mon, i);
|
|
i--;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (new_move != MOVE_NONE) {
|
|
for (i = 0; i < MAX_MON_MOVES; i++) {
|
|
if (GetMonData(mon, MON_DATA_MOVE1 + i, NULL) == MOVE_NONE) {
|
|
MonSetMoveInSlot_ResetPpUp(mon, new_move, i);
|
|
break;
|
|
}
|
|
}
|
|
if (i == MAX_MON_MOVES) {
|
|
MonSetMoveInSlot_ResetPpUp(mon, new_move, defaultSlot);
|
|
}
|
|
}
|
|
if (GetMonData(mon, MON_DATA_MOVE1, NULL) == MOVE_NONE) {
|
|
MonSetMoveInSlot_ResetPpUp(mon, MOVE_THUNDER_SHOCK, 0);
|
|
}
|
|
SetMonData(mon, MON_DATA_FORME, &forme);
|
|
UpdateMonAbility(mon);
|
|
CalcMonLevelAndStats(mon);
|
|
return TRUE;
|
|
}
|
|
|
|
void LoadWotbl_HandleAlternateForme(int species, int forme, u16 * wotbl) {
|
|
ReadWholeNarcMemberByIdPair(wotbl, NARC_poketool_personal_wotbl, ResolveMonForme(species, forme));
|
|
}
|
|
|
|
void sub_02071FDC(SOUND_CHATOT *r6, u32 r5, u16 r4, s32 unused, s32 sp18, u32 sp1C, u32 sp20, u32 sp24) {
|
|
#pragma unused(unused)
|
|
if (r4 == SPECIES_CHATOT) {
|
|
if (!sub_02006F30((int)r5)) {
|
|
sub_02006E3C(1);
|
|
PlayCryEx(r5, r4, sp18, sp1C, sp24, 0);
|
|
} else {
|
|
if (sp20) {
|
|
sub_02006E3C(1);
|
|
}
|
|
sub_02006E4C(r6, 0, sp1C, sp18);
|
|
}
|
|
} else {
|
|
PlayCryEx(r5, r4, sp18, sp1C, sp24, 0);
|
|
}
|
|
}
|
|
|
|
void sub_0207204C(SOUND_CHATOT *r7, u32 r6, u16 r5, s32 r4, s32 sp20, u32 sp24, u32 sp28, u32 sp2C, u8 sp30) {
|
|
if (r5 == SPECIES_CHATOT) {
|
|
if (!sub_02006F30((int)r6)) {
|
|
sub_02006E3C(1);
|
|
sub_02006920(r6, r5, sp20, sp24, sp2C, sp30, r4);
|
|
} else {
|
|
if (sp28) {
|
|
sub_02006E3C(1);
|
|
}
|
|
sub_02006EA0(r7, 0, sp24, sp20, sp30);
|
|
}
|
|
} else {
|
|
sub_02006920(r6, r5, sp20, sp24, sp2C, sp30, r4);
|
|
}
|
|
}
|
|
|
|
void sub_020720D4(Pokemon *mon) {
|
|
PlayCry(GetMonData(mon, MON_DATA_SPECIES, NULL), GetMonData(mon, MON_DATA_FORME, NULL));
|
|
}
|
|
|
|
void sub_020720FC(Pokemon *mon, PlayerProfile *a1, u32 pokeball, u32 a3, u32 encounterType, HeapID heap_id) {
|
|
u32 hp;
|
|
sub_0207213C(&mon->box, a1, pokeball, a3, encounterType, heap_id);
|
|
if (pokeball == ITEM_HEAL_BALL) {
|
|
hp = GetMonData(mon, MON_DATA_MAXHP, NULL);
|
|
SetMonData(mon, MON_DATA_HP, &hp);
|
|
hp = 0;
|
|
SetMonData(mon, MON_DATA_STATUS, &hp);
|
|
}
|
|
}
|
|
|
|
void sub_0207213C(BoxPokemon *boxMon, PlayerProfile *playerProfile, u32 pokeball, u32 a3, u32 encounterType, HeapID heap_id) {
|
|
sub_0208F270(boxMon, playerProfile, 0, a3, heap_id);
|
|
SetBoxMonData(boxMon, MON_DATA_GAME_VERSION, (void *)&gGameVersion);
|
|
SetBoxMonData(boxMon, MON_DATA_POKEBALL, &pokeball);
|
|
SetBoxMonData(boxMon, MON_DATA_ENCOUNTER_TYPE, &encounterType);
|
|
}
|
|
|
|
void sub_0207217C(Pokemon *mon, PlayerProfile *a1, u32 pokeball, u32 a3, u32 encounterType, HeapID heap_id) {
|
|
sub_02072190(&mon->box, a1, pokeball, a3, encounterType, heap_id);
|
|
}
|
|
|
|
void sub_02072190(BoxPokemon *boxMon, PlayerProfile *a1, u32 pokeball, u32 a3, u32 encounterType, HeapID heap_id) {
|
|
sub_0207213C(boxMon, a1, pokeball, a3, encounterType, heap_id);
|
|
}
|
|
|
|
static const u16 sItemOdds[2][2] = {
|
|
{45, 95},
|
|
{20, 80},
|
|
};
|
|
|
|
void WildMonSetRandomHeldItem(Pokemon *mon, u32 a1, u32 a2) {
|
|
u32 chance;
|
|
u16 species;
|
|
u16 forme;
|
|
u16 item1;
|
|
u16 item2;
|
|
if (!(a1 & 0x81)) {
|
|
chance = (u32)(LCRandom() % 100);
|
|
species = (u16)GetMonData(mon, MON_DATA_SPECIES, 0);
|
|
forme = (u16)GetMonData(mon, MON_DATA_FORME, 0);
|
|
item1 = (u16)GetMonBaseStat_HandleAlternateForme(species, forme, BASE_ITEM_1);
|
|
item2 = (u16)GetMonBaseStat_HandleAlternateForme(species, forme, BASE_ITEM_2);
|
|
if (item1 == item2 && item1 != ITEM_NONE) {
|
|
SetMonData(mon, MON_DATA_HELD_ITEM, &item1);
|
|
} else {
|
|
if (chance >= sItemOdds[a2][0]) {
|
|
if (chance < sItemOdds[a2][1]) {
|
|
SetMonData(mon, MON_DATA_HELD_ITEM, &item1);
|
|
} else {
|
|
SetMonData(mon, MON_DATA_HELD_ITEM, &item2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL GetMonTMHMCompat(Pokemon *mon, u8 tmhm) {
|
|
return GetBoxMonTMHMCompat(&mon->box, tmhm);
|
|
}
|
|
|
|
BOOL GetBoxMonTMHMCompat(BoxPokemon *boxMon, u8 tmhm) {
|
|
u16 species;
|
|
u32 forme;
|
|
|
|
species = GetBoxMonData(boxMon, MON_DATA_SPECIES2, NULL);
|
|
forme = GetBoxMonData(boxMon, MON_DATA_FORME, NULL);
|
|
return GetTMHMCompatBySpeciesAndForme(species, forme, tmhm);
|
|
}
|
|
|
|
BOOL GetTMHMCompatBySpeciesAndForme(u16 species, u32 forme, u8 tmhm) {
|
|
u32 mask;
|
|
enum BaseStat baseStat;
|
|
if (species == SPECIES_EGG) {
|
|
return FALSE;
|
|
}
|
|
|
|
// mask = 1 << (a2 % 32);
|
|
// baseStat = BASE_TMHM_1 + (a2 / 32);
|
|
if (tmhm < 32) {
|
|
mask = 1 << tmhm;
|
|
baseStat = BASE_TMHM_1;
|
|
} else if (tmhm < 64) {
|
|
mask = 1 << (tmhm - 32);
|
|
baseStat = BASE_TMHM_2;
|
|
} else if (tmhm < 96) {
|
|
mask = 1 << (tmhm - 64);
|
|
baseStat = BASE_TMHM_3;
|
|
} else {
|
|
mask = 1 << (tmhm - 96);
|
|
baseStat = BASE_TMHM_4;
|
|
}
|
|
return (GetMonBaseStat_HandleAlternateForme(species, forme, baseStat) & mask) != 0;
|
|
}
|
|
|
|
void UpdateMonAbility(Pokemon *mon) {
|
|
UpdateBoxMonAbility(&mon->box);
|
|
}
|
|
|
|
void UpdateBoxMonAbility(BoxPokemon *boxMon) {
|
|
BOOL decry = AcquireBoxMonLock(boxMon);
|
|
int species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
|
|
int pid = GetBoxMonData(boxMon, MON_DATA_PERSONALITY, NULL);
|
|
int forme = GetBoxMonData(boxMon, MON_DATA_FORME, NULL);
|
|
int ability1 = GetMonBaseStat_HandleAlternateForme(species, forme, BASE_ABILITY_1);
|
|
int ability2 = GetMonBaseStat_HandleAlternateForme(species, forme, BASE_ABILITY_2);
|
|
if (ability2 != ABILITY_NONE) {
|
|
if (pid & 1) {
|
|
SetBoxMonData(boxMon, MON_DATA_ABILITY, &ability2);
|
|
} else {
|
|
SetBoxMonData(boxMon, MON_DATA_ABILITY, &ability1);
|
|
}
|
|
} else {
|
|
SetBoxMonData(boxMon, MON_DATA_ABILITY, &ability1);
|
|
}
|
|
ReleaseBoxMonLock(boxMon, decry);
|
|
}
|
|
|
|
void SetMonPersonality(Pokemon *mon, u32 personality) {
|
|
PokemonDataBlockA * r4;
|
|
PokemonDataBlockB * r6;
|
|
PokemonDataBlockC * r7;
|
|
PokemonDataBlockD * sp8;
|
|
PokemonDataBlockA * spC;
|
|
PokemonDataBlockB * sp10;
|
|
PokemonDataBlockC * sp14;
|
|
PokemonDataBlockD * sp18;
|
|
Pokemon *tmpMon;
|
|
|
|
tmpMon = AllocMonZeroed(HEAP_ID_0);
|
|
CopyPokemonToPokemon(mon, tmpMon);
|
|
r4 = &GetSubstruct(&tmpMon->box, mon->box.pid, 0)->blockA;
|
|
r6 = &GetSubstruct(&tmpMon->box, mon->box.pid, 1)->blockB;
|
|
r7 = &GetSubstruct(&tmpMon->box, mon->box.pid, 2)->blockC;
|
|
sp8 = &GetSubstruct(&tmpMon->box, mon->box.pid, 3)->blockD;
|
|
spC = &GetSubstruct(&mon->box, personality, 0)->blockA;
|
|
sp10 = &GetSubstruct(&mon->box, personality, 1)->blockB;
|
|
sp14 = &GetSubstruct(&mon->box, personality, 2)->blockC;
|
|
sp18 = &GetSubstruct(&mon->box, personality, 3)->blockD;
|
|
|
|
DECRYPT_BOX(&tmpMon->box);
|
|
DECRYPT_PTY(mon);
|
|
DECRYPT_BOX(&mon->box);
|
|
mon->box.pid = personality;
|
|
*spC = *r4;
|
|
*sp10 = *r6;
|
|
*sp14 = *r7;
|
|
*sp18 = *sp8;
|
|
mon->box.checksum = CHECKSUM(&mon->box);
|
|
ENCRYPT_BOX(&mon->box);
|
|
ENCRYPT_PTY(mon);
|
|
FreeToHeap(tmpMon);
|
|
}
|
|
|
|
u32 ChangePersonalityToNatureGenderAndAbility(u32 pid, u16 species, u8 nature, u8 gender, u8 ability, BOOL gen_mode) {
|
|
// u32 pid is a random seed to convolve into a pid that bears the
|
|
// requested nature, gender, and ability. This is used to make
|
|
// pokemon caught on the Pokewalker, and the Spiky-Eared Pichu.
|
|
// In both actual use cases, u32 pid is OTID, which means r4 and r1
|
|
// are always SID ^ TID.
|
|
GF_ASSERT(ability < 2);
|
|
GF_ASSERT(gender != 0xFF);
|
|
if (gen_mode) {
|
|
u32 r4 = ((pid & 0xFFFF0000) >> 16) ^ (u16)pid;
|
|
pid = GenPersonalityByGenderAndNature(species, gender, nature);
|
|
if ((pid & 1) != ability) {
|
|
// Force correct ability for gender and nature pid
|
|
pid++;
|
|
}
|
|
pid |= ((u16)pid ^ r4) << 16;
|
|
} else {
|
|
u32 r1;
|
|
u8 ratio = GetMonBaseStat(species, BASE_GENDER_RATIO);
|
|
GF_ASSERT((nature & 1) == ability);
|
|
r1 = ((pid & 0xFFFF0000) >> 16) ^ (u16)pid;
|
|
pid = (0xFF00 ^ (r1 & 0xFF00)) << 16;
|
|
// Force the pid to have the requested nature
|
|
pid += nature - (pid % 25);
|
|
// Maintaining that pid%25 is nature, and pid&1 is ability,
|
|
// ensure pid&0xFF compared to the gender ratio yields gender
|
|
switch (ratio) {
|
|
case MON_RATIO_MALE:
|
|
case MON_RATIO_FEMALE:
|
|
case MON_RATIO_UNKNOWN:
|
|
break;
|
|
default:
|
|
if (gender == MON_MALE) {
|
|
if (ratio > (u8)pid) {
|
|
// pid is female; force pid to become male
|
|
pid += 25 * ((ratio - (u8)pid) / 25u + 1);
|
|
if ((pid & 1) != ability) {
|
|
GF_ASSERT((u8)pid <= 230);
|
|
pid += 25;
|
|
}
|
|
}
|
|
} else {
|
|
if (ratio < (u8)pid) {
|
|
// pid is male; force pid to become female
|
|
pid -= 25 * (((u8)pid - ratio) / 25u + 1);
|
|
if ((pid & 1) != ability) {
|
|
GF_ASSERT((u8)pid >= 25);
|
|
pid -= 25;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return pid;
|
|
}
|
|
|
|
void LoadMonPersonal(int species, BASE_STATS *personal) {
|
|
ReadWholeNarcMemberByIdPair(personal, NARC_poketool_personal_personal, species);
|
|
}
|
|
|
|
void LoadMonBaseStats_HandleAlternateForme(int species, int forme, BASE_STATS *personal) {
|
|
ReadWholeNarcMemberByIdPair(personal, NARC_poketool_personal_personal, ResolveMonForme(species, forme));
|
|
}
|
|
|
|
void LoadMonEvolutionTable(u16 species, struct Evolution *evo) {
|
|
ReadWholeNarcMemberByIdPair(evo, NARC_poketool_personal_evo, species);
|
|
}
|
|
|
|
void MonEncryptSegment(void *data, u32 size, u32 seed) {
|
|
_MonEncryptSegment(data, size, seed);
|
|
}
|
|
|
|
void MonDecryptSegment(void *data, u32 size, u32 seed) {
|
|
_MonDecryptSegment(data, size, seed);
|
|
}
|
|
|
|
u32 CalcMonChecksum(void * _data, u32 size) {
|
|
int i;
|
|
const u16 *data = _data;
|
|
u16 ret = 0;
|
|
for (i = 0; i < size / 2; i++) {
|
|
ret += data[i];
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
PokemonDataBlock *GetSubstruct(BoxPokemon *boxMon, u32 pid, u8 which) {
|
|
static const u8 offsets[32][4] = {
|
|
{0x00, 0x20, 0x40, 0x60},
|
|
{0x00, 0x20, 0x60, 0x40},
|
|
{0x00, 0x40, 0x20, 0x60},
|
|
{0x00, 0x60, 0x20, 0x40},
|
|
{0x00, 0x40, 0x60, 0x20},
|
|
{0x00, 0x60, 0x40, 0x20},
|
|
{0x20, 0x00, 0x40, 0x60},
|
|
{0x20, 0x00, 0x60, 0x40},
|
|
{0x40, 0x00, 0x20, 0x60},
|
|
{0x60, 0x00, 0x20, 0x40},
|
|
{0x40, 0x00, 0x60, 0x20},
|
|
{0x60, 0x00, 0x40, 0x20},
|
|
{0x20, 0x40, 0x00, 0x60},
|
|
{0x20, 0x60, 0x00, 0x40},
|
|
{0x40, 0x20, 0x00, 0x60},
|
|
{0x60, 0x20, 0x00, 0x40},
|
|
{0x40, 0x60, 0x00, 0x20},
|
|
{0x60, 0x40, 0x00, 0x20},
|
|
{0x20, 0x40, 0x60, 0x00},
|
|
{0x20, 0x60, 0x40, 0x00},
|
|
{0x40, 0x20, 0x60, 0x00},
|
|
{0x60, 0x20, 0x40, 0x00},
|
|
{0x40, 0x60, 0x20, 0x00},
|
|
{0x60, 0x40, 0x20, 0x00},
|
|
{0x00, 0x20, 0x40, 0x60},
|
|
{0x00, 0x20, 0x60, 0x40},
|
|
{0x00, 0x40, 0x20, 0x60},
|
|
{0x00, 0x60, 0x20, 0x40},
|
|
{0x00, 0x40, 0x60, 0x20},
|
|
{0x00, 0x60, 0x40, 0x20},
|
|
{0x20, 0x00, 0x40, 0x60},
|
|
{0x20, 0x00, 0x60, 0x40},
|
|
};
|
|
|
|
pid = ((pid & 0x3E000) >> 13);
|
|
GF_ASSERT(which <= 3);
|
|
return (PokemonDataBlock *)((char *)boxMon->substructs + offsets[pid][which]);
|
|
}
|
|
|
|
int ResolveMonForme(int species, int forme) {
|
|
switch (species) {
|
|
case SPECIES_DEOXYS:
|
|
if (forme != DEOXYS_NORMAL && forme <= DEOXYS_FORME_MAX - 1) {
|
|
return SPECIES_DEOXYS_ATK + forme - DEOXYS_ATTACK;
|
|
}
|
|
break;
|
|
case SPECIES_WORMADAM:
|
|
if (forme != WORMADAM_PLANT && forme <= WORMADAM_FORME_MAX - 1) {
|
|
return SPECIES_WORMADAM_SANDY + forme - WORMADAM_SANDY;
|
|
}
|
|
break;
|
|
case SPECIES_GIRATINA:
|
|
if (forme != GIRATINA_ALTERED && forme <= GIRATINA_FORME_MAX - 1) {
|
|
return SPECIES_GIRATINA_ORIGIN + forme - GIRATINA_ORIGIN;
|
|
}
|
|
break;
|
|
case SPECIES_SHAYMIN:
|
|
if (forme != SHAYMIN_LAND && forme <= SHAYMIN_FORME_MAX - 1) {
|
|
return SPECIES_SHAYMIN_SKY + forme - SHAYMIN_SKY;
|
|
}
|
|
break;
|
|
case SPECIES_ROTOM:
|
|
if (forme != ROTOM_NORMAL && forme <= ROTOM_FORME_MAX - 1) {
|
|
return SPECIES_ROTOM_HEAT + forme - ROTOM_HEAT;
|
|
}
|
|
break;
|
|
}
|
|
return species;
|
|
}
|
|
|
|
u32 MaskOfFlagNo(int flagno) {
|
|
// This is completely inane.
|
|
int i;
|
|
u32 ret = 1;
|
|
GF_ASSERT(flagno < 32);
|
|
for (i = 0; i < flagno; i++) {
|
|
ret <<= 1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int LowestFlagNo(u32 mask) {
|
|
// ctz
|
|
int i;
|
|
u32 bit = 1;
|
|
for (i = 0; i < 32; i++) {
|
|
if (mask & bit) {
|
|
break;
|
|
}
|
|
bit <<= 1;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static const u16 sBattleFrontierBanlist[18] = {
|
|
SPECIES_MEWTWO,
|
|
SPECIES_MEW,
|
|
SPECIES_LUGIA,
|
|
SPECIES_HO_OH,
|
|
SPECIES_CELEBI,
|
|
SPECIES_KYOGRE,
|
|
SPECIES_GROUDON,
|
|
SPECIES_RAYQUAZA,
|
|
SPECIES_JIRACHI,
|
|
SPECIES_DEOXYS,
|
|
SPECIES_DIALGA,
|
|
SPECIES_PALKIA,
|
|
SPECIES_GIRATINA,
|
|
SPECIES_PHIONE,
|
|
SPECIES_MANAPHY,
|
|
SPECIES_DARKRAI,
|
|
SPECIES_SHAYMIN,
|
|
SPECIES_ARCEUS,
|
|
};
|
|
|
|
BOOL IsPokemonBannedFromBattleFrontier(u16 species, u16 forme) {
|
|
int i;
|
|
for (i = 0; i < NELEMS(sBattleFrontierBanlist); i++) {
|
|
if (species == sBattleFrontierBanlist[i]) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
if (species == SPECIES_PICHU && forme == PICHU_SPIKY_EAR) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
u16 GetBannedBattleFrontierPokemon(u32 idx) {
|
|
if (idx >= NELEMS(sBattleFrontierBanlist)) {
|
|
idx = 0;
|
|
}
|
|
return sBattleFrontierBanlist[idx];
|
|
}
|
|
|
|
static const u16 sMythicalMonsList[9] = {
|
|
SPECIES_MEW,
|
|
SPECIES_CELEBI,
|
|
SPECIES_JIRACHI,
|
|
SPECIES_DEOXYS,
|
|
SPECIES_PHIONE,
|
|
SPECIES_MANAPHY,
|
|
SPECIES_DARKRAI,
|
|
SPECIES_SHAYMIN,
|
|
SPECIES_ARCEUS,
|
|
};
|
|
|
|
BOOL SpeciesIsMythical(u16 species) {
|
|
int i;
|
|
for (i = 0; i < NELEMS(sMythicalMonsList); i++) {
|
|
if (species == sMythicalMonsList[i]) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL MonCheckFrontierIneligibility(Pokemon *mon) {
|
|
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
u16 forme = GetMonData(mon, MON_DATA_FORME, NULL);
|
|
return IsPokemonBannedFromBattleFrontier(species, forme);
|
|
}
|
|
|
|
BOOL BoxmonBelongsToPlayer(BoxPokemon *boxMon, PlayerProfile * profile, HeapID heap_id) {
|
|
u32 myId = PlayerProfile_GetTrainerID(profile);
|
|
u32 otId = GetBoxMonData(boxMon, MON_DATA_OTID, NULL);
|
|
u32 myGender = PlayerProfile_GetTrainerGender(profile);
|
|
u32 otGender = GetBoxMonData(boxMon, MON_DATA_MET_GENDER, NULL);
|
|
String * r7 = PlayerProfile_GetPlayerName_NewString(profile, heap_id);
|
|
String * r6 = String_New(PLAYER_NAME_LENGTH + 1, heap_id);
|
|
BOOL ret = FALSE;
|
|
GetBoxMonData(boxMon, MON_DATA_OT_NAME_2, r6);
|
|
if (myId == otId && myGender == otGender && String_Compare(r7, r6) == 0) {
|
|
ret = TRUE;
|
|
}
|
|
String_Delete(r6);
|
|
String_Delete(r7);
|
|
return ret;
|
|
}
|
|
|
|
int TrainerClassToBackpicID(int trainerClass, int a1) {
|
|
switch (trainerClass) {
|
|
case TRAINERCLASS_PKMN_TRAINER_ETHAN:
|
|
case TRAINERCLASS_PKMN_TRAINER_LYRA:
|
|
if (a1) {
|
|
return trainerClass - TRAINERCLASS_PKMN_TRAINER_ETHAN + TRAINER_BACKPIC_ETHAN_2;
|
|
} else {
|
|
return trainerClass - TRAINERCLASS_PKMN_TRAINER_ETHAN + TRAINER_BACKPIC_ETHAN;
|
|
}
|
|
case TRAINERCLASS_RIVAL:
|
|
return TRAINER_BACKPIC_RIVAL;
|
|
case TRAINERCLASS_PKMN_TRAINER_LUCAS_DP:
|
|
case TRAINERCLASS_PKMN_TRAINER_DAWN_DP:
|
|
return trainerClass - TRAINERCLASS_PKMN_TRAINER_LUCAS_DP + TRAINER_BACKPIC_LUCAS_DP;
|
|
case TRAINERCLASS_PKMN_TRAINER_LANCE:
|
|
return TRAINER_BACKPIC_LANCE;
|
|
case TRAINERCLASS_PKMN_TRAINER_CHERYL:
|
|
case TRAINERCLASS_PKMN_TRAINER_RILEY:
|
|
case TRAINERCLASS_PKMN_TRAINER_BUCK:
|
|
case TRAINERCLASS_PKMN_TRAINER_MIRA:
|
|
case TRAINERCLASS_PKMN_TRAINER_MARLEY:
|
|
return trainerClass - TRAINERCLASS_PKMN_TRAINER_CHERYL + TRAINER_BACKPIC_CHERYL;
|
|
case TRAINERCLASS_PKMN_TRAINER_LUCAS_PT:
|
|
case TRAINERCLASS_PKMN_TRAINER_DAWN_PT:
|
|
return trainerClass - TRAINERCLASS_PKMN_TRAINER_LUCAS_PT + TRAINER_BACKPIC_LUCAS_PT;
|
|
default:
|
|
if (TrainerClass_GetGenderOrTrainerCount(trainerClass) == TRAINER_FEMALE) {
|
|
return TRAINER_BACKPIC_LYRA;
|
|
} else {
|
|
return TRAINER_BACKPIC_ETHAN;
|
|
}
|
|
}
|
|
return trainerClass;
|
|
}
|
|
|
|
void Pokemon_RemoveCapsule(Pokemon *mon) {
|
|
u8 sp0 = 0;
|
|
CAPSULE sp1;
|
|
MI_CpuClearFast(&sp1, sizeof(sp1));
|
|
SetMonData(mon, MON_DATA_CAPSULE, &sp0);
|
|
SetMonData(mon, MON_DATA_SEAL_COORDS, &sp1);
|
|
}
|
|
|
|
void RestoreBoxMonPP(BoxPokemon *boxMon) {
|
|
int i;
|
|
u8 pp;
|
|
BOOL decry = AcquireBoxMonLock(boxMon);
|
|
for (i = 0; i < MAX_MON_MOVES; i++) {
|
|
if (GetBoxMonData(boxMon, MON_DATA_MOVE1 + i, NULL) != MOVE_NONE) {
|
|
pp = (u8)GetBoxMonData(boxMon, MON_DATA_MOVE1MAXPP + i, NULL);
|
|
SetBoxMonData(boxMon, MON_DATA_MOVE1PP + i, &pp);
|
|
}
|
|
}
|
|
ReleaseBoxMonLock(boxMon, decry);
|
|
}
|
|
|
|
void sub_02072914(NARC *narc, struct UnkStruct_02072914_sub *dest, u16 a2, u16 a3) {
|
|
struct UnkStruct_02072914 sp4;
|
|
int r5 = (a3 & 1 ? 0 : 1);
|
|
NARC_ReadFromMember(narc, 0, a2 * sizeof(struct UnkStruct_02072914), sizeof(struct UnkStruct_02072914), &sp4);
|
|
MI_CpuCopy8(&sp4.unk0[r5].unk_3[0], dest, sizeof(struct UnkStruct_02072914_sub_sub) * 10);
|
|
}
|
|
|
|
void sub_0207294C(NARC *narc, void *a1, void *a2, u16 a3, int a4, int a5, int a6) {
|
|
struct UnkStruct_02072914 spA;
|
|
struct UnkStruct_0207294C sp4;
|
|
int r4 = (a4 == 2 ? 0 : 1);
|
|
NARC_ReadFromMember(narc, 0, a3 * sizeof(struct UnkStruct_02072914), sizeof(struct UnkStruct_02072914), &spA);
|
|
sp4.unk_0 = spA.unk0[r4].unk_1;
|
|
sp4.unk_2 = spA.unk0[r4].unk_2;
|
|
sp4.unk_4 = a5;
|
|
sub_02016F40(a1, a2, &sp4, a6);
|
|
}
|
|
|
|
void sub_020729A4(NARC *narc, u8 *ret, u16 a2, u16 a3) {
|
|
struct UnkStruct_02072914 sp4;
|
|
int r5 = (a3 & 1 ? 0 : 1);
|
|
NARC_ReadFromMember(narc, 0, a2 * sizeof(struct UnkStruct_02072914), sizeof(struct UnkStruct_02072914), &sp4);
|
|
*ret = sp4.unk0[r5].unk_0;
|
|
}
|
|
|
|
void sub_020729D8(NARC *narc, s8 *ret, u16 a2, u16 a3) {
|
|
struct UnkStruct_02072914 sp4;
|
|
NARC_ReadFromMember(narc, 0, a2 * sizeof(struct UnkStruct_02072914), sizeof(struct UnkStruct_02072914), &sp4);
|
|
*ret = sp4.unk_56;
|
|
}
|
|
|
|
void sub_020729FC(NARC *narc, s8 *ret, u16 a2, u16 a3) {
|
|
struct UnkStruct_02072914 sp4;
|
|
NARC_ReadFromMember(narc, 0, a2 * sizeof(struct UnkStruct_02072914), sizeof(struct UnkStruct_02072914), &sp4);
|
|
*ret = sp4.unk_57;
|
|
}
|
|
|
|
void sub_02072A20(NARC *narc, u8 *ret, u16 a2, u16 a3) {
|
|
struct UnkStruct_02072914 sp4;
|
|
NARC_ReadFromMember(narc, 0, a2 * sizeof(struct UnkStruct_02072914), sizeof(struct UnkStruct_02072914), &sp4);
|
|
*ret = sp4.unk_58;
|
|
}
|
|
|
|
BOOL SetTrMonCapsule(int a0, Pokemon *mon, HeapID heap_id) {
|
|
CAPSULE capsule;
|
|
int data;
|
|
NARC *narc;
|
|
if (a0 == 0) {
|
|
return FALSE;
|
|
}
|
|
narc = NARC_New(NARC_application_custom_ball_edit_gs_cb_data, heap_id);
|
|
data = 1;
|
|
NARC_ReadFromMember(narc, 0, (a0 - 1) * sizeof(CAPSULE), sizeof(CAPSULE), &capsule);
|
|
SetMonData(mon, MON_DATA_CAPSULE, &data);
|
|
SetMonData(mon, MON_DATA_SEAL_COORDS, &capsule);
|
|
NARC_Delete(narc);
|
|
return TRUE;
|
|
}
|
|
|
|
void sub_02072A98(Pokemon *mon, struct UnkPokemonStruct_02072A98 *dest) {
|
|
PokemonDataBlockA *dbA; // r5
|
|
PokemonDataBlockB *dbB; // sp4
|
|
PokemonDataBlockC *dbC; // r7
|
|
PokemonDataBlockD *dbD; // ip
|
|
BoxPokemon *boxMon; // r6
|
|
int i;
|
|
|
|
if (!mon->box.party_lock) {
|
|
DECRYPT_PTY(mon);
|
|
DECRYPT_BOX(&mon->box);
|
|
}
|
|
boxMon = Mon_GetBoxMon(mon);
|
|
dbA = &GetSubstruct(boxMon, boxMon->pid, 0)->blockA;
|
|
dbB = &GetSubstruct(boxMon, boxMon->pid, 1)->blockB;
|
|
dbC = &GetSubstruct(boxMon, boxMon->pid, 2)->blockC;
|
|
dbD = &GetSubstruct(boxMon, boxMon->pid, 3)->blockD;
|
|
|
|
dest->pid = boxMon->pid;
|
|
dest->party_lock = FALSE;
|
|
dest->box_lock = FALSE;
|
|
dest->checksum_fail = boxMon->checksum_fail;
|
|
|
|
dest->species = dbA->species;
|
|
dest->heldItem = dbA->heldItem;
|
|
dest->otID = dbA->otID;
|
|
dest->exp = dbA->exp;
|
|
dest->friendship = dbA->friendship;
|
|
dest->ability = dbA->ability;
|
|
dest->hpEV = dbA->hpEV;
|
|
dest->atkEV = dbA->atkEV;
|
|
dest->defEV = dbA->defEV;
|
|
dest->spdEV = dbA->spdEV;
|
|
dest->spatkEV = dbA->spatkEV;
|
|
dest->spdefEV = dbA->spdefEV;
|
|
dest->originLanguage = dbA->originLanguage;
|
|
|
|
for (i = 0; i < MAX_MON_MOVES; i++) {
|
|
dest->moves[i] = dbB->moves[i];
|
|
dest->movePP[i] = dbB->movePP[i];
|
|
dest->movePpUps[i] = dbB->movePpUps[i];
|
|
}
|
|
dest->hpIV = dbB->hpIV;
|
|
dest->atkIV = dbB->atkIV;
|
|
dest->defIV = dbB->defIV;
|
|
dest->spdIV = dbB->spdIV;
|
|
dest->spatkIV = dbB->spatkIV;
|
|
dest->spdefIV = dbB->spdefIV;
|
|
dest->isEgg = dbB->isEgg;
|
|
dest->isNicknamed = dbB->isNicknamed;
|
|
dest->fatefulEncounter = dbB->fatefulEncounter;
|
|
dest->gender = dbB->gender;
|
|
dest->alternateForm = dbB->alternateForm;
|
|
|
|
for (i = 0; i < POKEMON_NAME_LENGTH + 1; i++) {
|
|
dest->nickname[i] = dbC->nickname[i];
|
|
}
|
|
|
|
for (i = 0; i < PLAYER_NAME_LENGTH + 1; i++) {
|
|
dest->otTrainerName[i] = dbD->otTrainerName[i];
|
|
}
|
|
dest->pokeball = dbD->pokeball;
|
|
|
|
dest->status = mon->party.status;
|
|
dest->level = mon->party.level;
|
|
dest->capsule = mon->party.capsule;
|
|
dest->hp = mon->party.hp;
|
|
dest->maxHp = mon->party.maxHp;
|
|
dest->atk = mon->party.atk;
|
|
dest->def = mon->party.def;
|
|
dest->speed = mon->party.speed;
|
|
dest->spatk = mon->party.spatk;
|
|
dest->spdef = mon->party.spdef;
|
|
|
|
if (!mon->box.party_lock) {
|
|
ENCRYPT_PTY(mon);
|
|
ENCRYPT_BOX(&mon->box);
|
|
}
|
|
}
|
|
|
|
void sub_02072D64(const struct UnkPokemonStruct_02072A98 *src, Pokemon *mon) {
|
|
PokemonDataBlockA *dbA; // r5
|
|
PokemonDataBlockB *dbB; // r6
|
|
PokemonDataBlockC *dbC; // r7
|
|
PokemonDataBlockD *dbD; // ip
|
|
BoxPokemon *boxMon; // sp4
|
|
int i;
|
|
|
|
MI_CpuClearFast(mon, sizeof(Pokemon));
|
|
boxMon = Mon_GetBoxMon(mon);
|
|
dbA = &GetSubstruct(boxMon, src->pid, 0)->blockA;
|
|
dbB = &GetSubstruct(boxMon, src->pid, 1)->blockB;
|
|
dbC = &GetSubstruct(boxMon, src->pid, 2)->blockC;
|
|
dbD = &GetSubstruct(boxMon, src->pid, 3)->blockD;
|
|
|
|
boxMon->pid = src->pid;
|
|
boxMon->party_lock = FALSE;
|
|
boxMon->box_lock = FALSE;
|
|
boxMon->checksum_fail = src->checksum_fail;
|
|
|
|
dbA->species = src->species;
|
|
dbA->heldItem = src->heldItem;
|
|
dbA->otID = src->otID;
|
|
dbA->exp = src->exp;
|
|
dbA->friendship = src->friendship;
|
|
dbA->ability = src->ability;
|
|
dbA->hpEV = src->hpEV;
|
|
dbA->atkEV = src->atkEV;
|
|
dbA->defEV = src->defEV;
|
|
dbA->spdEV = src->spdEV;
|
|
dbA->spatkEV = src->spatkEV;
|
|
dbA->spdefEV = src->spdefEV;
|
|
dbA->originLanguage = src->originLanguage;
|
|
|
|
for (i = 0; i < MAX_MON_MOVES; i++) {
|
|
dbB->moves[i] = src->moves[i];
|
|
dbB->movePP[i] = src->movePP[i];
|
|
dbB->movePpUps[i] = src->movePpUps[i];
|
|
}
|
|
dbB->hpIV = src->hpIV;
|
|
dbB->atkIV = src->atkIV;
|
|
dbB->defIV = src->defIV;
|
|
dbB->spdIV = src->spdIV;
|
|
dbB->spatkIV = src->spatkIV;
|
|
dbB->spdefIV = src->spdefIV;
|
|
dbB->isEgg = src->isEgg;
|
|
dbB->isNicknamed = src->isNicknamed;
|
|
dbB->fatefulEncounter = src->fatefulEncounter;
|
|
dbB->gender = src->gender;
|
|
dbB->alternateForm = src->alternateForm;
|
|
|
|
for (i = 0; i < POKEMON_NAME_LENGTH + 1; i++) {
|
|
dbC->nickname[i] = src->nickname[i];
|
|
}
|
|
|
|
for (i = 0; i < PLAYER_NAME_LENGTH + 1; i++) {
|
|
dbD->otTrainerName[i] = src->otTrainerName[i];
|
|
}
|
|
dbD->HGSS_Pokeball = src->pokeball;
|
|
if (src->pokeball <= BALL_CHERISH) {
|
|
dbD->pokeball = src->pokeball;
|
|
} else {
|
|
dbD->pokeball = BALL_POKE;
|
|
}
|
|
|
|
mon->party.status = src->status;
|
|
mon->party.level = src->level;
|
|
mon->party.capsule = src->capsule;
|
|
mon->party.hp = src->hp;
|
|
mon->party.maxHp = src->maxHp;
|
|
mon->party.atk = src->atk;
|
|
mon->party.def = src->def;
|
|
mon->party.speed = src->speed;
|
|
mon->party.spatk = src->spatk;
|
|
mon->party.spdef = src->spdef;
|
|
|
|
ENCRYPT_PTY(mon);
|
|
mon->box.checksum = CHECKSUM(&mon->box);
|
|
ENCRYPT_BOX(&mon->box);
|
|
}
|
|
|
|
u32 _u32_getDigitN(u32 num, u8 digit) {
|
|
const u32 sp0[8] = {
|
|
1,
|
|
10,
|
|
100,
|
|
1000,
|
|
10000,
|
|
100000,
|
|
1000000,
|
|
10000000
|
|
};
|
|
return (num % sp0[digit + 1]) / sp0[digit];
|
|
}
|
|
|
|
s16 PokeathlonStatScoreToStars(s16 a0) {
|
|
if (a0 <= -120) {
|
|
return -4;
|
|
} else if (a0 <= -80) {
|
|
return -3;
|
|
} else if (a0 <= -40) {
|
|
return -2;
|
|
} else if (a0 <= -15) {
|
|
return -1;
|
|
} else if (a0 <= 14) {
|
|
return 0;
|
|
} else if (a0 <= 39) {
|
|
return 1;
|
|
} else if (a0 <= 79) {
|
|
return 2;
|
|
} else if (a0 <= 119) {
|
|
return 3;
|
|
} else {
|
|
return 4;
|
|
}
|
|
}
|
|
|
|
static const u16 sPokeathlonPerformanceArcIdxs[] = {
|
|
0,
|
|
0,
|
|
1,
|
|
2,
|
|
3,
|
|
4,
|
|
5,
|
|
6,
|
|
7,
|
|
8,
|
|
9,
|
|
10,
|
|
11,
|
|
12,
|
|
13,
|
|
14,
|
|
15,
|
|
16,
|
|
17,
|
|
18,
|
|
19,
|
|
20,
|
|
21,
|
|
22,
|
|
23,
|
|
24,
|
|
25,
|
|
26,
|
|
27,
|
|
28,
|
|
29,
|
|
30,
|
|
31,
|
|
32,
|
|
33,
|
|
34,
|
|
35,
|
|
36,
|
|
37,
|
|
38,
|
|
39,
|
|
40,
|
|
41,
|
|
42,
|
|
43,
|
|
44,
|
|
45,
|
|
46,
|
|
47,
|
|
48,
|
|
49,
|
|
50,
|
|
51,
|
|
52,
|
|
53,
|
|
54,
|
|
55,
|
|
56,
|
|
57,
|
|
58,
|
|
59,
|
|
60,
|
|
61,
|
|
62,
|
|
63,
|
|
64,
|
|
65,
|
|
66,
|
|
67,
|
|
68,
|
|
69,
|
|
70,
|
|
71,
|
|
72,
|
|
73,
|
|
74,
|
|
75,
|
|
76,
|
|
77,
|
|
78,
|
|
79,
|
|
80,
|
|
81,
|
|
82,
|
|
83,
|
|
84,
|
|
85,
|
|
86,
|
|
87,
|
|
88,
|
|
89,
|
|
90,
|
|
91,
|
|
92,
|
|
93,
|
|
94,
|
|
95,
|
|
96,
|
|
97,
|
|
98,
|
|
99,
|
|
100,
|
|
101,
|
|
102,
|
|
103,
|
|
104,
|
|
105,
|
|
106,
|
|
107,
|
|
108,
|
|
109,
|
|
110,
|
|
111,
|
|
112,
|
|
113,
|
|
114,
|
|
115,
|
|
116,
|
|
117,
|
|
118,
|
|
119,
|
|
120,
|
|
121,
|
|
122,
|
|
123,
|
|
124,
|
|
125,
|
|
126,
|
|
127,
|
|
128,
|
|
129,
|
|
130,
|
|
131,
|
|
132,
|
|
133,
|
|
134,
|
|
135,
|
|
136,
|
|
137,
|
|
138,
|
|
139,
|
|
140,
|
|
141,
|
|
142,
|
|
143,
|
|
144,
|
|
145,
|
|
146,
|
|
147,
|
|
148,
|
|
149,
|
|
150,
|
|
151,
|
|
152,
|
|
153,
|
|
154,
|
|
155,
|
|
156,
|
|
157,
|
|
158,
|
|
159,
|
|
160,
|
|
161,
|
|
162,
|
|
163,
|
|
164,
|
|
165,
|
|
166,
|
|
167,
|
|
168,
|
|
169,
|
|
170,
|
|
171,
|
|
173,
|
|
174,
|
|
175,
|
|
176,
|
|
177,
|
|
178,
|
|
179,
|
|
180,
|
|
181,
|
|
182,
|
|
183,
|
|
184,
|
|
185,
|
|
186,
|
|
187,
|
|
188,
|
|
189,
|
|
190,
|
|
191,
|
|
192,
|
|
193,
|
|
194,
|
|
195,
|
|
196,
|
|
197,
|
|
198,
|
|
199,
|
|
200,
|
|
201,
|
|
229,
|
|
230,
|
|
231,
|
|
232,
|
|
233,
|
|
234,
|
|
235,
|
|
236,
|
|
237,
|
|
238,
|
|
239,
|
|
240,
|
|
241,
|
|
242,
|
|
243,
|
|
244,
|
|
245,
|
|
246,
|
|
247,
|
|
248,
|
|
249,
|
|
250,
|
|
251,
|
|
252,
|
|
253,
|
|
254,
|
|
255,
|
|
256,
|
|
257,
|
|
258,
|
|
259,
|
|
260,
|
|
261,
|
|
262,
|
|
263,
|
|
264,
|
|
265,
|
|
266,
|
|
267,
|
|
268,
|
|
269,
|
|
270,
|
|
271,
|
|
272,
|
|
273,
|
|
274,
|
|
275,
|
|
276,
|
|
277,
|
|
278,
|
|
279,
|
|
280,
|
|
281,
|
|
282,
|
|
283,
|
|
284,
|
|
285,
|
|
286,
|
|
287,
|
|
288,
|
|
289,
|
|
290,
|
|
291,
|
|
292,
|
|
293,
|
|
294,
|
|
295,
|
|
296,
|
|
297,
|
|
298,
|
|
299,
|
|
300,
|
|
301,
|
|
302,
|
|
303,
|
|
304,
|
|
305,
|
|
306,
|
|
307,
|
|
308,
|
|
309,
|
|
310,
|
|
311,
|
|
312,
|
|
313,
|
|
314,
|
|
315,
|
|
316,
|
|
317,
|
|
318,
|
|
319,
|
|
320,
|
|
321,
|
|
322,
|
|
323,
|
|
324,
|
|
325,
|
|
326,
|
|
327,
|
|
328,
|
|
329,
|
|
330,
|
|
331,
|
|
332,
|
|
333,
|
|
334,
|
|
335,
|
|
336,
|
|
337,
|
|
338,
|
|
339,
|
|
340,
|
|
341,
|
|
342,
|
|
343,
|
|
344,
|
|
345,
|
|
346,
|
|
347,
|
|
348,
|
|
349,
|
|
350,
|
|
351,
|
|
352,
|
|
353,
|
|
354,
|
|
355,
|
|
356,
|
|
357,
|
|
358,
|
|
359,
|
|
360,
|
|
361,
|
|
362,
|
|
363,
|
|
364,
|
|
365,
|
|
366,
|
|
367,
|
|
368,
|
|
369,
|
|
370,
|
|
371,
|
|
372,
|
|
373,
|
|
374,
|
|
375,
|
|
376,
|
|
377,
|
|
378,
|
|
379,
|
|
380,
|
|
381,
|
|
382,
|
|
383,
|
|
384,
|
|
385,
|
|
386,
|
|
387,
|
|
388,
|
|
389,
|
|
390,
|
|
391,
|
|
392,
|
|
393,
|
|
394,
|
|
395,
|
|
396,
|
|
397,
|
|
398,
|
|
399,
|
|
400,
|
|
401,
|
|
402,
|
|
403,
|
|
404,
|
|
405,
|
|
406,
|
|
407,
|
|
408,
|
|
409,
|
|
410,
|
|
411,
|
|
412,
|
|
413,
|
|
417,
|
|
418,
|
|
419,
|
|
420,
|
|
421,
|
|
422,
|
|
423,
|
|
424,
|
|
425,
|
|
426,
|
|
427,
|
|
428,
|
|
429,
|
|
430,
|
|
431,
|
|
432,
|
|
433,
|
|
434,
|
|
435,
|
|
436,
|
|
437,
|
|
438,
|
|
439,
|
|
440,
|
|
441,
|
|
442,
|
|
445,
|
|
448,
|
|
449,
|
|
450,
|
|
451,
|
|
452,
|
|
453,
|
|
454,
|
|
455,
|
|
456,
|
|
458,
|
|
460,
|
|
461,
|
|
462,
|
|
463,
|
|
464,
|
|
465,
|
|
466,
|
|
467,
|
|
468,
|
|
469,
|
|
470,
|
|
471,
|
|
472,
|
|
473,
|
|
474,
|
|
475,
|
|
476,
|
|
477,
|
|
478,
|
|
479,
|
|
480,
|
|
481,
|
|
482,
|
|
483,
|
|
484,
|
|
485,
|
|
486,
|
|
487,
|
|
488,
|
|
489,
|
|
490,
|
|
491,
|
|
492,
|
|
493,
|
|
494,
|
|
495,
|
|
496,
|
|
497,
|
|
498,
|
|
499,
|
|
500,
|
|
501,
|
|
502,
|
|
503,
|
|
504,
|
|
505,
|
|
506,
|
|
507,
|
|
508,
|
|
509,
|
|
510,
|
|
511,
|
|
512,
|
|
513,
|
|
514,
|
|
515,
|
|
521,
|
|
522,
|
|
523,
|
|
524,
|
|
525,
|
|
526,
|
|
527,
|
|
528,
|
|
530,
|
|
531,
|
|
532,
|
|
533,
|
|
534,
|
|
536,
|
|
};
|
|
|
|
static const s8 sPokeathlonPerformanceNatureMods[NATURE_NUM][5] = {
|
|
{ 10, 0, 0, 0, -10},
|
|
{ 35, -35, 0, 0, 0},
|
|
{ 35, 0, 0, 0, -35},
|
|
{ 35, 0, 0, -35, 0},
|
|
{ 35, 0, -35, 0, 0},
|
|
{-35, 35, 0, 0, 0},
|
|
{ 0, 10, 0, -10, 0},
|
|
{ 0, 35, 0, 0, -35},
|
|
{ 0, 35, 0, -35, 0},
|
|
{ 0, 35, -35, 0, 0},
|
|
{-35, 0, 0, 0, 35},
|
|
{ 0, -35, 0, 0, 35},
|
|
{ 0, 0, -10, 0, 10},
|
|
{ 0, 0, 0, -35, 35},
|
|
{ 0, 0, -35, 0, 35},
|
|
{-35, 0, 0, 35, 0},
|
|
{ 0, -35, 0, 35, 0},
|
|
{ 0, 0, 0, 35, -35},
|
|
{-10, 0, 0, 10, 0},
|
|
{ 0, 0, -35, 35, 0},
|
|
{-35, 0, 35, 0, 0},
|
|
{ 0, -35, 35, 0, 0},
|
|
{ 0, 0, 35, 0, -35},
|
|
{ 0, 0, 35, -35, 0},
|
|
{ 0, -10, 10, 0, 0},
|
|
};
|
|
|
|
void CalcBoxMonPokeathlonPerformance(BoxPokemon *boxMon, struct PokeathlonTodayPerformance *dest) {
|
|
u32 pid;
|
|
u32 nature;
|
|
struct PokeathlonBasePerformance data;
|
|
RTCDate date;
|
|
RTCTime time;
|
|
int i;
|
|
s16 day;
|
|
u16 species;
|
|
u16 forme;
|
|
|
|
pid = GetBoxMonData(boxMon, MON_DATA_PERSONALITY, NULL);
|
|
nature = GetBoxMonNature(boxMon);
|
|
GF_RTC_CopyDateTime(&date, &time);
|
|
day = date.day;
|
|
|
|
for (i = PERFORMANCE_MIN; i < PERFORMANCE_MAX; i++) {
|
|
u32 pid_digit = _u32_getDigitN(pid, i);
|
|
dest->stats[i].dailyMod = sPokeathlonPerformanceNatureMods[nature][i] + (2 * _u32_getDigitN(pid_digit + (day + (7 - i)) * (day + (i + 3)), 0) - 9);
|
|
}
|
|
|
|
species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
|
|
forme = GetBoxMonData(boxMon, MON_DATA_FORME, NULL);
|
|
ReadWholeNarcMemberByIdPair(&data, NARC_poketool_personal_performance, sPokeathlonPerformanceArcIdxs[species] + forme);
|
|
dest->stats[PERFORMANCE_POWER].base = data.base[ARCPERF_POWER];
|
|
dest->stats[PERFORMANCE_POWER].lo = data.minmax[ARCPERF_POWER][0];
|
|
dest->stats[PERFORMANCE_POWER].hi = data.minmax[ARCPERF_POWER][1];
|
|
dest->stats[PERFORMANCE_SKILL].base = data.base[ARCPERF_SKILL];
|
|
dest->stats[PERFORMANCE_SKILL].lo = data.minmax[ARCPERF_SKILL][0];
|
|
dest->stats[PERFORMANCE_SKILL].hi = data.minmax[ARCPERF_SKILL][1];
|
|
dest->stats[PERFORMANCE_SPEED].base = data.base[ARCPERF_SPEED];
|
|
dest->stats[PERFORMANCE_SPEED].lo = data.minmax[ARCPERF_SPEED][0];
|
|
dest->stats[PERFORMANCE_SPEED].hi = data.minmax[ARCPERF_SPEED][1];
|
|
dest->stats[PERFORMANCE_JUMP].base = data.base[ARCPERF_JUMP];
|
|
dest->stats[PERFORMANCE_JUMP].lo = data.minmax[ARCPERF_JUMP][0];
|
|
dest->stats[PERFORMANCE_JUMP].hi = data.minmax[ARCPERF_JUMP][1];
|
|
dest->stats[PERFORMANCE_STAMINA].base = data.base[ARCPERF_STAMINA];
|
|
dest->stats[PERFORMANCE_STAMINA].lo = data.minmax[ARCPERF_STAMINA][0];
|
|
dest->stats[PERFORMANCE_STAMINA].hi = data.minmax[ARCPERF_STAMINA][1];
|
|
}
|
|
|
|
void CalcMonPokeathlonPerformance(Pokemon *mon, struct PokeathlonTodayPerformance *dest) {
|
|
CalcBoxMonPokeathlonPerformance(Mon_GetBoxMon(mon), dest);
|
|
}
|
|
|
|
void CalcBoxmonPokeathlonStars(struct PokeathlonPerformanceStars *dest, BoxPokemon *boxMon, const s8 *aprijuice, HeapID heap_id) {
|
|
#pragma unused(heap_id)
|
|
int i;
|
|
struct PokeathlonTodayPerformance basePerf;
|
|
|
|
MI_CpuClear8(dest, sizeof(struct PokeathlonPerformanceStars));
|
|
CalcBoxMonPokeathlonPerformance(boxMon, &basePerf);
|
|
for (i = PERFORMANCE_MIN; i < PERFORMANCE_MAX; i++) {
|
|
s16 stars = (
|
|
aprijuice != NULL
|
|
? PokeathlonStatScoreToStars(basePerf.stats[i].dailyMod + aprijuice[i])
|
|
: PokeathlonStatScoreToStars(basePerf.stats[i].dailyMod));
|
|
stars += basePerf.stats[i].base;
|
|
if (stars < basePerf.stats[i].lo) {
|
|
stars = basePerf.stats[i].lo;
|
|
} else if (stars > basePerf.stats[i].hi) {
|
|
stars = basePerf.stats[i].hi;
|
|
}
|
|
if (stars == basePerf.stats[i].base) {
|
|
dest->color[i] = STARS_AT_BASE;
|
|
} else if (stars < basePerf.stats[i].base) {
|
|
dest->color[i] = STARS_BELOW_BASE;
|
|
} else {
|
|
dest->color[i] = STARS_ABOVE_BASE;
|
|
}
|
|
dest->stars |= stars << (3 * i);
|
|
}
|
|
}
|
|
|
|
void CalcMonPokeathlonStars(struct PokeathlonPerformanceStars *dest, Pokemon *mon, const s8 *aprijuice, HeapID heap_id) {
|
|
CalcBoxmonPokeathlonStars(dest, Mon_GetBoxMon(mon), aprijuice, heap_id);
|
|
}
|