summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPikalaxALT <pikalaxalt@gmail.com>2018-10-17 11:05:44 -0700
committerPikalaxALT <pikalaxalt@gmail.com>2018-10-17 11:05:44 -0700
commitaa6e1a91fc89d01ad65eb532d6832767c936807a (patch)
tree77ea1ef0aa2dff7e70165741fcc455c8faaadfc7 /src
parent46e957bacd9ba5538262c89bb87e1915167bb201 (diff)
parentd05339979e39580b162e618087136bb220a4f20d (diff)
Merge branch 'master' into vs_seeker
Diffstat (limited to 'src')
-rw-r--r--src/battle_ai_script_commands.c27
-rw-r--r--src/load_save.c297
-rw-r--r--src/pokemon.c5609
-rw-r--r--src/save.c919
-rw-r--r--src/vs_seeker.c20
5 files changed, 6848 insertions, 24 deletions
diff --git a/src/battle_ai_script_commands.c b/src/battle_ai_script_commands.c
index 22ed95040..6f5577f70 100644
--- a/src/battle_ai_script_commands.c
+++ b/src/battle_ai_script_commands.c
@@ -256,7 +256,6 @@ extern const u32 gBitTable[]; // util.h
extern u32 gStatuses3[]; // battle_2.h
extern u16 gSideAffecting[2];
extern const struct BattleMove gBattleMoves[];
-extern u16 gBattlerPartyIndexes[];
extern u16 gDynamicBasePower;
extern u8 gMoveResultFlags;
extern u8 gCritMultiplier;
@@ -485,14 +484,14 @@ void sub_80C71A8(u8 a)
void sub_80C71D0(u8 a, u8 b)
{
- if (GetBankSide(a) == 0)
- BATTLE_HISTORY->abilities[GetBankIdentity(a) & 1] = b;
+ if (GetBattlerSide(a) == 0)
+ BATTLE_HISTORY->abilities[GetBattlerPosition(a) & 1] = b;
}
void sub_80C7208(u8 a, u8 b)
{
- if (GetBankSide(a) == 0)
- BATTLE_HISTORY->itemEffects[GetBankIdentity(a) & 1] = b;
+ if (GetBattlerSide(a) == 0)
+ BATTLE_HISTORY->itemEffects[GetBattlerPosition(a) & 1] = b;
}
static void BattleAICmd_if_random_less_than(void)
@@ -720,7 +719,7 @@ static void BattleAICmd_if_status4(void)
else
index = gBattlerTarget;
- arg1 = GetBankIdentity(index) & 1;
+ arg1 = GetBattlerPosition(index) & 1;
arg2 = T1_READ_32(gAIScriptPtr + 2);
if ((gSideAffecting[arg1] & arg2) != 0)
@@ -739,7 +738,7 @@ static void BattleAICmd_if_not_status4(void)
else
index = gBattlerTarget;
- arg1 = GetBankIdentity(index) & 1;
+ arg1 = GetBattlerPosition(index) & 1;
arg2 = T1_READ_32(gAIScriptPtr + 2);
if ((gSideAffecting[arg1] & arg2) == 0)
@@ -1361,7 +1360,7 @@ static void BattleAICmd_count_alive_pokemon(void)
else
index = gBattlerTarget;
- if (GetBankSide(index) == 0)
+ if (GetBattlerSide(index) == 0)
party = gPlayerParty;
else
party = gEnemyParty;
@@ -1370,8 +1369,8 @@ static void BattleAICmd_count_alive_pokemon(void)
{
u32 status;
var = gBattlerPartyIndexes[index];
- status = GetBankIdentity(index) ^ 2;
- var2 = gBattlerPartyIndexes[GetBankByIdentity(status)];
+ status = GetBattlerPosition(index) ^ 2;
+ var2 = gBattlerPartyIndexes[GetBattlerAtPosition(status)];
}
else
{
@@ -1414,9 +1413,9 @@ static void BattleAICmd_get_ability(void)
else
index = gBattlerTarget;
- if (GetBankSide(index) == TARGET)
+ if (GetBattlerSide(index) == TARGET)
{
- u16 side = GetBankIdentity(index) & 1;
+ u16 side = GetBattlerPosition(index) & 1;
if (BATTLE_HISTORY->abilities[side] != 0)
{
@@ -2008,9 +2007,9 @@ static void BattleAICmd_get_hold_effect(void)
else
index = gBattlerTarget;
- if (GetBankSide(index) == 0)
+ if (GetBattlerSide(index) == 0)
{
- side = (GetBankIdentity(index) & 1);
+ side = (GetBattlerPosition(index) & 1);
AI_THINKING_STRUCT->funcResult = BATTLE_HISTORY->itemEffects[side];
}
else
diff --git a/src/load_save.c b/src/load_save.c
new file mode 100644
index 000000000..eba01642c
--- /dev/null
+++ b/src/load_save.c
@@ -0,0 +1,297 @@
+#include "global.h"
+#include "gba/flash_internal.h"
+#include "load_save.h"
+#include "main.h"
+#include "pokemon.h"
+#include "random.h"
+#include "malloc.h"
+#include "item.h"
+
+extern void sub_8099E44(void);
+extern void sub_8110840(void *oldSave);
+extern void sub_8055778(int);
+extern void sub_8054F38(u32 newKey);
+extern void ApplyNewEncryptionKeyToBagItems_(u32 newKey);
+extern void sub_815EE6C(u32 newKey);
+
+#define SAVEBLOCK_MOVE_RANGE 128
+
+struct LoadedSaveData
+{
+ /*0x0000*/ struct ItemSlot items[BAG_ITEMS_COUNT];
+ /*0x0078*/ struct ItemSlot keyItems[BAG_KEYITEMS_COUNT];
+ /*0x00F0*/ struct ItemSlot pokeBalls[BAG_POKEBALLS_COUNT];
+ /*0x0130*/ struct ItemSlot TMsHMs[BAG_TMHM_COUNT];
+ /*0x0230*/ struct ItemSlot berries[BAG_BERRIES_COUNT];
+ /*0x02E8*/ struct MailStruct mail[MAIL_COUNT];
+};
+
+// EWRAM DATA
+EWRAM_DATA struct SaveBlock2 gSaveBlock2 = {0};
+EWRAM_DATA u8 gSaveBlock2_DMA[SAVEBLOCK_MOVE_RANGE] = {0};
+
+EWRAM_DATA struct SaveBlock1 gSaveBlock1 = {0};
+EWRAM_DATA u8 gSaveBlock1_DMA[SAVEBLOCK_MOVE_RANGE] = {0};
+
+EWRAM_DATA struct PokemonStorage gPokemonStorage = {0};
+EWRAM_DATA u8 gSaveBlock3_DMA[SAVEBLOCK_MOVE_RANGE] = {0};
+
+EWRAM_DATA struct LoadedSaveData gLoadedSaveData = {0};
+EWRAM_DATA u32 gLastEncryptionKey = 0;
+
+// IWRAM common
+IWRAM_DATA bool32 gFlashMemoryPresent;
+IWRAM_DATA struct SaveBlock1 *gSaveBlock1Ptr;
+IWRAM_DATA struct SaveBlock2 *gSaveBlock2Ptr;
+IWRAM_DATA struct PokemonStorage *gPokemonStoragePtr;
+
+void CheckForFlashMemory(void)
+{
+ if (!IdentifyFlash())
+ {
+ gFlashMemoryPresent = TRUE;
+ InitFlashTimer();
+ }
+ else
+ {
+ gFlashMemoryPresent = FALSE;
+ }
+}
+
+void ClearSav2(void)
+{
+ CpuFill16(0, &gSaveBlock2, sizeof(struct SaveBlock2) + sizeof(gSaveBlock2_DMA));
+}
+
+void ClearSav1(void)
+{
+ CpuFill16(0, &gSaveBlock1, sizeof(struct SaveBlock1) + sizeof(gSaveBlock1_DMA));
+}
+
+void SetSaveBlocksPointers(void)
+{
+ u32 offset;
+ struct SaveBlock1** sav1_LocalVar = &gSaveBlock1Ptr;
+ void *oldSave = (void *)gSaveBlock1Ptr;
+
+ offset = (Random()) & (SAVEBLOCK_MOVE_RANGE - 4);
+
+ gSaveBlock2Ptr = (void*)(&gSaveBlock2) + offset;
+ *sav1_LocalVar = (void*)(&gSaveBlock1) + offset;
+ gPokemonStoragePtr = (void*)(&gPokemonStorage) + offset;
+
+ sub_8099E44();
+ sub_8110840(oldSave);
+}
+
+void MoveSaveBlocks_ResetHeap(void)
+{
+ void *vblankCB, *hblankCB;
+ u32 encryptionKey;
+ struct SaveBlock2 *saveBlock2Copy;
+ struct SaveBlock1 *saveBlock1Copy;
+ struct PokemonStorage *pokemonStorageCopy;
+
+ // save interrupt functions and turn them off
+ vblankCB = gMain.vblankCallback;
+ hblankCB = gMain.hblankCallback;
+ gMain.vblankCallback = NULL;
+ gMain.hblankCallback = NULL;
+ gMain.vblankCounter1 = NULL;
+
+ saveBlock2Copy = (struct SaveBlock2 *)(gHeap);
+ saveBlock1Copy = (struct SaveBlock1 *)(gHeap + sizeof(struct SaveBlock2));
+ pokemonStorageCopy = (struct PokemonStorage *)(gHeap + sizeof(struct SaveBlock2) + sizeof(struct SaveBlock1));
+
+ // backup the saves.
+ *saveBlock2Copy = *gSaveBlock2Ptr;
+ *saveBlock1Copy = *gSaveBlock1Ptr;
+ *pokemonStorageCopy = *gPokemonStoragePtr;
+
+ // change saveblocks' pointers
+ SetSaveBlocksPointers(); // unlike Emerald, this does not use
+ // the trainer ID sum for an offset.
+
+ // restore saveblock data since the pointers changed
+ *gSaveBlock2Ptr = *saveBlock2Copy;
+ *gSaveBlock1Ptr = *saveBlock1Copy;
+ *gPokemonStoragePtr = *pokemonStorageCopy;
+
+ // heap was destroyed in the copying process, so reset it
+ InitHeap(gHeap, HEAP_SIZE);
+
+ // restore interrupt functions
+ gMain.hblankCallback = hblankCB;
+ gMain.vblankCallback = vblankCB;
+
+ // create a new encryption key
+ encryptionKey = (Random() << 0x10) + (Random());
+ ApplyNewEncryptionKeyToAllEncryptedData(encryptionKey);
+ gSaveBlock2Ptr->encryptionKey = encryptionKey;
+}
+
+u32 sav2_x1_query_bit1(void)
+{
+ return gSaveBlock2Ptr->specialSaveWarp & 1;
+}
+
+void sav2_x9_clear_bit1(void)
+{
+ gSaveBlock2Ptr->specialSaveWarp &= ~1;
+}
+
+void sub_804C1AC(void)
+{
+ gSaveBlock2Ptr->specialSaveWarp |= 1;
+}
+
+void sub_804C1C0(void)
+{
+ sub_8055778(0);
+ gSaveBlock2Ptr->specialSaveWarp |= 1;
+}
+
+void sav2_gender2_inplace_and_xFE(void)
+{
+ gSaveBlock2Ptr->specialSaveWarp &= ~1;
+}
+
+void SavePlayerParty(void)
+{
+ int i;
+
+ gSaveBlock1Ptr->playerPartyCount = gPlayerPartyCount;
+
+ for (i = 0; i < PARTY_SIZE; i++)
+ gSaveBlock1Ptr->playerParty[i] = gPlayerParty[i];
+}
+
+void LoadPlayerParty(void)
+{
+ int i;
+
+ gPlayerPartyCount = gSaveBlock1Ptr->playerPartyCount;
+
+ for (i = 0; i < PARTY_SIZE; i++)
+ gPlayerParty[i] = gSaveBlock1Ptr->playerParty[i];
+}
+
+void SaveMapObjects(void)
+{
+ int i;
+
+ for (i = 0; i < NUM_FIELD_OBJECTS; i++)
+ gSaveBlock1Ptr->mapObjects[i] = gMapObjects[i];
+}
+
+void LoadMapObjects(void)
+{
+ int i;
+
+ for (i = 0; i < NUM_FIELD_OBJECTS; i++)
+ gMapObjects[i] = gSaveBlock1Ptr->mapObjects[i];
+}
+
+void SaveSerializedGame(void)
+{
+ SavePlayerParty();
+ SaveMapObjects();
+}
+
+void LoadSerializedGame(void)
+{
+ LoadPlayerParty();
+ LoadMapObjects();
+}
+
+void LoadPlayerBag(void)
+{
+ int i;
+
+ // load player items.
+ for (i = 0; i < BAG_ITEMS_COUNT; i++)
+ gLoadedSaveData.items[i] = gSaveBlock1Ptr->bagPocket_Items[i];
+
+ // load player key items.
+ for (i = 0; i < BAG_KEYITEMS_COUNT; i++)
+ gLoadedSaveData.keyItems[i] = gSaveBlock1Ptr->bagPocket_KeyItems[i];
+
+ // load player pokeballs.
+ for (i = 0; i < BAG_POKEBALLS_COUNT; i++)
+ gLoadedSaveData.pokeBalls[i] = gSaveBlock1Ptr->bagPocket_PokeBalls[i];
+
+ // load player TMs and HMs.
+ for (i = 0; i < BAG_TMHM_COUNT; i++)
+ gLoadedSaveData.TMsHMs[i] = gSaveBlock1Ptr->bagPocket_TMHM[i];
+
+ // load player berries.
+ for (i = 0; i < BAG_BERRIES_COUNT; i++)
+ gLoadedSaveData.berries[i] = gSaveBlock1Ptr->bagPocket_Berries[i];
+
+ // load mail.
+ for (i = 0; i < MAIL_COUNT; i++)
+ gLoadedSaveData.mail[i] = gSaveBlock1Ptr->mail[i];
+
+ gLastEncryptionKey = gSaveBlock2Ptr->encryptionKey;
+}
+
+void SavePlayerBag(void)
+{
+ int i;
+ u32 encryptionKeyBackup;
+
+ // save player items.
+ for (i = 0; i < BAG_ITEMS_COUNT; i++)
+ gSaveBlock1Ptr->bagPocket_Items[i] = gLoadedSaveData.items[i];
+
+ // save player key items.
+ for (i = 0; i < BAG_KEYITEMS_COUNT; i++)
+ gSaveBlock1Ptr->bagPocket_KeyItems[i] = gLoadedSaveData.keyItems[i];
+
+ // save player pokeballs.
+ for (i = 0; i < BAG_POKEBALLS_COUNT; i++)
+ gSaveBlock1Ptr->bagPocket_PokeBalls[i] = gLoadedSaveData.pokeBalls[i];
+
+ // save player TMs and HMs.
+ for (i = 0; i < BAG_TMHM_COUNT; i++)
+ gSaveBlock1Ptr->bagPocket_TMHM[i] = gLoadedSaveData.TMsHMs[i];
+
+ // save player berries.
+ for (i = 0; i < BAG_BERRIES_COUNT; i++)
+ gSaveBlock1Ptr->bagPocket_Berries[i] = gLoadedSaveData.berries[i];
+
+ // save mail.
+ for (i = 0; i < MAIL_COUNT; i++)
+ gSaveBlock1Ptr->mail[i] = gLoadedSaveData.mail[i];
+
+ encryptionKeyBackup = gSaveBlock2Ptr->encryptionKey;
+ gSaveBlock2Ptr->encryptionKey = gLastEncryptionKey;
+ ApplyNewEncryptionKeyToBagItems(encryptionKeyBackup);
+ gSaveBlock2Ptr->encryptionKey = encryptionKeyBackup;
+}
+
+void ApplyNewEncryptionKeyToHword(u16 *hWord, u32 newKey)
+{
+ *hWord ^= gSaveBlock2Ptr->encryptionKey;
+ *hWord ^= newKey;
+}
+
+void ApplyNewEncryptionKeyToWord(u32 *word, u32 newKey)
+{
+ *word ^= gSaveBlock2Ptr->encryptionKey;
+ *word ^= newKey;
+}
+
+void ApplyNewEncryptionKeyToAllEncryptedData(u32 encryptionKey)
+{
+ int i;
+
+ for(i = 0; i < 4; i++)
+ ApplyNewEncryptionKeyToWord(&gSaveBlock1Ptr->unkArray[i][1], encryptionKey);
+
+ sub_8054F38(encryptionKey);
+ ApplyNewEncryptionKeyToBagItems_(encryptionKey);
+ sub_815EE6C(encryptionKey);
+ ApplyNewEncryptionKeyToWord(&gSaveBlock1Ptr->money, encryptionKey);
+ ApplyNewEncryptionKeyToHword(&gSaveBlock1Ptr->coins, encryptionKey);
+}
diff --git a/src/pokemon.c b/src/pokemon.c
new file mode 100644
index 000000000..ff7c907ca
--- /dev/null
+++ b/src/pokemon.c
@@ -0,0 +1,5609 @@
+#include "global.h"
+#include "pokemon.h"
+#include "random.h"
+#include "main.h"
+#include "text.h"
+#include "string_util.h"
+#include "battle.h"
+#include "item.h"
+#include "event_data.h"
+#include "util.h"
+#include "pokemon_storage_system.h"
+#include "data2.h"
+#include "battle_gfx_sfx_util.h"
+#include "battle_controllers.h"
+#include "evolution_scene.h"
+#include "battle_message.h"
+#include "link.h"
+#include "m4a.h"
+#include "sound.h"
+#include "constants/items.h"
+#include "constants/species.h"
+#include "constants/pokemon.h"
+#include "constants/abilities.h"
+#include "constants/flags.h"
+#include "constants/moves.h"
+#include "constants/hold_effects.h"
+#include "constants/battle_move_effects.h"
+
+// Extracts the upper 16 bits of a 32-bit number
+#define HIHALF(n) (((n) & 0xFFFF0000) >> 16)
+
+// Extracts the lower 16 bits of a 32-bit number
+#define LOHALF(n) ((n) & 0xFFFF)
+
+// TODO: what is this
+struct UnkStruct20244F4
+{
+ u8 unk0:4;
+ u8 unk0_2:4;
+ u8 filler1[0xF];
+ struct SpriteTemplate *unk10;
+};
+
+// External symbols
+extern struct UnkStruct20244F4 *gUnknown_20244F4;
+extern struct SpriteTemplate gUnknown_825DEF0[];
+extern struct SpriteTemplate gUnknown_825DF50[];
+extern const union AnimCmd *const *const gTrainerBackAnimsPtrTable[];
+extern struct SpriteTemplate gUnknown_825DEF0[];
+extern const union AnimCmd *const *const gTrainerFrontAnimsPtrTable[];
+extern const union AnimCmd *const gUnknown_82349BC[];
+extern const u8 gUnknown_825DEA1[];
+extern const u8 gPPUpWriteMasks[];
+extern u8 *gUnknown_83FD5D0[];
+extern const u8 gUnknown_825DFF0[];
+extern const u8 gText_EggNickname[];
+extern const u8 gText_BadEgg[];
+extern const u8 BattleText_Rose[];
+extern const u8 BattleText_UnknownString3[];
+extern const u8 BattleText_GetPumped[];
+extern const u8 BattleText_MistShroud[];
+extern const u8 gText_PkmnsXPreventsSwitching[];
+extern const u8 sHoldEffectToType[][2];
+extern u8 sLearningMoveTableID;
+extern const u8 sSecretBaseFacilityClasses[2][5];
+extern u16 gUnknown_8251CB8[];
+extern u16 gUnknown_8251FEE[];
+extern u16 gUnknown_8252324[];
+extern u16 gUnknown_82539D4[];
+extern struct SpindaSpot gSpindaSpotGraphics[];
+extern s8 gNatureStatTable[][5];
+extern const s8 sFriendshipEventDeltas[][3];
+extern u32 gTMHMLearnsets[][2];
+extern u8 gBattleMonForms[4];
+extern const struct CompressedSpritePalette gMonPaletteTable[];
+extern const struct CompressedSpritePalette gMonShinyPaletteTable[];
+extern const u16 sHMMoves[];
+extern s8 gPokeblockFlavorCompatibilityTable[];
+
+// External functions
+extern u8 sav1_map_get_name(void); // overworld
+extern const struct BattleMove gBattleMoves[];
+extern u8 sBattler_AI; // battle_ai
+extern void set_unknown_box_id(u8); // field_specials
+extern u8 pokemon_order_func(u8);
+extern u16 get_unknown_box_id(void); // field_specials
+extern u8 StorageGetCurrentBox(void); // pokemon_storage_system
+extern void sub_80174B8(u8 battlerId);
+
+union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 personality, u8 substructType);
+s32 GetDeoxysStat(struct Pokemon *mon, s32 statId);
+
+// code
+void ZeroBoxMonData(struct BoxPokemon *boxMon)
+{
+ u8 *raw = (u8 *)boxMon;
+ u32 i;
+ for (i = 0; i < sizeof(struct BoxPokemon); i++)
+ raw[i] = 0;
+}
+
+void ZeroMonData(struct Pokemon *mon)
+{
+ u32 arg;
+ ZeroBoxMonData(&mon->box);
+ arg = 0;
+ SetMonData(mon, MON_DATA_STATUS, &arg);
+ SetMonData(mon, MON_DATA_LEVEL, &arg);
+ SetMonData(mon, MON_DATA_HP, &arg);
+ SetMonData(mon, MON_DATA_MAX_HP, &arg);
+ SetMonData(mon, MON_DATA_ATK, &arg);
+ SetMonData(mon, MON_DATA_DEF, &arg);
+ SetMonData(mon, MON_DATA_SPEED, &arg);
+ SetMonData(mon, MON_DATA_SPATK, &arg);
+ SetMonData(mon, MON_DATA_SPDEF, &arg);
+ arg = 255;
+ SetMonData(mon, MON_DATA_MAIL, &arg);
+}
+
+void ZeroPlayerPartyMons(void)
+{
+ s32 i;
+ for (i = 0; i < PARTY_SIZE; i++)
+ ZeroMonData(&gPlayerParty[i]);
+}
+
+void ZeroEnemyPartyMons(void)
+{
+ s32 i;
+ for (i = 0; i < PARTY_SIZE; i++)
+ ZeroMonData(&gEnemyParty[i]);
+}
+
+void CreateMon(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId)
+{
+ u32 arg;
+ ZeroMonData(mon);
+ CreateBoxMon(&mon->box, species, level, fixedIV, hasFixedPersonality, fixedPersonality, otIdType, fixedOtId);
+ SetMonData(mon, MON_DATA_LEVEL, &level);
+ arg = 255;
+ SetMonData(mon, MON_DATA_MAIL, &arg);
+ CalculateMonStats(mon);
+}
+
+void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId)
+{
+ u8 speciesName[POKEMON_NAME_LENGTH + 1];
+ u32 personality;
+ u32 value;
+ u16 checksum;
+
+ ZeroBoxMonData(boxMon);
+
+ if (hasFixedPersonality)
+ personality = fixedPersonality;
+ else
+ personality = Random32();
+
+ SetBoxMonData(boxMon, MON_DATA_PERSONALITY, &personality);
+
+ //Determine original trainer ID
+ if (otIdType == OT_ID_RANDOM_NO_SHINY) //Pokemon cannot be shiny
+ {
+ u32 shinyValue;
+ do
+ {
+ value = Random32();
+ shinyValue = HIHALF(value) ^ LOHALF(value) ^ HIHALF(personality) ^ LOHALF(personality);
+ } while (shinyValue < 8);
+ }
+ else if (otIdType == OT_ID_PRESET) //Pokemon has a preset OT ID
+ {
+ value = fixedOtId;
+ }
+ else //Player is the OT
+ {
+ value = gSaveBlock2Ptr->playerTrainerId[0]
+ | (gSaveBlock2Ptr->playerTrainerId[1] << 8)
+ | (gSaveBlock2Ptr->playerTrainerId[2] << 16)
+ | (gSaveBlock2Ptr->playerTrainerId[3] << 24);
+ }
+
+ SetBoxMonData(boxMon, MON_DATA_OT_ID, &value);
+
+ checksum = CalculateBoxMonChecksum(boxMon);
+ SetBoxMonData(boxMon, MON_DATA_CHECKSUM, &checksum);
+ EncryptBoxMon(boxMon);
+ GetSpeciesName(speciesName, species);
+ SetBoxMonData(boxMon, MON_DATA_NICKNAME, speciesName);
+ SetBoxMonData(boxMon, MON_DATA_LANGUAGE, &gGameLanguage);
+ SetBoxMonData(boxMon, MON_DATA_OT_NAME, gSaveBlock2Ptr->playerName);
+ SetBoxMonData(boxMon, MON_DATA_SPECIES, &species);
+ SetBoxMonData(boxMon, MON_DATA_EXP, &gExperienceTables[gBaseStats[species].growthRate][level]);
+ SetBoxMonData(boxMon, MON_DATA_FRIENDSHIP, &gBaseStats[species].friendship);
+ value = sav1_map_get_name();
+ SetBoxMonData(boxMon, MON_DATA_MET_LOCATION, &value);
+ SetBoxMonData(boxMon, MON_DATA_MET_LEVEL, &level);
+ SetBoxMonData(boxMon, MON_DATA_MET_GAME, &gGameVersion);
+ value = ITEM_POKE_BALL;
+ SetBoxMonData(boxMon, MON_DATA_POKEBALL, &value);
+ SetBoxMonData(boxMon, MON_DATA_OT_GENDER, &gSaveBlock2Ptr->playerGender);
+
+ if (fixedIV < 32)
+ {
+ SetBoxMonData(boxMon, MON_DATA_HP_IV, &fixedIV);
+ SetBoxMonData(boxMon, MON_DATA_ATK_IV, &fixedIV);
+ SetBoxMonData(boxMon, MON_DATA_DEF_IV, &fixedIV);
+ SetBoxMonData(boxMon, MON_DATA_SPEED_IV, &fixedIV);
+ SetBoxMonData(boxMon, MON_DATA_SPATK_IV, &fixedIV);
+ SetBoxMonData(boxMon, MON_DATA_SPDEF_IV, &fixedIV);
+ }
+ else
+ {
+ u32 iv;
+ value = Random();
+
+ iv = value & 0x1F;
+ SetBoxMonData(boxMon, MON_DATA_HP_IV, &iv);
+ iv = (value & 0x3E0) >> 5;
+ SetBoxMonData(boxMon, MON_DATA_ATK_IV, &iv);
+ iv = (value & 0x7C00) >> 10;
+ SetBoxMonData(boxMon, MON_DATA_DEF_IV, &iv);
+
+ value = Random();
+
+ iv = value & 0x1F;
+ SetBoxMonData(boxMon, MON_DATA_SPEED_IV, &iv);
+ iv = (value & 0x3E0) >> 5;
+ SetBoxMonData(boxMon, MON_DATA_SPATK_IV, &iv);
+ iv = (value & 0x7C00) >> 10;
+ SetBoxMonData(boxMon, MON_DATA_SPDEF_IV, &iv);
+ }
+
+ if (gBaseStats[species].ability2)
+ {
+ value = personality & 1;
+ SetBoxMonData(boxMon, MON_DATA_ALT_ABILITY, &value);
+ }
+
+ GiveBoxMonInitialMoveset(boxMon);
+}
+
+void CreateMonWithNature(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 nature)
+{
+ u32 personality;
+
+ do
+ {
+ personality = Random32();
+ }
+ while (nature != GetNatureFromPersonality(personality));
+
+ CreateMon(mon, species, level, fixedIV, 1, personality, OT_ID_PLAYER_ID, 0);
+}
+
+void CreateMonWithGenderNatureLetter(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 gender, u8 nature, u8 unownLetter)
+{
+ u32 personality;
+
+ if ((u8)(unownLetter - 1) < 28)
+ {
+ u16 actualLetter;
+
+ do
+ {
+ personality = Random32();
+ actualLetter = ((((personality & 0x3000000) >> 18) | ((personality & 0x30000) >> 12) | ((personality & 0x300) >> 6) | (personality & 0x3)) % 28);
+ }
+ while (nature != GetNatureFromPersonality(personality)
+ || gender != GetGenderFromSpeciesAndPersonality(species, personality)
+ || actualLetter != unownLetter - 1);
+ }
+ else
+ {
+ do
+ {
+ personality = Random32();
+ }
+ while (nature != GetNatureFromPersonality(personality)
+ || gender != GetGenderFromSpeciesAndPersonality(species, personality));
+ }
+
+ CreateMon(mon, species, level, fixedIV, 1, personality, OT_ID_PLAYER_ID, 0);
+}
+
+// Used to create the Old Man's Weedle?
+void CreateMaleMon(struct Pokemon *mon, u16 species, u8 level)
+{
+ u32 personality;
+ u32 otId;
+
+ do
+ {
+ otId = Random32();
+ personality = Random32();
+ }
+ while (GetGenderFromSpeciesAndPersonality(species, personality) != MON_MALE);
+ CreateMon(mon, species, level, 32, 1, personality, OT_ID_PRESET, otId);
+}
+
+void CreateMonWithIVsPersonality(struct Pokemon *mon, u16 species, u8 level, u32 ivs, u32 personality)
+{
+ CreateMon(mon, species, level, 0, 1, personality, OT_ID_PLAYER_ID, 0);
+ SetMonData(mon, MON_DATA_IVS, &ivs);
+ CalculateMonStats(mon);
+}
+
+void CreateMonWithIVsOTID(struct Pokemon *mon, u16 species, u8 level, u8 *ivs, u32 otId)
+{
+ CreateMon(mon, species, level, 0, 0, 0, OT_ID_PRESET, otId);
+ SetMonData(mon, MON_DATA_HP_IV, &ivs[0]);
+ SetMonData(mon, MON_DATA_ATK_IV, &ivs[1]);
+ SetMonData(mon, MON_DATA_DEF_IV, &ivs[2]);
+ SetMonData(mon, MON_DATA_SPEED_IV, &ivs[3]);
+ SetMonData(mon, MON_DATA_SPATK_IV, &ivs[4]);
+ SetMonData(mon, MON_DATA_SPDEF_IV, &ivs[5]);
+ CalculateMonStats(mon);
+}
+
+void CreateMonWithEVSpread(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 evSpread)
+{
+ s32 i;
+ s32 statCount = 0;
+ u16 evAmount;
+ u8 evsBits;
+
+ CreateMon(mon, species, level, fixedIV, 0, 0, 0, 0);
+
+ evsBits = evSpread;
+
+ for (i = 0; i < NUM_STATS; i++)
+ {
+ if (evsBits & 1)
+ statCount++;
+ evsBits >>= 1;
+ }
+
+ evAmount = MAX_TOTAL_EVS / statCount;
+
+ evsBits = 1;
+
+ for (i = 0; i < NUM_STATS; i++)
+ {
+ if (evSpread & evsBits)
+ SetMonData(mon, MON_DATA_HP_EV + i, &evAmount);
+ evsBits <<= 1;
+ }
+
+ CalculateMonStats(mon);
+}
+
+void sub_803E0A4(struct Pokemon *mon, struct UnknownPokemonStruct *src)
+{
+ s32 i;
+ u8 value;
+
+ CreateMon(mon, src->species, src->level, 0, 1, src->personality, 1, src->otId);
+
+ for (i = 0; i < 4; i++)
+ SetMonMoveSlot(mon, src->moves[i], i);
+
+ SetMonData(mon, MON_DATA_PP_BONUSES, &src->ppBonuses);
+ SetMonData(mon, MON_DATA_HELD_ITEM, &src->heldItem);
+
+ // Why is this commented out in FR/LG?
+ /*
+ StringCopy(nickname, src->nickname);
+
+ if (nickname[0] == 0xFC && nickname[1] == 0x15)
+ language = LANGUAGE_JAPANESE;
+ else
+ language = GAME_LANGUAGE;
+
+ SetMonData(mon, MON_DATA_LANGUAGE, &language);
+ Text_StripExtCtrlCodes(nickname);
+ */
+
+ SetMonData(mon, MON_DATA_NICKNAME, &src->nickname);
+ SetMonData(mon, MON_DATA_FRIENDSHIP, &src->friendship);
+ SetMonData(mon, MON_DATA_HP_EV, &src->hpEV);
+ SetMonData(mon, MON_DATA_ATK_EV, &src->attackEV);
+ SetMonData(mon, MON_DATA_DEF_EV, &src->defenseEV);
+ SetMonData(mon, MON_DATA_SPEED_EV, &src->speedEV);
+ SetMonData(mon, MON_DATA_SPATK_EV, &src->spAttackEV);
+ SetMonData(mon, MON_DATA_SPDEF_EV, &src->spDefenseEV);
+ value = src->altAbility;
+ SetMonData(mon, MON_DATA_ALT_ABILITY, &value);
+ value = src->hpIV;
+ SetMonData(mon, MON_DATA_HP_IV, &value);
+ value = src->attackIV;
+ SetMonData(mon, MON_DATA_ATK_IV, &value);
+ value = src->defenseIV;
+ SetMonData(mon, MON_DATA_DEF_IV, &value);
+ value = src->speedIV;
+ SetMonData(mon, MON_DATA_SPEED_IV, &value);
+ value = src->spAttackIV;
+ SetMonData(mon, MON_DATA_SPATK_IV, &value);
+ value = src->spDefenseIV;
+ SetMonData(mon, MON_DATA_SPDEF_IV, &value);
+ CalculateMonStats(mon);
+}
+
+void CreateObedientMon(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId)
+{
+ bool32 obedient = TRUE;
+
+ CreateMon(mon, species, level, fixedIV, hasFixedPersonality, fixedPersonality, otIdType, fixedOtId);
+ SetMonData(mon, MON_DATA_OBEDIENCE, &obedient);
+}
+
+void sub_803E23C(struct Pokemon *mon, struct UnknownPokemonStruct *dest)
+{
+ s32 i;
+ u16 heldItem;
+
+ dest->species = GetMonData(mon, MON_DATA_SPECIES, NULL);
+ heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, NULL);
+
+ if (heldItem == ITEM_ENIGMA_BERRY)
+ heldItem = 0;
+
+ dest->heldItem = heldItem;
+
+ for (i = 0; i < 4; i++)
+ dest->moves[i] = GetMonData(mon, MON_DATA_MOVE1 + i, NULL);
+
+ dest->level = GetMonData(mon, MON_DATA_LEVEL, NULL);
+ dest->ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES, NULL);
+ dest->otId = GetMonData(mon, MON_DATA_OT_ID, NULL);
+ dest->hpEV = GetMonData(mon, MON_DATA_HP_EV, NULL);
+ dest->attackEV = GetMonData(mon, MON_DATA_ATK_EV, NULL);
+ dest->defenseEV = GetMonData(mon, MON_DATA_DEF_EV, NULL);
+ dest->speedEV = GetMonData(mon, MON_DATA_SPEED_EV, NULL);
+ dest->spAttackEV = GetMonData(mon, MON_DATA_SPATK_EV, NULL);
+ dest->spDefenseEV = GetMonData(mon, MON_DATA_SPDEF_EV, NULL);
+ dest->friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL);
+ dest->hpIV = GetMonData(mon, MON_DATA_HP_IV, NULL);
+ dest->attackIV = GetMonData(mon, MON_DATA_ATK_IV, NULL);
+ dest->defenseIV = GetMonData(mon, MON_DATA_DEF_IV, NULL);
+ dest->speedIV = GetMonData(mon, MON_DATA_SPEED_IV, NULL);
+ dest->spAttackIV = GetMonData(mon, MON_DATA_SPATK_IV, NULL);
+ dest->spDefenseIV = GetMonData(mon, MON_DATA_SPDEF_IV, NULL);
+ dest->altAbility = GetMonData(mon, MON_DATA_ALT_ABILITY, NULL);
+ dest->personality = GetMonData(mon, MON_DATA_PERSONALITY, NULL);
+ GetMonData(mon, MON_DATA_NICKNAME, dest->nickname);
+}
+
+u16 CalculateBoxMonChecksum(struct BoxPokemon *boxMon)
+{
+ u16 checksum = 0;
+ union PokemonSubstruct *substruct0 = GetSubstruct(boxMon, boxMon->personality, 0);
+ union PokemonSubstruct *substruct1 = GetSubstruct(boxMon, boxMon->personality, 1);
+ union PokemonSubstruct *substruct2 = GetSubstruct(boxMon, boxMon->personality, 2);
+ union PokemonSubstruct *substruct3 = GetSubstruct(boxMon, boxMon->personality, 3);
+ s32 i;
+
+ for (i = 0; i < 6; i++)
+ checksum += substruct0->raw[i];
+
+ for (i = 0; i < 6; i++)
+ checksum += substruct1->raw[i];
+
+ for (i = 0; i < 6; i++)
+ checksum += substruct2->raw[i];
+
+ for (i = 0; i < 6; i++)
+ checksum += substruct3->raw[i];
+
+ return checksum;
+}
+
+#define CALC_STAT(base, iv, ev, statIndex, field) \
+{ \
+ u8 baseStat = gBaseStats[species].base; \
+ s32 n = (((2 * baseStat + iv + ev / 4) * level) / 100) + 5; \
+ u8 nature = GetNature(mon); \
+ n = nature_stat_mod(nature, n, statIndex); \
+ SetMonData(mon, field, &n); \
+}
+
+void CalculateMonStats(struct Pokemon *mon)
+{
+ s32 oldMaxHP = GetMonData(mon, MON_DATA_MAX_HP, NULL);
+ s32 currentHP = GetMonData(mon, MON_DATA_HP, NULL);
+ s32 hpIV = GetMonData(mon, MON_DATA_HP_IV, NULL);
+ s32 hpEV = GetMonData(mon, MON_DATA_HP_EV, NULL);
+ s32 attackIV = GetMonData(mon, MON_DATA_ATK_IV, NULL);
+ s32 attackEV = GetMonData(mon, MON_DATA_ATK_EV, NULL);
+ s32 defenseIV = GetMonData(mon, MON_DATA_DEF_IV, NULL);
+ s32 defenseEV = GetMonData(mon, MON_DATA_DEF_EV, NULL);
+ s32 speedIV = GetMonData(mon, MON_DATA_SPEED_IV, NULL);
+ s32 speedEV = GetMonData(mon, MON_DATA_SPEED_EV, NULL);
+ s32 spAttackIV = GetMonData(mon, MON_DATA_SPATK_IV, NULL);
+ s32 spAttackEV = GetMonData(mon, MON_DATA_SPATK_EV, NULL);
+ s32 spDefenseIV = GetMonData(mon, MON_DATA_SPDEF_IV, NULL);
+ s32 spDefenseEV = GetMonData(mon, MON_DATA_SPDEF_EV, NULL);
+ u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
+ s32 level = GetLevelFromMonExp(mon);
+ s32 newMaxHP;
+
+ SetMonData(mon, MON_DATA_LEVEL, &level);
+
+ if (species == SPECIES_SHEDINJA)
+ {
+ newMaxHP = 1;
+ }
+ else
+ {
+ s32 n = 2 * gBaseStats[species].baseHP + hpIV;
+ newMaxHP = (((n + hpEV / 4) * level) / 100) + level + 10;
+ }
+
+ gBattleScripting.field_23 = newMaxHP - oldMaxHP;
+ if (gBattleScripting.field_23 == 0)
+ gBattleScripting.field_23 = 1;
+
+ SetMonData(mon, MON_DATA_MAX_HP, &newMaxHP);
+
+ CALC_STAT(baseAttack, attackIV, attackEV, STAT_ATK, MON_DATA_ATK)
+ CALC_STAT(baseDefense, defenseIV, defenseEV, STAT_DEF, MON_DATA_DEF)
+ CALC_STAT(baseSpeed, speedIV, speedEV, STAT_SPEED, MON_DATA_SPEED)
+ CALC_STAT(baseSpAttack, spAttackIV, spAttackEV, STAT_SPATK, MON_DATA_SPATK)
+ CALC_STAT(baseSpDefense, spDefenseIV, spDefenseEV, STAT_SPDEF, MON_DATA_SPDEF)
+
+ if (species == SPECIES_SHEDINJA)
+ {
+ if (currentHP != 0 || oldMaxHP == 0)
+ currentHP = 1;
+ else
+ return;
+ }
+ else
+ {
+ if (currentHP == 0 && oldMaxHP == 0)
+ currentHP = newMaxHP;
+ else if (currentHP != 0)
+ currentHP += newMaxHP - oldMaxHP;
+ else
+ return;
+ }
+
+ SetMonData(mon, MON_DATA_HP, &currentHP);
+}
+
+void BoxMonToMon(struct BoxPokemon *src, struct Pokemon *dest)
+{
+ u32 value = 0;
+ dest->box = *src;
+ SetMonData(dest, MON_DATA_STATUS, &value);
+ SetMonData(dest, MON_DATA_HP, &value);
+ SetMonData(dest, MON_DATA_MAX_HP, &value);
+ value = 255;
+ SetMonData(dest, MON_DATA_MAIL, &value);
+ CalculateMonStats(dest);
+}
+
+u8 GetLevelFromMonExp(struct Pokemon *mon)
+{
+ u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
+ u32 exp = GetMonData(mon, MON_DATA_EXP, NULL);
+ s32 level = 1;
+
+ while (level <= MAX_MON_LEVEL && gExperienceTables[gBaseStats[species].growthRate][level] <= exp)
+ level++;
+
+ return level - 1;
+}
+
+u8 GetLevelFromBoxMonExp(struct BoxPokemon *boxMon)
+{
+ u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
+ u32 exp = GetBoxMonData(boxMon, MON_DATA_EXP, NULL);
+ s32 level = 1;
+
+ while (level <= MAX_MON_LEVEL && gExperienceTables[gBaseStats[species].growthRate][level] <= exp)
+ level++;
+
+ return level - 1;
+}
+
+u16 GiveMoveToMon(struct Pokemon *mon, u16 move)
+{
+ return GiveMoveToBoxMon(&mon->box, move);
+}
+
+u16 GiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move)
+{
+ s32 i;
+ for (i = 0; i < 4; i++)
+ {
+ u16 existingMove = GetBoxMonData(boxMon, MON_DATA_MOVE1 + i, NULL);
+ if (!existingMove)
+ {
+ SetBoxMonData(boxMon, MON_DATA_MOVE1 + i, &move);
+ SetBoxMonData(boxMon, MON_DATA_PP1 + i, &gBattleMoves[move].pp);
+ return move;
+ }
+ if (existingMove == move)
+ return -2;
+ }
+ return -1;
+}
+
+u16 GiveMoveToBattleMon(struct BattlePokemon *mon, u16 move)
+{
+ s32 i;
+
+ for (i = 0; i < 4; i++)
+ {
+ if (!mon->moves[i])
+ {
+ mon->moves[i] = move;
+ mon->pp[i] = gBattleMoves[move].pp;
+ return move;
+ }
+ }
+
+ return -1;
+}
+
+void SetMonMoveSlot(struct Pokemon *mon, u16 move, u8 slot)
+{
+ SetMonData(mon, MON_DATA_MOVE1 + slot, &move);
+ SetMonData(mon, MON_DATA_PP1 + slot, &gBattleMoves[move].pp);
+}
+
+void SetBattleMonMoveSlot(struct BattlePokemon *mon, u16 move, u8 slot)
+{
+ mon->moves[slot] = move;
+ mon->pp[slot] = gBattleMoves[move].pp;
+}
+
+void GiveMonInitialMoveset(struct Pokemon *mon)
+{
+ GiveBoxMonInitialMoveset(&mon->box);
+}
+
+// TODO: make level_up_learnsets.h in src/data and move this to there.
+#define LEVEL_UP_END 0xffff
+
+void GiveBoxMonInitialMoveset(struct BoxPokemon *boxMon)
+{
+ u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
+ s32 level = GetLevelFromBoxMonExp(boxMon);
+ s32 i;
+
+ for (i = 0; gLevelUpLearnsets[species][i] != LEVEL_UP_END; i++)
+ {
+ u16 moveLevel;
+ u16 move;
+
+ moveLevel = (gLevelUpLearnsets[species][i] & 0xFE00);
+
+ if (moveLevel > (level << 9))
+ break;
+
+ move = (gLevelUpLearnsets[species][i] & 0x1FF);
+
+ if (GiveMoveToBoxMon(boxMon, move) == 0xFFFF)
+ DeleteFirstMoveAndGiveMoveToBoxMon(boxMon, move);
+ }
+}
+
+u16 MonTryLearningNewMove(struct Pokemon *mon, bool8 firstMove)
+{
+ u32 retVal = 0;
+ u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
+ u8 level = GetMonData(mon, MON_DATA_LEVEL, NULL);
+
+ // since you can learn more than one move per level
+ // the game needs to know whether you decided to
+ // learn it or keep the old set to avoid asking
+ // you to learn the same move over and over again
+ if (firstMove)
+ {
+ sLearningMoveTableID = 0;
+
+ while ((gLevelUpLearnsets[species][sLearningMoveTableID] & 0xFE00) != (level << 9))
+ {
+ sLearningMoveTableID++;
+ if (gLevelUpLearnsets[species][sLearningMoveTableID] == LEVEL_UP_END)
+ return 0;
+ }
+ }
+
+ if ((gLevelUpLearnsets[species][sLearningMoveTableID] & 0xFE00) == (level << 9))
+ {
+ gMoveToLearn = (gLevelUpLearnsets[species][sLearningMoveTableID] & 0x1FF);
+ sLearningMoveTableID++;
+ retVal = GiveMoveToMon(mon, gMoveToLearn);
+ }
+
+ return retVal;
+}
+
+void DeleteFirstMoveAndGiveMoveToMon(struct Pokemon *mon, u16 move)
+{
+ s32 i;
+ u16 moves[4];
+ u8 pp[4];
+ u8 ppBonuses;
+
+ for (i = 0; i < 3; i++)
+ {
+ moves[i] = GetMonData(mon, MON_DATA_MOVE2 + i, NULL);
+ pp[i] = GetMonData(mon, MON_DATA_PP2 + i, NULL);
+ }
+
+ ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES, NULL);
+ ppBonuses >>= 2;
+ moves[3] = move;
+ pp[3] = gBattleMoves[move].pp;
+
+ for (i = 0; i < 4; i++)
+ {
+ SetMonData(mon, MON_DATA_MOVE1 + i, &moves[i]);
+ SetMonData(mon, MON_DATA_PP1 + i, &pp[i]);
+ }
+
+ SetMonData(mon, MON_DATA_PP_BONUSES, &ppBonuses);
+}
+
+void DeleteFirstMoveAndGiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move)
+{
+ s32 i;
+ u16 moves[4];
+ u8 pp[4];
+ u8 ppBonuses;
+
+ for (i = 0; i < 3; i++)
+ {
+ moves[i] = GetBoxMonData(boxMon, MON_DATA_MOVE2 + i, NULL);
+ pp[i] = GetBoxMonData(boxMon, MON_DATA_PP2 + i, NULL);
+ }
+
+ ppBonuses = GetBoxMonData(boxMon, MON_DATA_PP_BONUSES, NULL);
+ ppBonuses >>= 2;
+ moves[3] = move;
+ pp[3] = gBattleMoves[move].pp;
+
+ for (i = 0; i < 4; i++)
+ {
+ SetBoxMonData(boxMon, MON_DATA_MOVE1 + i, &moves[i]);
+ SetBoxMonData(boxMon, MON_DATA_PP1 + i, &pp[i]);
+ }
+
+ SetBoxMonData(boxMon, MON_DATA_PP_BONUSES, &ppBonuses);
+}
+
+#define APPLY_STAT_MOD(var, mon, stat, statIndex) \
+{ \
+ (var) = (stat) * (gStatStageRatios)[(mon)->statStages[(statIndex)]][0]; \
+ (var) /= (gStatStageRatios)[(mon)->statStages[(statIndex)]][1]; \
+}
+
+s32 CalculateBaseDamage(struct BattlePokemon *attacker, struct BattlePokemon *defender, u32 move, u16 sideStatus, u16 powerOverride, u8 typeOverride, u8 battlerIdAtk, u8 battlerIdDef)
+{
+ u32 i;
+ s32 damage = 0;
+ s32 damageHelper;
+ u8 type;
+ u16 attack, defense;
+ u16 spAttack, spDefense;
+ u8 defenderHoldEffect;
+ u8 defenderHoldEffectParam;
+ u8 attackerHoldEffect;
+ u8 attackerHoldEffectParam;
+
+ if (!powerOverride)
+ gBattleMovePower = gBattleMoves[move].power;
+ else
+ gBattleMovePower = powerOverride;
+
+ if (!typeOverride)
+ type = gBattleMoves[move].type;
+ else
+ type = typeOverride & 0x3F;
+
+ attack = attacker->attack;
+ defense = defender->defense;
+ spAttack = attacker->spAttack;
+ spDefense = defender->spDefense;
+
+ if (attacker->item == ITEM_ENIGMA_BERRY)
+ {
+ attackerHoldEffect = gEnigmaBerries[battlerIdAtk].holdEffect;
+ attackerHoldEffectParam = gEnigmaBerries[battlerIdAtk].holdEffectParam;
+ }
+ else
+ {
+ attackerHoldEffect = ItemId_GetHoldEffect(attacker->item);
+ attackerHoldEffectParam = ItemId_GetHoldEffectParam(attacker->item);
+ }
+
+ if (defender->item == ITEM_ENIGMA_BERRY)
+ {
+ defenderHoldEffect = gEnigmaBerries[battlerIdDef].holdEffect;
+ defenderHoldEffectParam = gEnigmaBerries[battlerIdDef].holdEffectParam;
+ }
+ else
+ {
+ defenderHoldEffect = ItemId_GetHoldEffect(defender->item);
+ defenderHoldEffectParam = ItemId_GetHoldEffectParam(defender->item);
+ }
+
+ if (attacker->ability == ABILITY_HUGE_POWER || attacker->ability == ABILITY_PURE_POWER)
+ attack *= 2;
+
+ // In FRLG, the Battle Tower and opponent checks are stubbed here.
+ if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | /*BATTLE_TYPE_BATTLE_TOWER |*/ BATTLE_TYPE_EREADER_TRAINER)))
+ {
+ if (FlagGet(FLAG_UNK820)
+ && !GetBattlerSide(battlerIdAtk))
+ attack = (110 * attack) / 100;
+ }
+ if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | /*BATTLE_TYPE_BATTLE_TOWER |*/ BATTLE_TYPE_EREADER_TRAINER)))
+ {
+ if (FlagGet(FLAG_UNK824)
+ && !GetBattlerSide(battlerIdDef))
+ defense = (110 * defense) / 100;
+ }
+ if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | /*BATTLE_TYPE_BATTLE_TOWER |*/ BATTLE_TYPE_EREADER_TRAINER)))
+ {
+ if (FlagGet(FLAG_UNK826)
+ && !GetBattlerSide(battlerIdAtk))
+ spAttack = (110 * spAttack) / 100;
+ }
+ if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | /*BATTLE_TYPE_BATTLE_TOWER |*/ BATTLE_TYPE_EREADER_TRAINER)))
+ {
+ if (FlagGet(FLAG_UNK826)
+ && !GetBattlerSide(battlerIdDef))
+ spDefense = (110 * spDefense) / 100;
+ }
+
+ // TODO: Use ARRAY_COUNT(sHoldEffectToType)
+ for (i = 0; i < 17; i++)
+ {
+ if (attackerHoldEffect == sHoldEffectToType[i][0]
+ && type == sHoldEffectToType[i][1])
+ {
+ if (IS_TYPE_PHYSICAL(type))
+ attack = (attack * (attackerHoldEffectParam + 100)) / 100;
+ else
+ spAttack = (spAttack * (attackerHoldEffectParam + 100)) / 100;
+ break;
+ }
+ }
+
+ if (attackerHoldEffect == HOLD_EFFECT_CHOICE_BAND)
+ attack = (150 * attack) / 100;
+ if (attackerHoldEffect == HOLD_EFFECT_SOUL_DEW && !(gBattleTypeFlags & (BATTLE_TYPE_BATTLE_TOWER)) && (attacker->species == SPECIES_LATIAS || attacker->species == SPECIES_LATIOS))
+ spAttack = (150 * spAttack) / 100;
+ if (defenderHoldEffect == HOLD_EFFECT_SOUL_DEW && !(gBattleTypeFlags & (BATTLE_TYPE_BATTLE_TOWER)) && (defender->species == SPECIES_LATIAS || defender->species == SPECIES_LATIOS))
+ spDefense = (150 * spDefense) / 100;
+ if (attackerHoldEffect == HOLD_EFFECT_DEEP_SEA_TOOTH && attacker->species == SPECIES_CLAMPERL)
+ spAttack *= 2;
+ if (defenderHoldEffect == HOLD_EFFECT_DEEP_SEA_SCALE && defender->species == SPECIES_CLAMPERL)
+ spDefense *= 2;
+ if (attackerHoldEffect == HOLD_EFFECT_LIGHT_BALL && attacker->species == SPECIES_PIKACHU)
+ spAttack *= 2;
+ if (defenderHoldEffect == HOLD_EFFECT_METAL_POWDER && defender->species == SPECIES_DITTO)
+ defense *= 2;
+ if (attackerHoldEffect == HOLD_EFFECT_THICK_CLUB && (attacker->species == SPECIES_CUBONE || attacker->species == SPECIES_MAROWAK))
+ attack *= 2;
+ if (defender->ability == ABILITY_THICK_FAT && (type == TYPE_FIRE || type == TYPE_ICE))
+ spAttack /= 2;
+ if (attacker->ability == ABILITY_HUSTLE)
+ attack = (150 * attack) / 100;
+ if (attacker->ability == ABILITY_PLUS && ABILITY_ON_FIELD2(ABILITY_MINUS))
+ spAttack = (150 * spAttack) / 100;
+ if (attacker->ability == ABILITY_MINUS && ABILITY_ON_FIELD2(ABILITY_PLUS))
+ spAttack = (150 * spAttack) / 100;
+ if (attacker->ability == ABILITY_GUTS && attacker->status1)
+ attack = (150 * attack) / 100;
+ if (defender->ability == ABILITY_MARVEL_SCALE && defender->status1)
+ defense = (150 * defense) / 100;
+ if (type == TYPE_ELECTRIC && AbilityBattleEffects(ABILITYEFFECT_FIELD_SPORT, 0, 0, 0xFD, 0))
+ gBattleMovePower /= 2;
+ if (type == TYPE_FIRE && AbilityBattleEffects(ABILITYEFFECT_FIELD_SPORT, 0, 0, 0xFE, 0))
+ gBattleMovePower /= 2;
+ if (type == TYPE_GRASS && attacker->ability == ABILITY_OVERGROW && attacker->hp <= (attacker->maxHP / 3))
+ gBattleMovePower = (150 * gBattleMovePower) / 100;
+ if (type == TYPE_FIRE && attacker->ability == ABILITY_BLAZE && attacker->hp <= (attacker->maxHP / 3))
+ gBattleMovePower = (150 * gBattleMovePower) / 100;
+ if (type == TYPE_WATER && attacker->ability == ABILITY_TORRENT && attacker->hp <= (attacker->maxHP / 3))
+ gBattleMovePower = (150 * gBattleMovePower) / 100;
+ if (type == TYPE_BUG && attacker->ability == ABILITY_SWARM && attacker->hp <= (attacker->maxHP / 3))
+ gBattleMovePower = (150 * gBattleMovePower) / 100;
+ if (gBattleMoves[gCurrentMove].effect == EFFECT_EXPLOSION)
+ defense /= 2;
+
+ if (IS_TYPE_PHYSICAL(type))
+ {
+ if (gCritMultiplier == 2)
+ {
+ if (attacker->statStages[STAT_ATK] > 6)
+ APPLY_STAT_MOD(damage, attacker, attack, STAT_ATK)
+ else
+ damage = attack;
+ }
+ else
+ APPLY_STAT_MOD(damage, attacker, attack, STAT_ATK)
+
+ damage = damage * gBattleMovePower;
+ damage *= (2 * attacker->level / 5 + 2);
+
+ if (gCritMultiplier == 2)
+ {
+ if (defender->statStages[STAT_DEF] < 6)
+ APPLY_STAT_MOD(damageHelper, defender, defense, STAT_DEF)
+ else
+ damageHelper = defense;
+ }
+ else
+ APPLY_STAT_MOD(damageHelper, defender, defense, STAT_DEF)
+
+ damage = damage / damageHelper;
+ damage /= 50;
+
+ if ((attacker->status1 & STATUS1_BURN) && attacker->ability != ABILITY_GUTS)
+ damage /= 2;
+
+ if ((sideStatus & SIDE_STATUS_REFLECT) && gCritMultiplier == 1)
+ {
+ if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && CountAliveMons(2) == 2)
+ damage = 2 * (damage / 3);
+ else
+ damage /= 2;
+ }
+
+ if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && gBattleMoves[move].target == 8 && CountAliveMons(2) == 2)
+ damage /= 2;
+
+ // moves always do at least 1 damage.
+ if (damage == 0)
+ damage = 1;
+ }
+
+ if (type == TYPE_MYSTERY)
+ damage = 0; // is ??? type. does 0 damage.
+
+ if (IS_TYPE_SPECIAL(type))
+ {
+ if (gCritMultiplier == 2)
+ {
+ if (attacker->statStages[STAT_SPATK] > 6)
+ APPLY_STAT_MOD(damage, attacker, spAttack, STAT_SPATK)
+ else
+ damage = spAttack;
+ }
+ else
+ APPLY_STAT_MOD(damage, attacker, spAttack, STAT_SPATK)
+
+ damage = damage * gBattleMovePower;
+ damage *= (2 * attacker->level / 5 + 2);
+
+ if (gCritMultiplier == 2)
+ {
+ if (defender->statStages[STAT_SPDEF] < 6)
+ APPLY_STAT_MOD(damageHelper, defender, spDefense, STAT_SPDEF)
+ else
+ damageHelper = spDefense;
+ }
+ else
+ APPLY_STAT_MOD(damageHelper, defender, spDefense, STAT_SPDEF)
+
+ damage = (damage / damageHelper);
+ damage /= 50;
+
+ if ((sideStatus & SIDE_STATUS_LIGHTSCREEN) && gCritMultiplier == 1)
+ {
+ if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && CountAliveMons(2) == 2)
+ damage = 2 * (damage / 3);
+ else
+ damage /= 2;
+ }
+
+ if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && gBattleMoves[move].target == 8 && CountAliveMons(2) == 2)
+ damage /= 2;
+
+ // are effects of weather negated with cloud nine or air lock
+ if (WEATHER_HAS_EFFECT2)
+ {
+ if (gBattleWeather & WEATHER_RAIN_TEMPORARY)
+ {
+ switch (type)
+ {
+ case TYPE_FIRE:
+ damage /= 2;
+ break;
+ case TYPE_WATER:
+ damage = (15 * damage) / 10;
+ break;
+ }
+ }
+
+ // any weather except sun weakens solar beam
+ if ((gBattleWeather & (WEATHER_RAIN_ANY | WEATHER_SANDSTORM_ANY | WEATHER_HAIL)) && gCurrentMove == MOVE_SOLAR_BEAM)
+ damage /= 2;
+
+ // sunny
+ if (gBattleWeather & WEATHER_SUN_ANY)
+ {
+ switch (type)
+ {
+ case TYPE_FIRE:
+ damage = (15 * damage) / 10;
+ break;
+ case TYPE_WATER:
+ damage /= 2;
+ break;
+ }
+ }
+ }
+
+ // flash fire triggered
+ if ((gBattleResources->flags->flags[battlerIdAtk] & UNKNOWN_FLAG_FLASH_FIRE) && type == TYPE_FIRE)
+ damage = (15 * damage) / 10;
+ }
+
+ return damage + 2;
+}
+
+u8 CountAliveMons(u8 a1)
+{
+ s32 i;
+ u8 retVal = 0;
+
+ switch (a1)
+ {
+ case 0:
+ for (i = 0; i < 4; i++)
+ {
+ if (i != gActiveBattler && !(gAbsentBattlerFlags & gBitTable[i]))
+ retVal++;
+ }
+ break;
+ case 1:
+ for (i = 0; i < 4; i++)
+ {
+ if (GetBattlerSide(i) == GetBattlerSide(sBattler_AI) && !(gAbsentBattlerFlags & gBitTable[i]))
+ retVal++;
+ }
+ break;
+ case 2:
+ for (i = 0; i < 4; i++)
+ {
+ if (GetBattlerSide(i) == GetBattlerSide(gBattlerTarget) && !(gAbsentBattlerFlags & gBitTable[i]))
+ retVal++;
+ }
+ break;
+ }
+
+ return retVal;
+}
+
+u8 GetDefaultMoveTarget(u8 a1)
+{
+ u8 status = GetBattlerPosition(a1) & 1;
+
+ status ^= 1;
+ if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
+ return GetBattlerAtPosition(status);
+ if (CountAliveMons(0) > 1)
+ {
+ u8 val;
+
+ if ((Random() & 1) == 0)
+ val = status ^ 2;
+ else
+ val = status;
+ return GetBattlerAtPosition(val);
+ }
+ else
+ {
+ if ((gAbsentBattlerFlags & gBitTable[status]))
+ return GetBattlerAtPosition(status ^ 2);
+ else
+ return GetBattlerAtPosition(status);
+ }
+}
+
+u8 GetMonGender(struct Pokemon *mon)
+{
+ return GetBoxMonGender(&mon->box);
+}
+
+u8 GetBoxMonGender(struct BoxPokemon *boxMon)
+{
+ u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
+ u32 personality = GetBoxMonData(boxMon, MON_DATA_PERSONALITY, NULL);
+
+ switch (gBaseStats[species].genderRatio)
+ {
+ case MON_MALE:
+ case MON_FEMALE:
+ case MON_GENDERLESS:
+ return gBaseStats[species].genderRatio;
+ }
+
+ if (gBaseStats[species].genderRatio > (personality & 0xFF))
+ return MON_FEMALE;
+ else
+ return MON_MALE;
+}
+
+u8 GetGenderFromSpeciesAndPersonality(u16 species, u32 personality)
+{
+ switch (gBaseStats[species].genderRatio)
+ {
+ case MON_MALE:
+ case MON_FEMALE:
+ case MON_GENDERLESS:
+ return gBaseStats[species].genderRatio;
+ }
+
+ if (gBaseStats[species].genderRatio > (personality & 0xFF))
+ return MON_FEMALE;
+ else
+ return MON_MALE;
+}
+
+void sub_803F7D4(u16 trainerSpriteId, u8 battlerPosition)
+{
+ if(gMonSpritesGfxPtr != NULL)
+ {
+ if(battlerPosition >= 4)
+ battlerPosition = 0;
+
+ gMultiuseSpriteTemplate = gMonSpritesGfxPtr->templates[battlerPosition];
+ }
+ else
+ {
+ if(gUnknown_20244F4)
+ {
+ if(battlerPosition >= (s8)gUnknown_20244F4->unk0_2) // why a cast?!? changing the unk0_2 type to s8 causes extra shifts, but a cast is the correct fix. why, compiler?
+ battlerPosition = 0;
+
+ gMultiuseSpriteTemplate = gUnknown_20244F4->unk10[battlerPosition];
+ }
+ else
+ {
+ if(battlerPosition >= 4)
+ battlerPosition = 0;
+
+ gMultiuseSpriteTemplate = gUnknown_825DEF0[battlerPosition];
+ }
+ }
+ gMultiuseSpriteTemplate.paletteTag = trainerSpriteId;
+ gMultiuseSpriteTemplate.anims = gUnknown_82349BC;
+}
+
+void SetMultiuseSpriteTemplateToTrainerBack(u16 trainerSpriteId, u8 battlerPosition)
+{
+ gMultiuseSpriteTemplate.paletteTag = trainerSpriteId;
+ if(battlerPosition == B_POSITION_PLAYER_LEFT || battlerPosition == B_POSITION_PLAYER_RIGHT)
+ {
+ gMultiuseSpriteTemplate = gUnknown_825DF50[trainerSpriteId];
+ gMultiuseSpriteTemplate.anims = gTrainerBackAnimsPtrTable[trainerSpriteId];
+ }
+ else
+ {
+ if (gMonSpritesGfxPtr != NULL)
+ gMultiuseSpriteTemplate = gMonSpritesGfxPtr->templates[battlerPosition];
+ else
+ gMultiuseSpriteTemplate = gUnknown_825DEF0[battlerPosition];
+ gMultiuseSpriteTemplate.anims = gTrainerFrontAnimsPtrTable[trainerSpriteId];
+ }
+}
+
+void EncryptBoxMon(struct BoxPokemon *boxMon)
+{
+ u32 i;
+ for (i = 0; i < 12; i++)
+ {
+ boxMon->secure.raw[i] ^= boxMon->personality;
+ boxMon->secure.raw[i] ^= boxMon->otId;
+ }
+}
+
+void DecryptBoxMon(struct BoxPokemon *boxMon)
+{
+ u32 i;
+ for (i = 0; i < 12; i++)
+ {
+ boxMon->secure.raw[i] ^= boxMon->otId;
+ boxMon->secure.raw[i] ^= boxMon->personality;
+ }
+}
+
+#define SUBSTRUCT_CASE(n, v1, v2, v3, v4) \
+case n: \
+ { \
+ union PokemonSubstruct *substructs0 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs1 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs2 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs3 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs4 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs5 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs6 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs7 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs8 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs9 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs10 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs11 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs12 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs13 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs14 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs15 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs16 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs17 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs18 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs19 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs20 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs21 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs22 = boxMon->secure.substructs; \
+ union PokemonSubstruct *substructs23 = boxMon->secure.substructs; \
+ \
+ switch (substructType) \
+ { \
+ case 0: \
+ substruct = &substructs ## n [v1]; \
+ break; \
+ case 1: \
+ substruct = &substructs ## n [v2]; \
+ break; \
+ case 2: \
+ substruct = &substructs ## n [v3]; \
+ break; \
+ case 3: \
+ substruct = &substructs ## n [v4]; \
+ break; \
+ } \
+ break; \
+ } \
+
+union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 personality, u8 substructType)
+{
+ union PokemonSubstruct *substruct = NULL;
+
+ switch (personality % 24)
+ {
+ SUBSTRUCT_CASE( 0,0,1,2,3)
+ SUBSTRUCT_CASE( 1,0,1,3,2)
+ SUBSTRUCT_CASE( 2,0,2,1,3)
+ SUBSTRUCT_CASE( 3,0,3,1,2)
+ SUBSTRUCT_CASE( 4,0,2,3,1)
+ SUBSTRUCT_CASE( 5,0,3,2,1)
+ SUBSTRUCT_CASE( 6,1,0,2,3)
+ SUBSTRUCT_CASE( 7,1,0,3,2)
+ SUBSTRUCT_CASE( 8,2,0,1,3)
+ SUBSTRUCT_CASE( 9,3,0,1,2)
+ SUBSTRUCT_CASE(10,2,0,3,1)
+ SUBSTRUCT_CASE(11,3,0,2,1)
+ SUBSTRUCT_CASE(12,1,2,0,3)
+ SUBSTRUCT_CASE(13,1,3,0,2)
+ SUBSTRUCT_CASE(14,2,1,0,3)
+ SUBSTRUCT_CASE(15,3,1,0,2)
+ SUBSTRUCT_CASE(16,2,3,0,1)
+ SUBSTRUCT_CASE(17,3,2,0,1)
+ SUBSTRUCT_CASE(18,1,2,3,0)
+ SUBSTRUCT_CASE(19,1,3,2,0)
+ SUBSTRUCT_CASE(20,2,1,3,0)
+ SUBSTRUCT_CASE(21,3,1,2,0)
+ SUBSTRUCT_CASE(22,2,3,1,0)
+ SUBSTRUCT_CASE(23,3,2,1,0)
+ }
+
+ return substruct;
+}
+
+u32 GetMonData(struct Pokemon *mon, s32 field, u8* data)
+{
+ u32 ret;
+
+ switch (field)
+ {
+ case MON_DATA_STATUS:
+ ret = mon->status;
+ break;
+ case MON_DATA_LEVEL:
+ ret = mon->level;
+ break;
+ case MON_DATA_HP:
+ ret = mon->hp;
+ break;
+ case MON_DATA_MAX_HP:
+ ret = mon->maxHP;
+ break;
+ case MON_DATA_ATK:
+ ret = (u16)GetDeoxysStat(mon, STAT_ATK);
+ if (!ret)
+ ret = mon->attack;
+ break;
+ case MON_DATA_DEF:
+ ret = (u16)GetDeoxysStat(mon, STAT_DEF);
+ if (!ret)
+ ret = mon->defense;
+ break;
+ case MON_DATA_SPEED:
+ ret = (u16)GetDeoxysStat(mon, STAT_SPEED);
+ if (!ret)
+ ret = mon->speed;
+ break;
+ case MON_DATA_SPATK:
+ ret = (u16)GetDeoxysStat(mon, STAT_SPATK);
+ if (!ret)
+ ret = mon->spAttack;
+ break;
+ case MON_DATA_SPDEF:
+ ret = (u16)GetDeoxysStat(mon, STAT_SPDEF);
+ if (!ret)
+ ret = mon->spDefense;
+ break;
+ case MON_DATA_ATK2:
+ ret = mon->attack;
+ break;
+ case MON_DATA_DEF2:
+ ret = mon->defense;
+ break;
+ case MON_DATA_SPEED2:
+ ret = mon->speed;
+ break;
+ case MON_DATA_SPATK2:
+ ret = mon->spAttack;
+ break;
+ case MON_DATA_SPDEF2:
+ ret = mon->spDefense;
+ break;
+ case MON_DATA_MAIL:
+ ret = mon->mail;
+ break;
+ default:
+ ret = GetBoxMonData(&mon->box, field, data);
+ break;
+ }
+ return ret;
+}
+
+u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data)
+{
+ s32 i;
+ u32 retVal = 0;
+ struct PokemonSubstruct0 *substruct0 = NULL;
+ struct PokemonSubstruct1 *substruct1 = NULL;
+ struct PokemonSubstruct2 *substruct2 = NULL;
+ struct PokemonSubstruct3 *substruct3 = NULL;
+
+ if (field > MON_DATA_10)
+ {
+ substruct0 = &(GetSubstruct(boxMon, boxMon->personality, 0)->type0);
+ substruct1 = &(GetSubstruct(boxMon, boxMon->personality, 1)->type1);
+ substruct2 = &(GetSubstruct(boxMon, boxMon->personality, 2)->type2);
+ substruct3 = &(GetSubstruct(boxMon, boxMon->personality, 3)->type3);
+
+ DecryptBoxMon(boxMon);
+
+ if (CalculateBoxMonChecksum(boxMon) != boxMon->checksum)
+ {
+ boxMon->isBadEgg = 1;
+ boxMon->isEgg = 1;
+ substruct3->isEgg = 1;
+ }
+ }
+
+ switch (field)
+ {
+ case MON_DATA_PERSONALITY:
+ retVal = boxMon->personality;
+ break;
+ case MON_DATA_OT_ID:
+ retVal = boxMon->otId;
+ break;
+ case MON_DATA_NICKNAME:
+ {
+ if (boxMon->isBadEgg)
+ {
+ for (retVal = 0;
+ retVal < POKEMON_NAME_LENGTH && gText_BadEgg[retVal] != EOS;
+ data[retVal] = gText_BadEgg[retVal], retVal++) {}
+
+ data[retVal] = EOS;
+ }
+ else if (boxMon->isEgg)
+ {
+ StringCopy(data, gText_EggNickname);
+ retVal = StringLength(data);
+ }
+ else if (boxMon->language == LANGUAGE_JAPANESE)
+ {
+ data[0] = EXT_CTRL_CODE_BEGIN;
+ data[1] = EXT_CTRL_CODE_JPN;
+
+ // FRLG changed i < 7 to i < 6
+ for (retVal = 2, i = 0;
+ i < 6 && boxMon->nickname[i] != EOS;
+ data[retVal] = boxMon->nickname[i], retVal++, i++) {}
+
+ data[retVal++] = EXT_CTRL_CODE_BEGIN;
+ data[retVal++] = EXT_CTRL_CODE_ENG;
+ data[retVal] = EOS;
+ }
+ else
+ {
+ for (retVal = 0;
+ retVal < POKEMON_NAME_LENGTH;
+ data[retVal] = boxMon->nickname[retVal], retVal++){}
+
+ data[retVal] = EOS;
+ }
+ break;
+ }
+ case MON_DATA_LANGUAGE:
+ retVal = boxMon->language;
+ break;
+ case MON_DATA_SANITY_BIT1:
+ retVal = boxMon->isBadEgg;
+ break;
+ case MON_DATA_SANITY_BIT2:
+ retVal = boxMon->hasSpecies;
+ break;
+ case MON_DATA_SANITY_BIT3:
+ retVal = boxMon->isEgg;
+ break;
+ case MON_DATA_OT_NAME:
+ {
+ retVal = 0;
+
+ // FRLG changed this to 7 which used to be PLAYER_NAME_LENGTH
+ while (retVal < 7)
+ {
+ data[retVal] = boxMon->otName[retVal];
+ retVal++;
+ }
+
+ data[retVal] = EOS;
+ break;
+ }
+ case MON_DATA_MARKINGS:
+ retVal = boxMon->markings;
+ break;
+ case MON_DATA_CHECKSUM:
+ retVal = boxMon->checksum;
+ break;
+ case MON_DATA_10:
+ retVal = boxMon->unknown;
+ break;
+ case MON_DATA_SPECIES:
+ retVal = boxMon->isBadEgg ? SPECIES_EGG : substruct0->species;
+ break;
+ case MON_DATA_HELD_ITEM:
+ retVal = substruct0->heldItem;
+ break;
+ case MON_DATA_EXP:
+ retVal = substruct0->experience;
+ break;
+ case MON_DATA_PP_BONUSES:
+ retVal = substruct0->ppBonuses;
+ break;
+ case MON_DATA_FRIENDSHIP:
+ retVal = substruct0->friendship;
+ break;
+ case MON_DATA_MOVE1:
+ case MON_DATA_MOVE2:
+ case MON_DATA_MOVE3:
+ case MON_DATA_MOVE4:
+ retVal = substruct1->moves[field - MON_DATA_MOVE1];
+ break;
+ case MON_DATA_PP1:
+ case MON_DATA_PP2:
+ case MON_DATA_PP3:
+ case MON_DATA_PP4:
+ retVal = substruct1->pp[field - MON_DATA_PP1];
+ break;
+ case MON_DATA_HP_EV:
+ retVal = substruct2->hpEV;
+ break;
+ case MON_DATA_ATK_EV:
+ retVal = substruct2->attackEV;
+ break;
+ case MON_DATA_DEF_EV:
+ retVal = substruct2->defenseEV;
+ break;
+ case MON_DATA_SPEED_EV:
+ retVal = substruct2->speedEV;
+ break;
+ case MON_DATA_SPATK_EV:
+ retVal = substruct2->spAttackEV;
+ break;
+ case MON_DATA_SPDEF_EV:
+ retVal = substruct2->spDefenseEV;
+ break;
+ case MON_DATA_COOL:
+ retVal = substruct2->cool;
+ break;
+ case MON_DATA_BEAUTY:
+ retVal = substruct2->beauty;
+ break;
+ case MON_DATA_CUTE:
+ retVal = substruct2->cute;
+ break;
+ case MON_DATA_SMART:
+ retVal = substruct2->smart;
+ break;
+ case MON_DATA_TOUGH:
+ retVal = substruct2->tough;
+ break;
+ case MON_DATA_SHEEN:
+ retVal = substruct2->sheen;
+ break;
+ case MON_DATA_POKERUS:
+ retVal = substruct3->pokerus;
+ break;
+ case MON_DATA_MET_LOCATION:
+ retVal = substruct3->metLocation;
+ break;
+ case MON_DATA_MET_LEVEL:
+ retVal = substruct3->metLevel;
+ break;
+ case MON_DATA_MET_GAME:
+ retVal = substruct3->metGame;
+ break;
+ case MON_DATA_POKEBALL:
+ retVal = substruct3->pokeball;
+ break;
+ case MON_DATA_OT_GENDER:
+ retVal = substruct3->otGender;
+ break;
+ case MON_DATA_HP_IV:
+ retVal = substruct3->hpIV;
+ break;
+ case MON_DATA_ATK_IV:
+ retVal = substruct3->attackIV;
+ break;
+ case MON_DATA_DEF_IV:
+ retVal = substruct3->defenseIV;
+ break;
+ case MON_DATA_SPEED_IV:
+ retVal = substruct3->speedIV;
+ break;
+ case MON_DATA_SPATK_IV:
+ retVal = substruct3->spAttackIV;
+ break;
+ case MON_DATA_SPDEF_IV:
+ retVal = substruct3->spDefenseIV;
+ break;
+ case MON_DATA_IS_EGG:
+ retVal = substruct3->isEgg;
+ break;
+ case MON_DATA_ALT_ABILITY:
+ retVal = substruct3->altAbility;
+ break;
+ case MON_DATA_COOL_RIBBON:
+ retVal = substruct3->coolRibbon;
+ break;
+ case MON_DATA_BEAUTY_RIBBON:
+ retVal = substruct3->beautyRibbon;
+ break;
+ case MON_DATA_CUTE_RIBBON:
+ retVal = substruct3->cuteRibbon;
+ break;
+ case MON_DATA_SMART_RIBBON:
+ retVal = substruct3->smartRibbon;
+ break;
+ case MON_DATA_TOUGH_RIBBON:
+ retVal = substruct3->toughRibbon;
+ break;
+ case MON_DATA_CHAMPION_RIBBON:
+ retVal = substruct3->championRibbon;
+ break;
+ case MON_DATA_WINNING_RIBBON:
+ retVal = substruct3->winningRibbon;
+ break;
+ case MON_DATA_VICTORY_RIBBON:
+ retVal = substruct3->victoryRibbon;
+ break;
+ case MON_DATA_ARTIST_RIBBON:
+ retVal = substruct3->artistRibbon;
+ break;
+ case MON_DATA_EFFORT_RIBBON:
+ retVal = substruct3->effortRibbon;
+ break;
+ case MON_DATA_GIFT_RIBBON_1:
+ retVal = substruct3->giftRibbon1;
+ break;
+ case MON_DATA_GIFT_RIBBON_2:
+ retVal = substruct3->giftRibbon2;
+ break;
+ case MON_DATA_GIFT_RIBBON_3:
+ retVal = substruct3->giftRibbon3;
+ break;
+ case MON_DATA_GIFT_RIBBON_4:
+ retVal = substruct3->giftRibbon4;
+ break;
+ case MON_DATA_GIFT_RIBBON_5:
+ retVal = substruct3->giftRibbon5;
+ break;
+ case MON_DATA_GIFT_RIBBON_6:
+ retVal = substruct3->giftRibbon6;
+ break;
+ case MON_DATA_GIFT_RIBBON_7:
+ retVal = substruct3->giftRibbon7;
+ break;
+ case MON_DATA_FATEFUL_ENCOUNTER:
+ retVal = substruct3->fatefulEncounter;
+ break;
+ case MON_DATA_OBEDIENCE:
+ retVal = substruct3->obedient;
+ break;
+ case MON_DATA_SPECIES2:
+ retVal = substruct0->species;
+ if (substruct0->species && (substruct3->isEgg || boxMon->isBadEgg))
+ retVal = SPECIES_EGG;
+ break;
+ case MON_DATA_IVS:
+ retVal = substruct3->hpIV | (substruct3->attackIV << 5) | (substruct3->defenseIV << 10) | (substruct3->speedIV << 15) | (substruct3->spAttackIV << 20) | (substruct3->spDefenseIV << 25);
+ break;
+ case MON_DATA_KNOWN_MOVES:
+ if (substruct0->species && !substruct3->isEgg)
+ {
+ u16 *moves = (u16 *)data;
+ s32 i = 0;
+
+ while (moves[i] != 355)
+ {
+ u16 move = moves[i];
+ if (substruct1->moves[0] == move
+ || substruct1->moves[1] == move
+ || substruct1->moves[2] == move
+ || substruct1->moves[3] == move)
+ retVal |= gBitTable[i];
+ i++;
+ }
+ }
+ break;
+ case MON_DATA_RIBBON_COUNT:
+ retVal = 0;
+ if (substruct0->species && !substruct3->isEgg)
+ {
+ retVal += substruct3->coolRibbon;
+ retVal += substruct3->beautyRibbon;
+ retVal += substruct3->cuteRibbon;
+ retVal += substruct3->smartRibbon;
+ retVal += substruct3->toughRibbon;
+ retVal += substruct3->championRibbon;
+ retVal += substruct3->winningRibbon;
+ retVal += substruct3->victoryRibbon;
+ retVal += substruct3->artistRibbon;
+ retVal += substruct3->effortRibbon;
+ retVal += substruct3->giftRibbon1;
+ retVal += substruct3->giftRibbon2;
+ retVal += substruct3->giftRibbon3;
+ retVal += substruct3->giftRibbon4;
+ retVal += substruct3->giftRibbon5;
+ retVal += substruct3->giftRibbon6;
+ retVal += substruct3->giftRibbon7;
+ }
+ break;
+ case MON_DATA_RIBBONS:
+ retVal = 0;
+ if (substruct0->species && !substruct3->isEgg)
+ {
+ retVal = substruct3->championRibbon
+ | (substruct3->coolRibbon << 1)
+ | (substruct3->beautyRibbon << 4)
+ | (substruct3->cuteRibbon << 7)
+ | (substruct3->smartRibbon << 10)
+ | (substruct3->toughRibbon << 13)
+ | (substruct3->winningRibbon << 16)
+ | (substruct3->victoryRibbon << 17)
+ | (substruct3->artistRibbon << 18)
+ | (substruct3->effortRibbon << 19)
+ | (substruct3->giftRibbon1 << 20)
+ | (substruct3->giftRibbon2 << 21)
+ | (substruct3->giftRibbon3 << 22)
+ | (substruct3->giftRibbon4 << 23)
+ | (substruct3->giftRibbon5 << 24)
+ | (substruct3->giftRibbon6 << 25)
+ | (substruct3->giftRibbon7 << 26);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (field > MON_DATA_10)
+ EncryptBoxMon(boxMon);
+
+ return retVal;
+}
+
+#define SET8(lhs) (lhs) = *data
+#define SET16(lhs) (lhs) = data[0] + (data[1] << 8)
+#define SET32(lhs) (lhs) = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24)
+
+void SetMonData(struct Pokemon *mon, s32 field, const void *dataArg)
+{
+ const u8 *data = dataArg;
+
+ switch (field)
+ {
+ case MON_DATA_STATUS:
+ SET32(mon->status);
+ break;
+ case MON_DATA_LEVEL:
+ SET8(mon->level);
+ break;
+ case MON_DATA_HP:
+ SET16(mon->hp);
+ break;
+ case MON_DATA_MAX_HP:
+ SET16(mon->maxHP);
+ break;
+ case MON_DATA_ATK:
+ case MON_DATA_ATK2:
+ SET16(mon->attack);
+ break;
+ case MON_DATA_DEF:
+ case MON_DATA_DEF2:
+ SET16(mon->defense);
+ break;
+ case MON_DATA_SPEED:
+ case MON_DATA_SPEED2:
+ SET16(mon->speed);
+ break;
+ case MON_DATA_SPATK:
+ case MON_DATA_SPATK2:
+ SET16(mon->spAttack);
+ break;
+ case MON_DATA_SPDEF:
+ case MON_DATA_SPDEF2:
+ SET16(mon->spDefense);
+ break;
+ case MON_DATA_MAIL:
+ SET8(mon->mail);
+ break;
+ case MON_DATA_SPECIES2:
+ break;
+ // why did FRLG go out of its way to specify all of these for default?
+ case MON_DATA_IVS:
+ case MON_DATA_CHAMPION_RIBBON:
+ case MON_DATA_WINNING_RIBBON:
+ case MON_DATA_VICTORY_RIBBON:
+ case MON_DATA_ARTIST_RIBBON:
+ case MON_DATA_EFFORT_RIBBON:
+ case MON_DATA_GIFT_RIBBON_1:
+ case MON_DATA_GIFT_RIBBON_2:
+ case MON_DATA_GIFT_RIBBON_3:
+ case MON_DATA_GIFT_RIBBON_4:
+ case MON_DATA_GIFT_RIBBON_5:
+ case MON_DATA_GIFT_RIBBON_6:
+ case MON_DATA_GIFT_RIBBON_7:
+ case MON_DATA_FATEFUL_ENCOUNTER:
+ case MON_DATA_OBEDIENCE:
+ case MON_DATA_KNOWN_MOVES:
+ case MON_DATA_RIBBON_COUNT:
+ case MON_DATA_RIBBONS:
+ default:
+ SetBoxMonData(&mon->box, field, data);
+ break;
+ }
+}
+
+void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg)
+{
+ const u8 *data = dataArg;
+
+ struct PokemonSubstruct0 *substruct0 = NULL;
+ struct PokemonSubstruct1 *substruct1 = NULL;
+ struct PokemonSubstruct2 *substruct2 = NULL;
+ struct PokemonSubstruct3 *substruct3 = NULL;
+
+ if (field > MON_DATA_10)
+ {
+ substruct0 = &(GetSubstruct(boxMon, boxMon->personality, 0)->type0);
+ substruct1 = &(GetSubstruct(boxMon, boxMon->personality, 1)->type1);
+ substruct2 = &(GetSubstruct(boxMon, boxMon->personality, 2)->type2);
+ substruct3 = &(GetSubstruct(boxMon, boxMon->personality, 3)->type3);
+
+ DecryptBoxMon(boxMon);
+
+ if (CalculateBoxMonChecksum(boxMon) != boxMon->checksum)
+ {
+ boxMon->isBadEgg = 1;
+ boxMon->isEgg = 1;
+ substruct3->isEgg = 1;
+ EncryptBoxMon(boxMon);
+ return;
+ }
+ }
+
+ switch (field)
+ {
+ case MON_DATA_PERSONALITY:
+ SET32(boxMon->personality);
+ break;
+ case MON_DATA_OT_ID:
+ SET32(boxMon->otId);
+ break;
+ case MON_DATA_NICKNAME:
+ {
+ s32 i;
+ for (i = 0; i < POKEMON_NAME_LENGTH; i++)
+ boxMon->nickname[i] = data[i];
+ break;
+ }
+ case MON_DATA_LANGUAGE:
+ SET8(boxMon->language);
+ break;
+ case MON_DATA_SANITY_BIT1:
+ SET8(boxMon->isBadEgg);
+ break;
+ case MON_DATA_SANITY_BIT2:
+ SET8(boxMon->hasSpecies);
+ break;
+ case MON_DATA_SANITY_BIT3:
+ SET8(boxMon->isEgg);
+ break;
+ case MON_DATA_OT_NAME:
+ {
+ s32 i;
+ for (i = 0; i < 7; i++)
+ boxMon->otName[i] = data[i];
+ break;
+ }
+ case MON_DATA_MARKINGS:
+ SET8(boxMon->markings);
+ break;
+ case MON_DATA_CHECKSUM:
+ SET16(boxMon->checksum);
+ break;
+ case MON_DATA_10:
+ SET16(boxMon->unknown);
+ break;
+ case MON_DATA_SPECIES:
+ {
+ SET16(substruct0->species);
+ if (substruct0->species)
+ boxMon->hasSpecies = 1;
+ else
+ boxMon->hasSpecies = 0;
+ break;
+ }
+ case MON_DATA_HELD_ITEM:
+ SET16(substruct0->heldItem);
+ break;
+ case MON_DATA_EXP:
+ SET32(substruct0->experience);
+ break;
+ case MON_DATA_PP_BONUSES:
+ SET8(substruct0->ppBonuses);
+ break;
+ case MON_DATA_FRIENDSHIP:
+ SET8(substruct0->friendship);
+ break;
+ case MON_DATA_MOVE1:
+ case MON_DATA_MOVE2:
+ case MON_DATA_MOVE3:
+ case MON_DATA_MOVE4:
+ SET16(substruct1->moves[field - MON_DATA_MOVE1]);
+ break;
+ case MON_DATA_PP1:
+ case MON_DATA_PP2:
+ case MON_DATA_PP3:
+ case MON_DATA_PP4:
+ SET8(substruct1->pp[field - MON_DATA_PP1]);
+ break;
+ case MON_DATA_HP_EV:
+ SET8(substruct2->hpEV);
+ break;
+ case MON_DATA_ATK_EV:
+ SET8(substruct2->attackEV);
+ break;
+ case MON_DATA_DEF_EV:
+ SET8(substruct2->defenseEV);
+ break;
+ case MON_DATA_SPEED_EV:
+ SET8(substruct2->speedEV);
+ break;
+ case MON_DATA_SPATK_EV:
+ SET8(substruct2->spAttackEV);
+ break;
+ case MON_DATA_SPDEF_EV:
+ SET8(substruct2->spDefenseEV);
+ break;
+ case MON_DATA_COOL:
+ SET8(substruct2->cool);
+ break;
+ case MON_DATA_BEAUTY:
+ SET8(substruct2->beauty);
+ break;
+ case MON_DATA_CUTE:
+ SET8(substruct2->cute);
+ break;
+ case MON_DATA_SMART:
+ SET8(substruct2->smart);
+ break;
+ case MON_DATA_TOUGH:
+ SET8(substruct2->tough);
+ break;
+ case MON_DATA_SHEEN:
+ SET8(substruct2->sheen);
+ break;
+ case MON_DATA_POKERUS:
+ SET8(substruct3->pokerus);
+ break;
+ case MON_DATA_MET_LOCATION:
+ SET8(substruct3->metLocation);
+ break;
+ case MON_DATA_MET_LEVEL:
+ {
+ u8 metLevel = *data;
+ substruct3->metLevel = metLevel;
+ break;
+ }
+ case MON_DATA_MET_GAME:
+ SET8(substruct3->metGame);
+ break;
+ case MON_DATA_POKEBALL:
+ {
+ u8 pokeball = *data;
+ substruct3->pokeball = pokeball;
+ break;
+ }
+ case MON_DATA_OT_GENDER:
+ SET8(substruct3->otGender);
+ break;
+ case MON_DATA_HP_IV:
+ SET8(substruct3->hpIV);
+ break;
+ case MON_DATA_ATK_IV:
+ SET8(substruct3->attackIV);
+ break;
+ case MON_DATA_DEF_IV:
+ SET8(substruct3->defenseIV);
+ break;
+ case MON_DATA_SPEED_IV:
+ SET8(substruct3->speedIV);
+ break;
+ case MON_DATA_SPATK_IV:
+ SET8(substruct3->spAttackIV);
+ break;
+ case MON_DATA_SPDEF_IV:
+ SET8(substruct3->spDefenseIV);
+ break;
+ case MON_DATA_IS_EGG:
+ SET8(substruct3->isEgg);
+ if (substruct3->isEgg)
+ boxMon->isEgg = 1;
+ else
+ boxMon->isEgg = 0;
+ break;
+ case MON_DATA_ALT_ABILITY:
+ SET8(substruct3->altAbility);
+ break;
+ case MON_DATA_COOL_RIBBON:
+ SET8(substruct3->coolRibbon);
+ break;
+ case MON_DATA_BEAUTY_RIBBON:
+ SET8(substruct3->beautyRibbon);
+ break;
+ case MON_DATA_CUTE_RIBBON:
+ SET8(substruct3->cuteRibbon);
+ break;
+ case MON_DATA_SMART_RIBBON:
+ SET8(substruct3->smartRibbon);
+ break;
+ case MON_DATA_TOUGH_RIBBON:
+ SET8(substruct3->toughRibbon);
+ break;
+ case MON_DATA_CHAMPION_RIBBON:
+ SET8(substruct3->championRibbon);
+ break;
+ case MON_DATA_WINNING_RIBBON:
+ SET8(substruct3->winningRibbon);
+ break;
+ case MON_DATA_VICTORY_RIBBON:
+ SET8(substruct3->victoryRibbon);
+ break;
+ case MON_DATA_ARTIST_RIBBON:
+ SET8(substruct3->artistRibbon);
+ break;
+ case MON_DATA_EFFORT_RIBBON:
+ SET8(substruct3->effortRibbon);
+ break;
+ case MON_DATA_GIFT_RIBBON_1:
+ SET8(substruct3->giftRibbon1);
+ break;
+ case MON_DATA_GIFT_RIBBON_2:
+ SET8(substruct3->giftRibbon2);
+ break;
+ case MON_DATA_GIFT_RIBBON_3:
+ SET8(substruct3->giftRibbon3);
+ break;
+ case MON_DATA_GIFT_RIBBON_4:
+ SET8(substruct3->giftRibbon4);
+ break;
+ case MON_DATA_GIFT_RIBBON_5:
+ SET8(substruct3->giftRibbon5);
+ break;
+ case MON_DATA_GIFT_RIBBON_6:
+ SET8(substruct3->giftRibbon6);
+ break;
+ case MON_DATA_GIFT_RIBBON_7:
+ SET8(substruct3->giftRibbon7);
+ break;
+ case MON_DATA_FATEFUL_ENCOUNTER:
+ SET8(substruct3->fatefulEncounter);
+ break;
+ case MON_DATA_OBEDIENCE:
+ SET8(substruct3->obedient);
+ break;
+ case MON_DATA_IVS:
+ {
+#ifdef BUGFIX_SETMONIVS
+ u32 ivs = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+#else
+ u32 ivs = *data; // Bug: Only the HP IV and the lower 3 bits of the Attack IV are read. The rest become 0.
+#endif
+ substruct3->hpIV = ivs & 0x1F;
+ substruct3->attackIV = (ivs >> 5) & 0x1F;
+ substruct3->defenseIV = (ivs >> 10) & 0x1F;
+ substruct3->speedIV = (ivs >> 15) & 0x1F;
+ substruct3->spAttackIV = (ivs >> 20) & 0x1F;
+ substruct3->spDefenseIV = (ivs >> 25) & 0x1F;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (field > MON_DATA_10)
+ {
+ boxMon->checksum = CalculateBoxMonChecksum(boxMon);
+ EncryptBoxMon(boxMon);
+ }
+}
+
+void CopyMon(void *dest, void *src, size_t size)
+{
+ memcpy(dest, src, size);
+}
+
+u8 GiveMonToPlayer(struct Pokemon *mon)
+{
+ s32 i;
+
+ SetMonData(mon, MON_DATA_OT_NAME, gSaveBlock2Ptr->playerName);
+ SetMonData(mon, MON_DATA_OT_GENDER, &gSaveBlock2Ptr->playerGender);
+ SetMonData(mon, MON_DATA_OT_ID, gSaveBlock2Ptr->playerTrainerId);
+
+ for (i = 0; i < PARTY_SIZE; i++)
+ {
+ if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL) == SPECIES_NONE)
+ break;
+ }
+
+ if (i >= PARTY_SIZE)
+ return SendMonToPC(mon);
+
+ CopyMon(&gPlayerParty[i], mon, sizeof(*mon));
+ gPlayerPartyCount = i + 1;
+ return MON_GIVEN_TO_PARTY;
+}
+
+u8 SendMonToPC(struct Pokemon* mon)
+{
+ s32 boxNo, boxPos;
+
+ set_unknown_box_id(VarGet(VAR_0x4037));
+
+ boxNo = StorageGetCurrentBox();
+
+ do
+ {
+ for (boxPos = 0; boxPos < 30; boxPos++)
+ {
+ struct BoxPokemon* checkingMon = GetBoxedMonPtr(boxNo, boxPos);
+ if (GetBoxMonData(checkingMon, MON_DATA_SPECIES, NULL) == SPECIES_NONE)
+ {
+ MonRestorePP(mon);
+ CopyMon(checkingMon, &mon->box, sizeof(mon->box));
+ gSpecialVar_MonBoxId = boxNo;
+ gSpecialVar_MonBoxPos = boxPos;
+ if (get_unknown_box_id() != boxNo)
+ FlagClear(FLAG_UNK843);
+ VarSet(VAR_0x4037, boxNo);
+ return MON_GIVEN_TO_PC;
+ }
+ }
+
+ boxNo++;
+ if (boxNo == 14)
+ boxNo = 0;
+ } while (boxNo != StorageGetCurrentBox());
+
+ return MON_CANT_GIVE;
+}
+
+u8 CalculatePlayerPartyCount(void)
+{
+ gPlayerPartyCount = 0;
+
+ while (gPlayerPartyCount < 6
+ && GetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_SPECIES, NULL) != SPECIES_NONE)
+ {
+ gPlayerPartyCount++;
+ }
+
+ return gPlayerPartyCount;
+}
+
+
+u8 CalculateEnemyPartyCount(void)
+{
+ gEnemyPartyCount = 0;
+
+ while (gEnemyPartyCount < 6
+ && GetMonData(&gEnemyParty[gEnemyPartyCount], MON_DATA_SPECIES, NULL) != SPECIES_NONE)
+ {
+ gEnemyPartyCount++;
+ }
+
+ return gEnemyPartyCount;
+}
+
+u8 GetMonsStateToDoubles(void)
+{
+ s32 aliveCount = 0;
+ s32 i;
+ CalculatePlayerPartyCount();
+
+ if (gPlayerPartyCount == 1)
+ return gPlayerPartyCount; // PLAYER_HAS_ONE_MON
+
+ for (i = 0; i < gPlayerPartyCount; i++)
+ {
+ // FRLG changed the order of these checks, but there's no point to doing that
+ // because of the requirement of all 3 of these checks.
+ if (GetMonData(&gPlayerParty[i], MON_DATA_HP, NULL) != 0
+ && GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2, NULL) != SPECIES_NONE
+ && GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2, NULL) != SPECIES_EGG)
+ aliveCount++;
+ }
+
+ return (aliveCount > 1) ? PLAYER_HAS_TWO_USABLE_MONS : PLAYER_HAS_ONE_USABLE_MON;
+}
+
+u8 GetAbilityBySpecies(u16 species, bool8 altAbility)
+{
+ if (altAbility)
+ gLastUsedAbility = gBaseStats[species].ability2;
+ else
+ gLastUsedAbility = gBaseStats[species].ability1;
+
+ return gLastUsedAbility;
+}
+
+u8 GetMonAbility(struct Pokemon *mon)
+{
+ u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
+ u8 altAbility = GetMonData(mon, MON_DATA_ALT_ABILITY, NULL);
+ return GetAbilityBySpecies(species, altAbility);
+}
+
+void CreateSecretBaseEnemyParty(struct SecretBaseRecord *secretBaseRecord)
+{
+ s32 i, j;
+
+ ZeroEnemyPartyMons();
+ *gBattleResources->secretBase = *secretBaseRecord;
+
+ for (i = 0; i < PARTY_SIZE; i++)
+ {
+ if (gBattleResources->secretBase->party.species[i])
+ {
+ CreateMon(&gEnemyParty[i],
+ gBattleResources->secretBase->party.species[i],
+ gBattleResources->secretBase->party.levels[i],
+ 15,
+ 1,
+ gBattleResources->secretBase->party.personality[i],
+ 2,
+ 0);
+
+ SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gBattleResources->secretBase->party.heldItems[i]);
+
+ for (j = 0; j < 6; j++)
+ SetMonData(&gEnemyParty[i], MON_DATA_HP_EV + j, &gBattleResources->secretBase->party.EVs[i]);
+
+ for (j = 0; j < 4; j++)
+ {
+ SetMonData(&gEnemyParty[i], MON_DATA_MOVE1 + j, &gBattleResources->secretBase->party.moves[i * 4 + j]);
+ SetMonData(&gEnemyParty[i], MON_DATA_PP1 + j, &gBattleMoves[gBattleResources->secretBase->party.moves[i * 4 + j]].pp);
+ }
+ }
+ }
+ gBattleTypeFlags = 8;
+ gTrainerBattleOpponent_A = 0x400;
+}
+
+u8 GetSecretBaseTrainerPicIndex(void)
+{
+ u8 facilityClass = sSecretBaseFacilityClasses[gBattleResources->secretBase->gender][gBattleResources->secretBase->trainerId[0] % 5];
+ return gFacilityClassToPicIndex[facilityClass];
+}
+
+u8 GetSecretBaseTrainerNameIndex(void)
+{
+ u8 facilityClass = sSecretBaseFacilityClasses[gBattleResources->secretBase->gender][gBattleResources->secretBase->trainerId[0] % 5];
+ return gFacilityClassToTrainerClass[facilityClass];
+}
+
+bool8 IsPlayerPartyAndPokemonStorageFull(void)
+{
+ s32 i;
+
+ for (i = 0; i < PARTY_SIZE; i++)
+ if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL) == SPECIES_NONE)
+ return FALSE;
+
+ return IsPokemonStorageFull();
+}
+
+bool8 IsPokemonStorageFull(void)
+{
+ s32 i, j;
+
+ for (i = 0; i < 14; i++)
+ for (j = 0; j < 30; j++)
+ if (GetBoxMonDataFromAnyBox(i, j, MON_DATA_SPECIES) == SPECIES_NONE)
+ return FALSE;
+
+ return TRUE;
+}
+
+void GetSpeciesName(u8 *name, u16 species)
+{
+ s32 i;
+
+ // Hmm? FRLG has < while Ruby/Emerald has <=
+ for (i = 0; i < POKEMON_NAME_LENGTH; i++)
+ {
+ if (species > NUM_SPECIES)
+ name[i] = gSpeciesNames[0][i];
+ else
+ name[i] = gSpeciesNames[species][i];
+
+ if (name[i] == EOS)
+ break;
+ }
+
+ name[i] = EOS;
+}
+
+u8 CalculatePPWithBonus(u16 move, u8 ppBonuses, u8 moveIndex)
+{
+ u8 basePP = gBattleMoves[move].pp;
+ return basePP + ((basePP * 20 * ((gUnknown_825DEA1[moveIndex] & ppBonuses) >> (2 * moveIndex))) / 100);
+}
+
+void RemoveMonPPBonus(struct Pokemon *mon, u8 moveIndex)
+{
+ u8 ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES, NULL);
+ ppBonuses &= gPPUpWriteMasks[moveIndex];
+ SetMonData(mon, MON_DATA_PP_BONUSES, &ppBonuses);
+}
+
+void RemoveBattleMonPPBonus(struct BattlePokemon *mon, u8 moveIndex)
+{
+ mon->ppBonuses &= gPPUpWriteMasks[moveIndex];
+}
+
+void CopyPlayerPartyMonToBattleData(u8 battlerId, u8 partyIndex)
+{
+ u16* hpSwitchout;
+ s32 i;
+ u8 nickname[POKEMON_NAME_LENGTH * 2]; // Why is the nickname array here longer in FR/LG?
+
+ gBattleMons[battlerId].species = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPECIES, NULL);
+ gBattleMons[battlerId].item = GetMonData(&gPlayerParty[partyIndex], MON_DATA_HELD_ITEM, NULL);
+
+ for (i = 0; i < 4; i++)
+ {
+ gBattleMons[battlerId].moves[i] = GetMonData(&gPlayerParty[partyIndex], MON_DATA_MOVE1 + i, NULL);
+ gBattleMons[battlerId].pp[i] = GetMonData(&gPlayerParty[partyIndex], MON_DATA_PP1 + i, NULL);
+ }
+
+ gBattleMons[battlerId].ppBonuses = GetMonData(&gPlayerParty[partyIndex], MON_DATA_PP_BONUSES, NULL);
+ gBattleMons[battlerId].friendship = GetMonData(&gPlayerParty[partyIndex], MON_DATA_FRIENDSHIP, NULL);
+ gBattleMons[battlerId].experience = GetMonData(&gPlayerParty[partyIndex], MON_DATA_EXP, NULL);
+ gBattleMons[battlerId].hpIV = GetMonData(&gPlayerParty[partyIndex], MON_DATA_HP_IV, NULL);
+ gBattleMons[battlerId].attackIV = GetMonData(&gPlayerParty[partyIndex], MON_DATA_ATK_IV, NULL);
+ gBattleMons[battlerId].defenseIV = GetMonData(&gPlayerParty[partyIndex], MON_DATA_DEF_IV, NULL);
+ gBattleMons[battlerId].speedIV = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPEED_IV, NULL);
+ gBattleMons[battlerId].spAttackIV = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPATK_IV, NULL);
+ gBattleMons[battlerId].spDefenseIV = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPDEF_IV, NULL);
+ gBattleMons[battlerId].personality = GetMonData(&gPlayerParty[partyIndex], MON_DATA_PERSONALITY, NULL);
+ gBattleMons[battlerId].status1 = GetMonData(&gPlayerParty[partyIndex], MON_DATA_STATUS, NULL);
+ gBattleMons[battlerId].level = GetMonData(&gPlayerParty[partyIndex], MON_DATA_LEVEL, NULL);
+ gBattleMons[battlerId].hp = GetMonData(&gPlayerParty[partyIndex], MON_DATA_HP, NULL);
+ gBattleMons[battlerId].maxHP = GetMonData(&gPlayerParty[partyIndex], MON_DATA_MAX_HP, NULL);
+ gBattleMons[battlerId].attack = GetMonData(&gPlayerParty[partyIndex], MON_DATA_ATK, NULL);
+ gBattleMons[battlerId].defense = GetMonData(&gPlayerParty[partyIndex], MON_DATA_DEF, NULL);
+ gBattleMons[battlerId].speed = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPEED, NULL);
+ gBattleMons[battlerId].spAttack = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPATK, NULL);
+ gBattleMons[battlerId].spDefense = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPDEF, NULL);
+ gBattleMons[battlerId].isEgg = GetMonData(&gPlayerParty[partyIndex], MON_DATA_IS_EGG, NULL);
+ gBattleMons[battlerId].altAbility = GetMonData(&gPlayerParty[partyIndex], MON_DATA_ALT_ABILITY, NULL);
+ gBattleMons[battlerId].otId = GetMonData(&gPlayerParty[partyIndex], MON_DATA_OT_ID, NULL);
+ gBattleMons[battlerId].type1 = gBaseStats[gBattleMons[battlerId].species].type1;
+ gBattleMons[battlerId].type2 = gBaseStats[gBattleMons[battlerId].species].type2;
+ gBattleMons[battlerId].ability = GetAbilityBySpecies(gBattleMons[battlerId].species, gBattleMons[battlerId].altAbility);
+ GetMonData(&gPlayerParty[partyIndex], MON_DATA_NICKNAME, nickname);
+ StringCopy10(gBattleMons[battlerId].nickname, nickname);
+ GetMonData(&gPlayerParty[partyIndex], MON_DATA_OT_NAME, gBattleMons[battlerId].otName);
+
+ hpSwitchout = &gBattleStruct->hpOnSwitchout[GetBattlerSide(battlerId)];
+ *hpSwitchout = gBattleMons[battlerId].hp;
+
+ for (i = 0; i < 8; i++)
+ gBattleMons[battlerId].statStages[i] = 6;
+
+ gBattleMons[battlerId].status2 = 0;
+ sub_80174B8(battlerId);
+ ClearTemporarySpeciesSpriteData(battlerId, FALSE);
+}
+
+bool8 ExecuteTableBasedItemEffect(struct Pokemon *mon, u16 item, u8 partyIndex, u8 moveIndex)
+{
+ return PokemonUseItemEffects(mon, item, partyIndex, moveIndex, 0);
+}
+
+extern const u8 gUnknown_825DEA1[];
+extern const u8 gUnknown_825DEA9[];
+extern const u8 sGetMonDataEVConstants[];
+
+bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 moveIndex, u8 e)
+{
+ u32 data;
+ s32 friendship;
+ s32 cmdIndex;
+ bool8 retVal = TRUE;
+ const u8 *itemEffect;
+ u8 sp24 = 6;
+ u32 sp28;
+ s8 sp2C = 0;
+ u8 holdEffect;
+ u8 sp34 = 4;
+ u16 heldItem;
+ u8 r10;
+ u32 r4;
+
+ heldItem = GetMonData(pkmn, MON_DATA_HELD_ITEM, NULL);
+ if (heldItem == ITEM_ENIGMA_BERRY)
+ {
+ if (gMain.inBattle)
+ holdEffect = gEnigmaBerries[gBattlerInMenuId].holdEffect;
+ else
+ holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
+ }
+ else
+ {
+ holdEffect = ItemId_GetHoldEffect(heldItem);
+ }
+
+ gPotentialItemEffectBattler = gBattlerInMenuId;
+ if (gMain.inBattle)
+ {
+ gActiveBattler = gBattlerInMenuId;
+ cmdIndex = (GetBattlerSide(gActiveBattler) != 0);
+ while (cmdIndex < gBattlersCount)
+ {
+ if (gBattlerPartyIndexes[cmdIndex] == partyIndex)
+ {
+ sp34 = cmdIndex;
+ break;
+ }
+ cmdIndex += 2;
+ }
+ }
+ else
+ {
+ gActiveBattler = 0;
+ sp34 = 4;
+ }
+
+ if (!IS_POKEMON_ITEM(item))
+ return TRUE;
+ if (gItemEffectTable[item - 13] == NULL && item != ITEM_ENIGMA_BERRY)
+ return TRUE;
+
+ if (item == ITEM_ENIGMA_BERRY)
+ {
+ if (gMain.inBattle)
+ itemEffect = gEnigmaBerries[gActiveBattler].itemEffect;
+ else
+ itemEffect = gSaveBlock1Ptr->enigmaBerry.itemEffect;
+ }
+ else
+ {
+ itemEffect = gItemEffectTable[item - 13];
+ }
+
+ for (cmdIndex = 0; cmdIndex < 6; cmdIndex++)
+ {
+ switch (cmdIndex)
+ {
+ // status healing effects
+ case 0:
+ if ((itemEffect[cmdIndex] & 0x80)
+ && gMain.inBattle && sp34 != 4 && (gBattleMons[sp34].status2 & STATUS2_INFATUATION))
+ {
+ gBattleMons[sp34].status2 &= ~STATUS2_INFATUATION;
+ retVal = FALSE;
+ }
+ if ((itemEffect[cmdIndex] & 0x30)
+ && !(gBattleMons[gActiveBattler].status2 & STATUS2_FOCUS_ENERGY))
+ {
+ gBattleMons[gActiveBattler].status2 |= STATUS2_FOCUS_ENERGY;
+ retVal = FALSE;
+ }
+ if ((itemEffect[cmdIndex] & 0xF)
+ && gBattleMons[gActiveBattler].statStages[STAT_STAGE_ATK] < 12)
+ {
+ gBattleMons[gActiveBattler].statStages[STAT_STAGE_ATK] += itemEffect[cmdIndex] & 0xF;
+ if (gBattleMons[gActiveBattler].statStages[STAT_STAGE_ATK] > 12)
+ gBattleMons[gActiveBattler].statStages[STAT_STAGE_ATK] = 12;
+ retVal = FALSE;
+ }
+ break;
+ // in-battle stat boosting effects?
+ case 1:
+ if ((itemEffect[cmdIndex] & 0xF0)
+ && gBattleMons[gActiveBattler].statStages[STAT_STAGE_DEF] < 12)
+ {
+ gBattleMons[gActiveBattler].statStages[STAT_STAGE_DEF] += (itemEffect[cmdIndex] & 0xF0) >> 4;
+ if (gBattleMons[gActiveBattler].statStages[STAT_STAGE_DEF] > 12)
+ gBattleMons[gActiveBattler].statStages[STAT_STAGE_DEF] = 12;
+ retVal = FALSE;
+ }
+ if ((itemEffect[cmdIndex] & 0xF)
+ && gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPEED] < 12)
+ {
+ gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPEED] += itemEffect[cmdIndex] & 0xF;
+ if (gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPEED] > 12)
+ gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPEED] = 12;
+ retVal = FALSE;
+ }
+ break;
+ // more stat boosting effects?
+ case 2:
+ if ((itemEffect[cmdIndex] & 0xF0)
+ && gBattleMons[gActiveBattler].statStages[STAT_STAGE_ACC] < 12)
+ {
+ gBattleMons[gActiveBattler].statStages[STAT_STAGE_ACC] += (itemEffect[cmdIndex] & 0xF0) >> 4;
+ if (gBattleMons[gActiveBattler].statStages[STAT_STAGE_ACC] > 12)
+ gBattleMons[gActiveBattler].statStages[STAT_STAGE_ACC] = 12;
+ retVal = FALSE;
+ }
+ if ((itemEffect[cmdIndex] & 0xF)
+ && gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPATK] < 12)
+ {
+ gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPATK] += itemEffect[cmdIndex] & 0xF;
+ if (gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPATK] > 12)
+ gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPATK] = 12;
+ retVal = FALSE;
+ }
+ break;
+ case 3:
+ if ((itemEffect[cmdIndex] & 0x80)
+ && gSideTimers[GetBattlerSide(gActiveBattler)].mistTimer == 0)
+ {
+ gSideTimers[GetBattlerSide(gActiveBattler)].mistTimer = 5;
+ retVal = FALSE;
+ }
+ if ((itemEffect[cmdIndex] & 0x40) // raise level
+ && GetMonData(pkmn, MON_DATA_LEVEL, NULL) != 100)
+ {
+ data = gExperienceTables[gBaseStats[GetMonData(pkmn, MON_DATA_SPECIES, NULL)].growthRate][GetMonData(pkmn, MON_DATA_LEVEL, NULL) + 1];
+ SetMonData(pkmn, MON_DATA_EXP, &data);
+ CalculateMonStats(pkmn);
+ retVal = FALSE;
+ }
+ if ((itemEffect[cmdIndex] & 0x20)
+ && HealStatusConditions(pkmn, partyIndex, 7, sp34) == 0)
+ {
+ if (sp34 != 4)
+ gBattleMons[sp34].status2 &= ~STATUS2_NIGHTMARE;
+ retVal = FALSE;
+ }
+ if ((itemEffect[cmdIndex] & 0x10) && HealStatusConditions(pkmn, partyIndex, 0xF88, sp34) == 0)
+ retVal = FALSE;
+ if ((itemEffect[cmdIndex] & 8) && HealStatusConditions(pkmn, partyIndex, 16, sp34) == 0)
+ retVal = FALSE;
+ if ((itemEffect[cmdIndex] & 4) && HealStatusConditions(pkmn, partyIndex, 32, sp34) == 0)
+ retVal = FALSE;
+ if ((itemEffect[cmdIndex] & 2) && HealStatusConditions(pkmn, partyIndex, 64, sp34) == 0)
+ retVal = FALSE;
+ if ((itemEffect[cmdIndex] & 1) // heal confusion
+ && gMain.inBattle && sp34 != 4 && (gBattleMons[sp34].status2 & STATUS2_CONFUSION))
+ {
+ gBattleMons[sp34].status2 &= ~STATUS2_CONFUSION;
+ retVal = FALSE;
+ }
+ break;
+ // EV, HP, and PP raising effects
+ case 4:
+ r10 = itemEffect[cmdIndex];
+ if (r10 & 0x20)
+ {
+ r10 &= ~0x20;
+ data = (GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL) & gUnknown_825DEA1[moveIndex]) >> (moveIndex * 2);
+ sp28 = CalculatePPWithBonus(GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex);
+ if (data < 3 && sp28 > 4)
+ {
+ data = GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL) + gUnknown_825DEA9[moveIndex];
+ SetMonData(pkmn, MON_DATA_PP_BONUSES, &data);
+
+ data = CalculatePPWithBonus(GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL), data, moveIndex) - sp28;
+ data = GetMonData(pkmn, MON_DATA_PP1 + moveIndex, NULL) + data;
+ SetMonData(pkmn, MON_DATA_PP1 + moveIndex, &data);
+ retVal = FALSE;
+ }
+ }
+ sp28 = 0;
+ while (r10 != 0)
+ {
+ if (r10 & 1)
+ {
+ u16 evCount;
+ s32 r5;
+
+ switch (sp28)
+ {
+ case 0:
+ case 1:
+ evCount = GetMonEVCount(pkmn);
+ if (evCount >= 510)
+ return TRUE;
+ data = GetMonData(pkmn, sGetMonDataEVConstants[sp28], NULL);
+ if (data < 100)
+ {
+ if (data + itemEffect[sp24] > 100)
+ r4 = 100 - (data + itemEffect[sp24]) + itemEffect[sp24];
+ else
+ r4 = itemEffect[sp24];
+ if (evCount + r4 > 510)
+ r4 += 510 - (evCount + r4);
+ data += r4;
+ SetMonData(pkmn, sGetMonDataEVConstants[sp28], &data);
+ CalculateMonStats(pkmn);
+ sp24++;
+ retVal = FALSE;
+ }
+ break;
+ case 2:
+ // revive?
+ if (r10 & 0x10)
+ {
+ if (GetMonData(pkmn, MON_DATA_HP, NULL) != 0)
+ {
+ sp24++;
+ break;
+ }
+ if (gMain.inBattle)
+ {
+ if (sp34 != 4)
+ {
+ gAbsentBattlerFlags &= ~gBitTable[sp34];
+ CopyPlayerPartyMonToBattleData(sp34, pokemon_order_func(gBattlerPartyIndexes[sp34]));
+ if (GetBattlerSide(gActiveBattler) == 0 && gBattleResults.unk4 < 255)
+ gBattleResults.unk4++;
+ }
+ else
+ {
+ gAbsentBattlerFlags &= ~gBitTable[gActiveBattler ^ 2];
+ if (GetBattlerSide(gActiveBattler) == 0 && gBattleResults.unk4 < 255)
+ gBattleResults.unk4++;
+ }
+ }
+ }
+ else
+ {
+ if (GetMonData(pkmn, MON_DATA_HP, NULL) == 0)
+ {
+ sp24++;
+ break;
+ }
+ }
+ data = itemEffect[sp24++];
+ switch (data)
+ {
+ case 0xFF:
+ data = GetMonData(pkmn, MON_DATA_MAX_HP, NULL) - GetMonData(pkmn, MON_DATA_HP, NULL);
+ break;
+ case 0xFE:
+ data = GetMonData(pkmn, MON_DATA_MAX_HP, NULL) / 2;
+ if (data == 0)
+ data = 1;
+ break;
+ case 0xFD:
+ data = gBattleScripting.field_23;
+ break;
+ }
+ if (GetMonData(pkmn, MON_DATA_MAX_HP, NULL) != GetMonData(pkmn, MON_DATA_HP, NULL))
+ {
+ if (e == 0)
+ {
+ data = GetMonData(pkmn, MON_DATA_HP, NULL) + data;
+ if (data > GetMonData(pkmn, MON_DATA_MAX_HP, NULL))
+ data = GetMonData(pkmn, MON_DATA_MAX_HP, NULL);
+ SetMonData(pkmn, MON_DATA_HP, &data);
+ if (gMain.inBattle && sp34 != 4)
+ {
+ gBattleMons[sp34].hp = data;
+ if (!(r10 & 0x10) && GetBattlerSide(gActiveBattler) == 0)
+ {
+ if (gBattleResults.unk3 < 255)
+ gBattleResults.unk3++;
+ // I have to re-use this variable to match.
+ r5 = gActiveBattler;
+ gActiveBattler = sp34;
+ BtlController_EmitGetMonData(0, 0, 0);
+ MarkBufferBankForExecution(gActiveBattler);
+ gActiveBattler = r5;
+ }
+ }
+ }
+ else
+ {
+ gBattleMoveDamage = -data;
+ }
+ retVal = FALSE;
+ }
+ r10 &= 0xEF;
+ break;
+ case 3:
+ if (!(r10 & 2))
+ {
+ for (r5 = 0; r5 < 4; r5++)
+ {
+ u16 r4;
+
+ data = GetMonData(pkmn, MON_DATA_PP1 + r5, NULL);
+ r4 = GetMonData(pkmn, MON_DATA_MOVE1 + r5, NULL);
+ if (data != CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), r5))
+ {
+ data += itemEffect[sp24];
+ r4 = GetMonData(pkmn, MON_DATA_MOVE1 + r5, NULL);
+ if (data > CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), r5))
+ {
+ r4 = GetMonData(pkmn, MON_DATA_MOVE1 + r5, NULL);
+ data = CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), r5);
+ }
+ SetMonData(pkmn, MON_DATA_PP1 + r5, &data);
+ if (gMain.inBattle
+ && sp34 != 4 && !(gBattleMons[sp34].status2 & 0x200000)
+ && !(gDisableStructs[sp34].unk18_b & gBitTable[r5]))
+ gBattleMons[sp34].pp[r5] = data;
+ retVal = FALSE;
+ }
+ }
+ sp24++;
+ }
+ else
+ {
+ u16 r4;
+
+ data = GetMonData(pkmn, MON_DATA_PP1 + moveIndex, NULL);
+ r4 = GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL);
+ if (data != CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex))
+ {
+ data += itemEffect[sp24++];
+ r4 = GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL);
+ if (data > CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex))
+ {
+ r4 = GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL);
+ data = CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex);
+ }
+ SetMonData(pkmn, MON_DATA_PP1 + moveIndex, &data);
+ if (gMain.inBattle
+ && sp34 != 4 && !(gBattleMons[sp34].status2 & 0x200000)
+ && !(gDisableStructs[sp34].unk18_b & gBitTable[moveIndex]))
+ gBattleMons[sp34].pp[moveIndex] = data;
+ retVal = FALSE;
+ }
+ }
+ break;
+ case 7:
+ {
+ u16 targetSpecies = GetEvolutionTargetSpecies(pkmn, 2, item);
+
+ if (targetSpecies != SPECIES_NONE)
+ {
+ BeginEvolutionScene(pkmn, targetSpecies, 0, partyIndex);
+ return FALSE;
+ }
+ }
+ break;
+ }
+ }
+ sp28++;
+ r10 >>= 1;
+ }
+ break;
+ case 5:
+ r10 = itemEffect[cmdIndex];
+ sp28 = 0;
+ while (r10 != 0)
+ {
+ if (r10 & 1)
+ {
+ u16 evCount;
+
+ switch (sp28)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ evCount = GetMonEVCount(pkmn);
+ if (evCount >= 510)
+ return TRUE;
+ data = GetMonData(pkmn, sGetMonDataEVConstants[sp28 + 2], NULL);
+ if (data < 100)
+ {
+ if (data + itemEffect[sp24] > 100)
+ r4 = 100 - (data + itemEffect[sp24]) + itemEffect[sp24];
+ else
+ r4 = itemEffect[sp24];
+ if (evCount + r4 > 510)
+ r4 += 510 - (evCount + r4);
+ data += r4;
+ SetMonData(pkmn, sGetMonDataEVConstants[sp28 + 2], &data);
+ CalculateMonStats(pkmn);
+ retVal = FALSE;
+ sp24++;
+ }
+ break;
+ case 4:
+ data = (GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL) & gUnknown_825DEA1[moveIndex]) >> (moveIndex * 2);
+ if (data < 3)
+ {
+ r4 = CalculatePPWithBonus(GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex);
+ data = GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL);
+ data &= gPPUpWriteMasks[moveIndex];
+ data += gUnknown_825DEA9[moveIndex] * 3;
+
+ SetMonData(pkmn, MON_DATA_PP_BONUSES, &data);
+ data = CalculatePPWithBonus(GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL), data, moveIndex) - r4;
+ data = GetMonData(pkmn, MON_DATA_PP1 + moveIndex, NULL) + data;
+ SetMonData(pkmn, MON_DATA_PP1 + moveIndex, &data);
+ retVal = FALSE;
+ }
+ break;
+ case 5:
+ if (GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL) < 100 && retVal == 0 && sp2C == 0)
+ {
+ sp2C = itemEffect[sp24];
+ friendship = GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL);
+ if (sp2C > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
+ friendship += 150 * sp2C / 100;
+ else
+ friendship += sp2C;
+ if (sp2C > 0)
+ {
+ if (GetMonData(pkmn, MON_DATA_POKEBALL, NULL) == 11)
+ friendship++;
+ if (GetMonData(pkmn, MON_DATA_MET_LOCATION, NULL) == sav1_map_get_name())
+ friendship++;
+ }
+ if (friendship < 0)
+ friendship = 0;
+ if (friendship > 255)
+ friendship = 255;
+ SetMonData(pkmn, MON_DATA_FRIENDSHIP, &friendship);
+ }
+ sp24++;
+ break;
+ case 6:
+ if (GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL) >= 100 && GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL) < 200
+ && retVal == 0 && sp2C == 0)
+ {
+ sp2C = itemEffect[sp24];
+ friendship = GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL);
+ if (sp2C > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
+ friendship += 150 * sp2C / 100;
+ else
+ friendship += sp2C;
+ if (sp2C > 0)
+ {
+ if (GetMonData(pkmn, MON_DATA_POKEBALL, NULL) == 11)
+ friendship++;
+ if (GetMonData(pkmn, MON_DATA_MET_LOCATION, NULL) == sav1_map_get_name())
+ friendship++;
+ }
+ if (friendship < 0)
+ friendship = 0;
+ if (friendship > 255)
+ friendship = 255;
+ SetMonData(pkmn, MON_DATA_FRIENDSHIP, &friendship);
+ }
+ sp24++;
+ break;
+ case 7:
+ if (GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL) >= 200 && retVal == 0 && sp2C == 0)
+ {
+ sp2C = itemEffect[sp24];
+ friendship = GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL);
+ if (sp2C > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
+ friendship += 150 * sp2C / 100;
+ else
+ friendship += sp2C;
+ if (sp2C > 0)
+ {
+ if (GetMonData(pkmn, MON_DATA_POKEBALL, NULL) == 11)
+ friendship++;
+ if (GetMonData(pkmn, MON_DATA_MET_LOCATION, NULL) == sav1_map_get_name())
+ friendship++;
+ }
+ if (friendship < 0)
+ friendship = 0;
+ if (friendship > 255)
+ friendship = 255;
+ SetMonData(pkmn, MON_DATA_FRIENDSHIP, &friendship);
+ }
+ sp24++;
+ break;
+ }
+ }
+ sp28++;
+ r10 >>= 1;
+ }
+ break;
+ }
+ }
+ return retVal;
+}
+
+bool8 HealStatusConditions(struct Pokemon *mon, u32 unused, u32 healMask, u8 battleId)
+{
+ u32 status = GetMonData(mon, MON_DATA_STATUS, 0);
+
+ if (status & healMask)
+ {
+ status &= ~healMask;
+ SetMonData(mon, MON_DATA_STATUS, &status);
+ if (gMain.inBattle && battleId != 4)
+ gBattleMons[battleId].status1 &= ~healMask;
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+extern bool8 sub_8042BE8(struct Pokemon *mon, u32 unused, u32 healMask, u8 battleId);
+
+#ifdef NONMATCHING
+/*
+ * This is nonmatching due to the compiler's insistence on avoiding the u8 cast
+ * when loading gMain.inBattle. If it weren't for this absent cast, differing
+ * the function would be a lot easier.
+ */
+bool8 PokemonUseItemEffects2(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 moveIndex, u8 e)
+{ // BEGIN
+ u32 data;
+ s32 cmdIndex;
+ bool8 retVal = TRUE;
+ const u8 *itemEffect;
+ u8 sp24 = 6;
+ u32 sp28;
+ s8 sp2C = 0;
+ u8 sp34 = 4;
+ u16 heldItem;
+ u8 r10;
+ s32 r4;
+
+ heldItem = GetMonData(pkmn, MON_DATA_HELD_ITEM, NULL);
+ if (heldItem == ITEM_ENIGMA_BERRY)
+ {
+ if (gMain.inBattle)
+ /*holdEffect = */gEnigmaBerries[gBattlerInMenuId].holdEffect;
+ else
+ /*holdEffect = */gSaveBlock1Ptr->enigmaBerry.holdEffect;
+ }
+ else
+ {
+ /*holdEffect = */ItemId_GetHoldEffect(heldItem);
+ }
+
+ gPotentialItemEffectBattler = gBattlerInMenuId;
+
+ // grr. the original asm also u8 masks after loading the bitmask, despite
+ // the fact that is a useless operation. what's going on here? Something
+ // dumb I bet like dead code.
+ if (gMain.inBattle)
+ {
+ gActiveBattler = gBattlerInMenuId;
+ if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
+ cmdIndex = 0;
+ else
+ cmdIndex = 1;
+ while (cmdIndex < gBattlersCount)
+ {
+ if (gBattlerPartyIndexes[cmdIndex] == partyIndex)
+ {
+ sp34 = cmdIndex;
+ break;
+ }
+ cmdIndex += 2;
+ }
+ }
+ else
+ {
+ gActiveBattler = 0;
+ sp34 = 4;
+ }
+
+ // _08042504
+ if (!IS_POKEMON_ITEM(item))
+ return TRUE;
+ if (gItemEffectTable[item - 13] == NULL && item != ITEM_ENIGMA_BERRY)
+ return TRUE;
+
+ if (item == ITEM_ENIGMA_BERRY)
+ {
+ if (gMain.inBattle)
+ itemEffect = gEnigmaBerries[gActiveBattler].itemEffect;
+ else
+ itemEffect = gSaveBlock1Ptr->enigmaBerry.itemEffect;
+ }
+ else
+ {
+ itemEffect = gItemEffectTable[item - 13];
+ }
+
+ for (cmdIndex = 0; cmdIndex < 6; cmdIndex++)
+ {
+ switch (cmdIndex)
+ {
+ // status healing effects
+ case 0:
+ if ((itemEffect[cmdIndex] & 0x80)
+ && gMain.inBattle && sp34 != 4 && (gBattleMons[sp34].status2 & STATUS2_INFATUATION))
+ {
+ //gBattleMons[sp34].status2 &= ~STATUS2_INFATUATION;
+ retVal = FALSE;
+ }
+ if ((itemEffect[cmdIndex] & 0x30)
+ && !(gBattleMons[gActiveBattler].status2 & STATUS2_FOCUS_ENERGY))
+ {
+ //gBattleMons[gActiveBattler].status2 |= STATUS2_FOCUS_ENERGY;
+ retVal = FALSE;
+ }
+ if ((itemEffect[cmdIndex] & 0xF)
+ && gBattleMons[gActiveBattler].statStages[STAT_STAGE_ATK] < 12)
+ {
+ //gBattleMons[gActiveBattler].statStages[STAT_STAGE_ATK] += itemEffect[cmdIndex] & 0xF;
+ //if (gBattleMons[gActiveBattler].statStages[STAT_STAGE_ATK] > 12)
+ // gBattleMons[gActiveBattler].statStages[STAT_STAGE_ATK] = 12;
+ retVal = FALSE;
+ }
+ break;
+ // in-battle stat boosting effects?
+ case 1:
+ if ((itemEffect[cmdIndex] & 0xF0)
+ && gBattleMons[gActiveBattler].statStages[STAT_STAGE_DEF] < 12)
+ {
+ //gBattleMons[gActiveBattler].statStages[STAT_STAGE_DEF] += (itemEffect[cmdIndex] & 0xF0) >> 4;
+ //if (gBattleMons[gActiveBattler].statStages[STAT_STAGE_DEF] > 12)
+ // gBattleMons[gActiveBattler].statStages[STAT_STAGE_DEF] = 12;
+ retVal = FALSE;
+ }
+ if ((itemEffect[cmdIndex] & 0xF)
+ && gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPEED] < 12)
+ {
+ //gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPEED] += itemEffect[cmdIndex] & 0xF;
+ //if (gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPEED] > 12)
+ // gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPEED] = 12;
+ retVal = FALSE;
+ }
+ break;
+ // more stat boosting effects?
+ case 2:
+ if ((itemEffect[cmdIndex] & 0xF0)
+ && gBattleMons[gActiveBattler].statStages[STAT_STAGE_ACC] < 12)
+ {
+ //gBattleMons[gActiveBattler].statStages[STAT_STAGE_ACC] += (itemEffect[cmdIndex] & 0xF0) >> 4;
+ //if (gBattleMons[gActiveBattler].statStages[STAT_STAGE_ACC] > 12)
+ // gBattleMons[gActiveBattler].statStages[STAT_STAGE_ACC] = 12;
+ retVal = FALSE;
+ }
+ if ((itemEffect[cmdIndex] & 0xF)
+ && gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPATK] < 12)
+ {
+ //gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPATK] += itemEffect[cmdIndex] & 0xF;
+ //if (gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPATK] > 12)
+ // gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPATK] = 12;
+ retVal = FALSE;
+ }
+ break;
+ case 3:
+ if ((itemEffect[cmdIndex] & 0x80)
+ && gSideTimers[GetBattlerSide(gActiveBattler)].mistTimer == 0)
+ {
+ //gSideTimers[GetBattlerSide(gActiveBattler)].mistTimer = 5;
+ retVal = FALSE;
+ }
+ if ((itemEffect[cmdIndex] & 0x40) // raise level
+ && GetMonData(pkmn, MON_DATA_LEVEL, NULL) != 100)
+ {
+ //data = gExperienceTables[gBaseStats[GetMonData(pkmn, MON_DATA_SPECIES, NULL)].growthRate][GetMonData(pkmn, MON_DATA_LEVEL, NULL) + 1];
+ //SetMonData(pkmn, MON_DATA_EXP, &data);
+ //CalculateMonStats(pkmn);
+ retVal = FALSE;
+ }
+ if ((itemEffect[cmdIndex] & 0x20)
+ && sub_8042BE8(pkmn, partyIndex, 7, sp34) == 0)
+ {
+ //if (sp34 != 4)
+ // gBattleMons[sp34].status2 &= ~STATUS2_NIGHTMARE;
+ retVal = FALSE;
+ }
+ if ((itemEffect[cmdIndex] & 0x10) && sub_8042BE8(pkmn, partyIndex, 0xF88, sp34) == 0)
+ retVal = FALSE;
+ if ((itemEffect[cmdIndex] & 8) && sub_8042BE8(pkmn, partyIndex, 16, sp34) == 0)
+ retVal = FALSE;
+ if ((itemEffect[cmdIndex] & 4) && sub_8042BE8(pkmn, partyIndex, 32, sp34) == 0)
+ retVal = FALSE;
+ if ((itemEffect[cmdIndex] & 2) && sub_8042BE8(pkmn, partyIndex, 64, sp34) == 0)
+ retVal = FALSE;
+ if ((itemEffect[cmdIndex] & 1) // heal confusion
+ && gMain.inBattle && sp34 != 4 && (gBattleMons[sp34].status2 & STATUS2_CONFUSION))
+ {
+ //gBattleMons[sp34].status2 &= ~STATUS2_CONFUSION;
+ retVal = FALSE;
+ }
+ break;
+ // EV, HP, and PP raising effects
+ case 4:
+ r10 = itemEffect[cmdIndex];
+ if (r10 & 0x20)
+ {
+ r10 &= ~0x20;
+ data = (GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL) & gUnknown_825DEA1[moveIndex]) >> (moveIndex * 2);
+ sp28 = CalculatePPWithBonus(GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex);
+ if (data < 3 && sp28 > 4)
+ {
+ //data = GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL) + gUnknown_825DEA9[moveIndex];
+ //SetMonData(pkmn, MON_DATA_PP_BONUSES, &data);
+ //
+ //data = CalculatePPWithBonus(GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL), data, moveIndex) - sp28;
+ //data = GetMonData(pkmn, MON_DATA_PP1 + moveIndex, NULL) + data;
+ //SetMonData(pkmn, MON_DATA_PP1 + moveIndex, &data);
+ retVal = FALSE;
+ }
+ }
+ sp28 = 0;
+ while (r10 != 0) // _080428C0
+ {
+ if (r10 & 1)
+ {
+ u16 evCount;
+ u16 targetSpecies;
+ s32 r5;
+
+ switch (sp28)
+ {
+ case 0:
+ case 1:
+ evCount = GetMonEVCount(pkmn);
+ if (evCount >= 510)
+ return TRUE;
+ data = GetMonData(pkmn, sGetMonDataEVConstants[sp28], NULL);
+ if (data < 100)
+ {
+ //if (data + itemEffect[sp24] > 100)
+ // r4 = 100 - (data + itemEffect[sp24]) + itemEffect[sp24];
+ //else
+ // r4 = itemEffect[sp24];
+ //if (evCount + r4 > 510)
+ // r4 += 510 - (evCount + r4);
+ //data += r4;
+ //SetMonData(pkmn, sGetMonDataEVConstants[sp28], &data);
+ //CalculateMonStats(pkmn);
+ sp24++;
+ retVal = FALSE;
+ }
+ break;
+ case 2:
+ // revive?
+ if (r10 & 0x10)
+ {
+ if (GetMonData(pkmn, MON_DATA_HP, NULL) != 0)
+ {
+ sp24++;
+ break;
+ }
+ /*
+ if (gMain.inBattle)
+ {
+ if (sp34 != 4)
+ {
+ gAbsentBattlerFlags &= ~gBitTable[sp34];
+ CopyPlayerPartyMonToBattleData(sp34, pokemon_order_func(gBattlerPartyIndexes[sp34]));
+ if (GetBattlerSide(gActiveBattler) == 0 && gBattleResults.unk4 < 255)
+ gBattleResults.unk4++;
+ }
+ else
+ {
+ gAbsentBattlerFlags &= ~gBitTable[gActiveBattler ^ 2];
+ if (GetBattlerSide(gActiveBattler) == 0 && gBattleResults.unk4 < 255)
+ gBattleResults.unk4++;
+ }
+ }
+ */
+ }
+ else
+ {
+ if (GetMonData(pkmn, MON_DATA_HP, NULL) == 0)
+ {
+ sp24++;
+ break;
+ }
+ }
+ /*
+ data = itemEffect[sp24++];
+ switch (data)
+ {
+ case 0xFF:
+ data = GetMonData(pkmn, MON_DATA_MAX_HP, NULL) - GetMonData(pkmn, MON_DATA_HP, NULL);
+ break;
+ case 0xFE:
+ data = GetMonData(pkmn, MON_DATA_MAX_HP, NULL) / 2;
+ if (data == 0)
+ data = 1;
+ break;
+ case 0xFD:
+ data = gBattleScripting.field_23;
+ break;
+ }
+ */
+ if (GetMonData(pkmn, MON_DATA_MAX_HP, NULL) != GetMonData(pkmn, MON_DATA_HP, NULL))
+ {
+ /*
+ if (e == 0)
+ {
+ data = GetMonData(pkmn, MON_DATA_HP, NULL) + data;
+ if (data > GetMonData(pkmn, MON_DATA_MAX_HP, NULL))
+ data = GetMonData(pkmn, MON_DATA_MAX_HP, NULL);
+ SetMonData(pkmn, MON_DATA_HP, &data);
+ if (gMain.inBattle && sp34 != 4)
+ {
+ gBattleMons[sp34].hp = data;
+ if (!(r10 & 0x10) && GetBattlerSide(gActiveBattler) == 0)
+ {
+ if (gBattleResults.unk3 < 255)
+ gBattleResults.unk3++;
+ // I have to re-use this variable to match.
+ r5 = gActiveBattler;
+ gActiveBattler = sp34;
+ BtlController_EmitGetMonData(0, 0, 0);
+ MarkBufferBankForExecution(gActiveBattler);
+ gActiveBattler = r5;
+ }
+ }
+ }
+ else
+ {
+ gBattleMoveDamage = -data;
+ }
+ */
+ retVal = FALSE;
+ }
+ sp24++;
+ r10 &= 0xEF;
+ break;
+ case 3:
+ if (!(r10 & 2))
+ {
+ for (r5 = 0; r5 < 4; r5++)
+ {
+ data = GetMonData(pkmn, MON_DATA_PP1 + r5, NULL);
+ r4 = GetMonData(pkmn, MON_DATA_MOVE1 + r5, NULL);
+ if (data != CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), r5))
+ {
+ /*
+ data += itemEffect[sp24];
+ r4 = GetMonData(pkmn, MON_DATA_MOVE1 + r5, NULL);
+ if (data > CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), r5))
+ {
+ r4 = GetMonData(pkmn, MON_DATA_MOVE1 + r5, NULL);
+ data = CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), r5);
+ }
+ SetMonData(pkmn, MON_DATA_PP1 + r5, &data);
+ if (gMain.inBattle
+ && sp34 != 4 && !(gBattleMons[sp34].status2 & 0x200000)
+ && !(gDisableStructs[sp34].unk18_b & gBitTable[r5]))
+ gBattleMons[sp34].pp[r5] = data;
+ */
+ retVal = FALSE;
+ }
+ }
+ }
+ else // _080429FA
+ {
+ data = GetMonData(pkmn, MON_DATA_PP1 + moveIndex, NULL);
+ r4 = GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL);
+ if (data != CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex))
+ {
+ /*
+ data += itemEffect[sp24++];
+ r4 = GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL);
+ if (data > CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex))
+ {
+ r4 = GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL);
+ data = CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex);
+ }
+ SetMonData(pkmn, MON_DATA_PP1 + moveIndex, &data);
+ if (gMain.inBattle
+ && sp34 != 4 && !(gBattleMons[sp34].status2 & 0x200000)
+ && !(gDisableStructs[sp34].unk18_b & gBitTable[moveIndex]))
+ gBattleMons[sp34].pp[moveIndex] = data;
+ */
+ sp24++;
+ retVal = FALSE;
+ }
+ }
+ break;
+ case 7:
+ {
+ targetSpecies = GetEvolutionTargetSpecies(pkmn, 2, item);
+
+ if (targetSpecies != SPECIES_NONE)
+ {
+ //BeginEvolutionScene(pkmn, targetSpecies, 0, partyIndex);
+ return FALSE;
+ }
+ }
+ break;
+ }
+ }
+ sp28++;
+ r10 >>= 1;
+ }
+ break;
+ case 5:
+ r10 = itemEffect[cmdIndex];
+ sp28 = 0;
+ while (r10 != 0)
+ {
+ if (r10 & 1)
+ {
+ u16 evCount;
+
+ switch (sp28)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ evCount = GetMonEVCount(pkmn);
+ if (evCount >= 510)
+ return TRUE;
+ data = GetMonData(pkmn, sGetMonDataEVConstants[sp28 + 2], NULL);
+ if (data < 100)
+ {
+ /*
+ if (data + itemEffect[sp24] > 100)
+ r4 = 100 - (data + itemEffect[sp24]) + itemEffect[sp24];
+ else
+ r4 = itemEffect[sp24];
+ if (evCount + r4 > 510)
+ r4 += 510 - (evCount + r4);
+ data += r4;
+ SetMonData(pkmn, sGetMonDataEVConstants[sp28 + 2], &data);
+ CalculateMonStats(pkmn);
+ */
+ retVal = FALSE;
+ sp24++;
+ }
+ break;
+ case 4:
+ data = (GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL) & gUnknown_825DEA1[moveIndex]) >> (moveIndex * 2);
+ r4 = CalculatePPWithBonus(GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex);
+ if (data < 3)
+ {
+ if(r4 <= 4)
+ break;
+ /*
+
+ data = GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL);
+ data &= gPPUpWriteMasks[moveIndex];
+ data += gUnknown_825DEA9[moveIndex] * 3;
+
+ SetMonData(pkmn, MON_DATA_PP_BONUSES, &data);
+ data = CalculatePPWithBonus(GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL), data, moveIndex) - r4;
+ data = GetMonData(pkmn, MON_DATA_PP1 + moveIndex, NULL) + data;
+ SetMonData(pkmn, MON_DATA_PP1 + moveIndex, &data);
+ */
+ retVal = FALSE;
+ }
+ break;
+ case 5:
+ if (GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL) < 100 && retVal == 0 && sp2C == 0)
+ {
+ sp2C = itemEffect[sp24];
+ /*
+ friendship = GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL);
+ if (sp2C > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
+ friendship += 150 * sp2C / 100;
+ else
+ friendship += sp2C;
+ if (sp2C > 0)
+ {
+ if (GetMonData(pkmn, MON_DATA_POKEBALL, NULL) == 11)
+ friendship++;
+ if (GetMonData(pkmn, MON_DATA_MET_LOCATION, NULL) == sav1_map_get_name())
+ friendship++;
+ }
+ if (friendship < 0)
+ friendship = 0;
+ if (friendship > 255)
+ friendship = 255;
+ SetMonData(pkmn, MON_DATA_FRIENDSHIP, &friendship);
+ */
+ }
+ sp24++;
+ break;
+ case 6:
+ if (GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL) >= 100 && GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL) < 200
+ && retVal == 0 && sp2C == 0)
+ {
+ sp2C = itemEffect[sp24];
+ /*
+ friendship = GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL);
+ if (sp2C > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
+ friendship += 150 * sp2C / 100;
+ else
+ friendship += sp2C;
+ if (sp2C > 0)
+ {
+ if (GetMonData(pkmn, MON_DATA_POKEBALL, NULL) == 11)
+ friendship++;
+ if (GetMonData(pkmn, MON_DATA_MET_LOCATION, NULL) == sav1_map_get_name())
+ friendship++;
+ }
+ if (friendship < 0)
+ friendship = 0;
+ if (friendship > 255)
+ friendship = 255;
+ SetMonData(pkmn, MON_DATA_FRIENDSHIP, &friendship);
+ */
+ }
+ sp24++;
+ break;
+ case 7:
+ if (GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL) >= 200 && retVal == 0 && sp2C == 0)
+ {
+ sp2C = itemEffect[sp24];
+ /*
+ friendship = GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL);
+ if (sp2C > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
+ friendship += 150 * sp2C / 100;
+ else
+ friendship += sp2C;
+ if (sp2C > 0)
+ {
+ if (GetMonData(pkmn, MON_DATA_POKEBALL, NULL) == 11)
+ friendship++;
+ if (GetMonData(pkmn, MON_DATA_MET_LOCATION, NULL) == sav1_map_get_name())
+ friendship++;
+ }
+ if (friendship < 0)
+ friendship = 0;
+ if (friendship > 255)
+ friendship = 255;
+ SetMonData(pkmn, MON_DATA_FRIENDSHIP, &friendship);
+ */
+ }
+ sp24++;
+ break;
+ }
+ }
+ sp28++;
+ r10 >>= 1;
+ }
+ break;
+ }
+ }
+ return retVal;
+}
+#else
+__attribute__((naked))
+bool8 PokemonUseItemEffects2(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 moveIndex, u8 e)
+{
+ asm(".syntax unified\n\
+ push {r4-r7,lr}\n\
+ mov r7, r10\n\
+ mov r6, r9\n\
+ mov r5, r8\n\
+ push {r5-r7}\n\
+ sub sp, 0x20\n\
+ mov r8, r0\n\
+ lsls r1, 16\n\
+ lsrs r1, 16\n\
+ str r1, [sp]\n\
+ lsls r2, 24\n\
+ lsrs r2, 24\n\
+ str r2, [sp, 0x4]\n\
+ lsls r3, 24\n\
+ lsrs r3, 24\n\
+ str r3, [sp, 0x8]\n\
+ movs r0, 0x1\n\
+ str r0, [sp, 0x10]\n\
+ movs r1, 0x6\n\
+ mov r10, r1\n\
+ movs r2, 0\n\
+ str r2, [sp, 0x18]\n\
+ movs r0, 0x4\n\
+ str r0, [sp, 0x1C]\n\
+ mov r0, r8\n\
+ movs r1, 0xC\n\
+ bl GetMonData\n\
+ lsls r0, 16\n\
+ lsrs r0, 16\n\
+ cmp r0, 0xAF\n\
+ beq _08042458\n\
+ bl ItemId_GetHoldEffect\n\
+_08042458:\n\
+ ldr r1, _080424B0 @ =gPotentialItemEffectBattler\n\
+ ldr r0, _080424B4 @ =gBattlerInMenuId\n\
+ ldrb r2, [r0]\n\
+ strb r2, [r1]\n\
+ ldr r0, _080424B8 @ =gMain\n\
+ ldr r1, _080424BC @ =0x00000439\n\
+ adds r0, r1\n\
+ ldrb r1, [r0]\n\
+ movs r0, 0x2\n\
+ ands r0, r1\n\
+ lsls r0, 24\n\
+ lsrs r1, r0, 24\n\
+ cmp r1, 0\n\
+ beq _080424F8\n\
+ ldr r0, _080424C0 @ =gActiveBattler\n\
+ strb r2, [r0]\n\
+ ldrb r0, [r0]\n\
+ bl GetBattlerSide\n\
+ lsls r0, 24\n\
+ lsrs r0, 24\n\
+ negs r1, r0\n\
+ orrs r1, r0\n\
+ lsrs r1, 31\n\
+ str r1, [sp, 0xC]\n\
+ ldr r0, _080424C4 @ =gBattlersCount\n\
+ ldr r4, [sp]\n\
+ subs r4, 0xD\n\
+ ldrb r0, [r0]\n\
+ cmp r1, r0\n\
+ bge _08042504\n\
+ ldr r2, _080424C8 @ =gBattlerPartyIndexes\n\
+ lsls r0, r1, 1\n\
+ adds r0, r2\n\
+ ldrh r3, [r0]\n\
+ ldr r1, [sp, 0x4]\n\
+ lsls r0, r1, 16\n\
+ lsrs r1, r0, 16\n\
+ adds r5, r0, 0\n\
+ cmp r3, r1\n\
+ bne _080424CC\n\
+ ldr r2, [sp, 0xC]\n\
+ str r2, [sp, 0x1C]\n\
+ b _08042504\n\
+ .align 2, 0\n\
+_080424B0: .4byte gPotentialItemEffectBattler\n\
+_080424B4: .4byte gBattlerInMenuId\n\
+_080424B8: .4byte gMain\n\
+_080424BC: .4byte 0x00000439\n\
+_080424C0: .4byte gActiveBattler\n\
+_080424C4: .4byte gBattlersCount\n\
+_080424C8: .4byte gBattlerPartyIndexes\n\
+_080424CC:\n\
+ ldr r0, [sp, 0xC]\n\
+ adds r0, 0x2\n\
+ str r0, [sp, 0xC]\n\
+ ldr r0, _080424F4 @ =gBattlersCount\n\
+ ldr r1, [sp, 0xC]\n\
+ ldrb r0, [r0]\n\
+ cmp r1, r0\n\
+ bge _08042504\n\
+ lsls r0, r1, 1\n\
+ adds r0, r2\n\
+ ldrh r1, [r0]\n\
+ lsrs r0, r5, 16\n\
+ cmp r1, r0\n\
+ bne _080424CC\n\
+ ldr r2, [sp, 0xC]\n\
+ lsls r0, r2, 24\n\
+ lsrs r0, 24\n\
+ str r0, [sp, 0x1C]\n\
+ b _08042504\n\
+ .align 2, 0\n\
+_080424F4: .4byte gBattlersCount\n\
+_080424F8:\n\
+ ldr r0, _08042520 @ =gActiveBattler\n\
+ strb r1, [r0]\n\
+ movs r0, 0x4\n\
+ str r0, [sp, 0x1C]\n\
+ ldr r4, [sp]\n\
+ subs r4, 0xD\n\
+_08042504:\n\
+ lsls r0, r4, 16\n\
+ lsrs r0, 16\n\
+ cmp r0, 0xA5\n\
+ bhi _08042578\n\
+ ldr r1, _08042524 @ =gItemEffectTable\n\
+ lsls r0, r4, 2\n\
+ adds r0, r1\n\
+ ldr r0, [r0]\n\
+ cmp r0, 0\n\
+ bne _08042528\n\
+ ldr r1, [sp]\n\
+ cmp r1, 0xAF\n\
+ beq _0804252E\n\
+ b _08042578\n\
+ .align 2, 0\n\
+_08042520: .4byte gActiveBattler\n\
+_08042524: .4byte gItemEffectTable\n\
+_08042528:\n\
+ ldr r2, [sp]\n\
+ cmp r2, 0xAF\n\
+ bne _0804257C\n\
+_0804252E:\n\
+ ldr r0, _08042550 @ =gMain\n\
+ ldr r1, _08042554 @ =0x00000439\n\
+ adds r0, r1\n\
+ ldrb r1, [r0]\n\
+ movs r0, 0x2\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ beq _08042560\n\
+ ldr r0, _08042558 @ =gActiveBattler\n\
+ ldrb r1, [r0]\n\
+ lsls r0, r1, 3\n\
+ subs r0, r1\n\
+ lsls r0, 2\n\
+ ldr r1, _0804255C @ =gEnigmaBerries+0x8\n\
+ adds r0, r1\n\
+ b _0804257C\n\
+ .align 2, 0\n\
+_08042550: .4byte gMain\n\
+_08042554: .4byte 0x00000439\n\
+_08042558: .4byte gActiveBattler\n\
+_0804255C: .4byte gEnigmaBerries+0x8\n\
+_08042560:\n\
+ ldr r0, _0804256C @ =gSaveBlock1Ptr\n\
+ ldr r0, [r0]\n\
+ ldr r2, _08042570 @ =0x00003108\n\
+ adds r2, r0, r2\n\
+ str r2, [sp, 0x14]\n\
+ b _0804257E\n\
+ .align 2, 0\n\
+_0804256C: .4byte gSaveBlock1Ptr\n\
+_08042570: .4byte 0x00003108\n\
+_08042574:\n\
+ movs r0, 0\n\
+ b _08042BD8\n\
+_08042578:\n\
+ movs r0, 0x1\n\
+ b _08042BD8\n\
+_0804257C:\n\
+ str r0, [sp, 0x14]\n\
+_0804257E:\n\
+ movs r0, 0\n\
+ str r0, [sp, 0xC]\n\
+_08042582:\n\
+ ldr r1, [sp, 0xC]\n\
+ cmp r1, 0x5\n\
+ bls _0804258A\n\
+ b _08042BCA\n\
+_0804258A:\n\
+ lsls r0, r1, 2\n\
+ ldr r1, _08042594 @ =_08042598\n\
+ adds r0, r1\n\
+ ldr r0, [r0]\n\
+ mov pc, r0\n\
+ .align 2, 0\n\
+_08042594: .4byte _08042598\n\
+ .align 2, 0\n\
+_08042598:\n\
+ .4byte _080425B0\n\
+ .4byte _0804264C\n\
+ .4byte _080426A8\n\
+ .4byte _08042708\n\
+ .4byte _08042850\n\
+ .4byte _08042A6A\n\
+_080425B0:\n\
+ ldr r0, [sp, 0x14]\n\
+ ldr r1, [sp, 0xC]\n\
+ adds r2, r0, r1\n\
+ ldrb r1, [r2]\n\
+ movs r0, 0x80\n\
+ ands r0, r1\n\
+ adds r5, r2, 0\n\
+ cmp r0, 0\n\
+ beq _080425F4\n\
+ ldr r0, _0804263C @ =gMain\n\
+ ldr r2, _08042640 @ =0x00000439\n\
+ adds r0, r2\n\
+ ldrb r1, [r0]\n\
+ movs r0, 0x2\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ beq _080425F4\n\
+ ldr r0, [sp, 0x1C]\n\
+ cmp r0, 0x4\n\
+ beq _080425F4\n\
+ ldr r1, _08042644 @ =gBattleMons\n\
+ movs r0, 0x58\n\
+ ldr r2, [sp, 0x1C]\n\
+ muls r0, r2\n\
+ adds r1, 0x50\n\
+ adds r0, r1\n\
+ ldr r0, [r0]\n\
+ movs r1, 0xF0\n\
+ lsls r1, 12\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ beq _080425F4\n\
+ movs r0, 0\n\
+ str r0, [sp, 0x10]\n\
+_080425F4:\n\
+ ldrb r1, [r5]\n\
+ movs r0, 0x30\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ beq _0804261C\n\
+ ldr r1, _08042644 @ =gBattleMons\n\
+ ldr r0, _08042648 @ =gActiveBattler\n\
+ ldrb r2, [r0]\n\
+ movs r0, 0x58\n\
+ muls r0, r2\n\
+ adds r1, 0x50\n\
+ adds r0, r1\n\
+ ldr r0, [r0]\n\
+ movs r1, 0x80\n\
+ lsls r1, 13\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ bne _0804261C\n\
+ movs r1, 0\n\
+ str r1, [sp, 0x10]\n\
+_0804261C:\n\
+ ldrb r1, [r5]\n\
+ movs r0, 0xF\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ bne _08042628\n\
+ b _08042BCA\n\
+_08042628:\n\
+ ldr r2, _08042644 @ =gBattleMons\n\
+ ldr r0, _08042648 @ =gActiveBattler\n\
+ ldrb r1, [r0]\n\
+ movs r0, 0x58\n\
+ muls r0, r1\n\
+ adds r0, r2\n\
+ ldrb r0, [r0, 0x19]\n\
+ lsls r0, 24\n\
+ asrs r0, 24\n\
+ b _080426F2\n\
+ .align 2, 0\n\
+_0804263C: .4byte gMain\n\
+_08042640: .4byte 0x00000439\n\
+_08042644: .4byte gBattleMons\n\
+_08042648: .4byte gActiveBattler\n\
+_0804264C:\n\
+ ldr r0, [sp, 0x14]\n\
+ ldr r1, [sp, 0xC]\n\
+ adds r2, r0, r1\n\
+ ldrb r1, [r2]\n\
+ movs r0, 0xF0\n\
+ ands r0, r1\n\
+ adds r5, r2, 0\n\
+ cmp r0, 0\n\
+ beq _08042678\n\
+ ldr r2, _080426A0 @ =gBattleMons\n\
+ ldr r0, _080426A4 @ =gActiveBattler\n\
+ ldrb r1, [r0]\n\
+ movs r0, 0x58\n\
+ muls r0, r1\n\
+ adds r0, r2\n\
+ ldrb r0, [r0, 0x1A]\n\
+ lsls r0, 24\n\
+ asrs r0, 24\n\
+ cmp r0, 0xB\n\
+ bgt _08042678\n\
+ movs r2, 0\n\
+ str r2, [sp, 0x10]\n\
+_08042678:\n\
+ ldrb r1, [r5]\n\
+ movs r0, 0xF\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ bne _08042684\n\
+ b _08042BCA\n\
+_08042684:\n\
+ ldr r2, _080426A0 @ =gBattleMons\n\
+ ldr r0, _080426A4 @ =gActiveBattler\n\
+ ldrb r1, [r0]\n\
+ movs r0, 0x58\n\
+ muls r0, r1\n\
+ adds r0, r2\n\
+ ldrb r0, [r0, 0x1B]\n\
+ lsls r0, 24\n\
+ asrs r0, 24\n\
+ cmp r0, 0xB\n\
+ ble _0804269C\n\
+ b _08042BCA\n\
+_0804269C:\n\
+ b _08042832\n\
+ .align 2, 0\n\
+_080426A0: .4byte gBattleMons\n\
+_080426A4: .4byte gActiveBattler\n\
+_080426A8:\n\
+ ldr r1, [sp, 0x14]\n\
+ ldr r0, [sp, 0xC]\n\
+ adds r2, r1, r0\n\
+ ldrb r1, [r2]\n\
+ movs r0, 0xF0\n\
+ ands r0, r1\n\
+ adds r5, r2, 0\n\
+ cmp r0, 0\n\
+ beq _080426D4\n\
+ ldr r2, _08042700 @ =gBattleMons\n\
+ ldr r0, _08042704 @ =gActiveBattler\n\
+ ldrb r1, [r0]\n\
+ movs r0, 0x58\n\
+ muls r0, r1\n\
+ adds r0, r2\n\
+ ldrb r0, [r0, 0x1E]\n\
+ lsls r0, 24\n\
+ asrs r0, 24\n\
+ cmp r0, 0xB\n\
+ bgt _080426D4\n\
+ movs r1, 0\n\
+ str r1, [sp, 0x10]\n\
+_080426D4:\n\
+ ldrb r1, [r5]\n\
+ movs r0, 0xF\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ bne _080426E0\n\
+ b _08042BCA\n\
+_080426E0:\n\
+ ldr r2, _08042700 @ =gBattleMons\n\
+ ldr r0, _08042704 @ =gActiveBattler\n\
+ ldrb r1, [r0]\n\
+ movs r0, 0x58\n\
+ muls r0, r1\n\
+ adds r0, r2\n\
+ ldrb r0, [r0, 0x1C]\n\
+ lsls r0, 24\n\
+ asrs r0, 24\n\
+_080426F2:\n\
+ cmp r0, 0xB\n\
+ ble _080426F8\n\
+ b _08042BCA\n\
+_080426F8:\n\
+ movs r2, 0\n\
+ str r2, [sp, 0x10]\n\
+ b _08042BCA\n\
+ .align 2, 0\n\
+_08042700: .4byte gBattleMons\n\
+_08042704: .4byte gActiveBattler\n\
+_08042708:\n\
+ ldr r0, [sp, 0x14]\n\
+ ldr r1, [sp, 0xC]\n\
+ adds r2, r0, r1\n\
+ ldrb r1, [r2]\n\
+ movs r0, 0x80\n\
+ ands r0, r1\n\
+ adds r5, r2, 0\n\
+ cmp r0, 0\n\
+ beq _0804273A\n\
+ ldr r4, _08042838 @ =gSideTimers\n\
+ ldr r0, _0804283C @ =gActiveBattler\n\
+ ldrb r0, [r0]\n\
+ bl GetBattlerSide\n\
+ lsls r0, 24\n\
+ lsrs r0, 24\n\
+ lsls r1, r0, 1\n\
+ adds r1, r0\n\
+ lsls r1, 2\n\
+ adds r1, r4\n\
+ ldrb r0, [r1, 0x4]\n\
+ cmp r0, 0\n\
+ bne _0804273A\n\
+ movs r2, 0\n\
+ str r2, [sp, 0x10]\n\
+_0804273A:\n\
+ ldrb r1, [r5]\n\
+ movs r0, 0x40\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ beq _08042756\n\
+ mov r0, r8\n\
+ movs r1, 0x38\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ cmp r0, 0x64\n\
+ beq _08042756\n\
+ movs r0, 0\n\
+ str r0, [sp, 0x10]\n\
+_08042756:\n\
+ ldrb r1, [r5]\n\
+ movs r0, 0x20\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ beq _08042776\n\
+ mov r0, r8\n\
+ ldr r1, [sp, 0x4]\n\
+ movs r2, 0x7\n\
+ ldr r3, [sp, 0x1C]\n\
+ bl sub_8042BE8\n\
+ lsls r0, 24\n\
+ cmp r0, 0\n\
+ beq _08042776\n\
+ movs r1, 0\n\
+ str r1, [sp, 0x10]\n\
+_08042776:\n\
+ ldrb r1, [r5]\n\
+ movs r0, 0x10\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ beq _08042796\n\
+ ldr r2, _08042840 @ =0x00000f88\n\
+ mov r0, r8\n\
+ ldr r1, [sp, 0x4]\n\
+ ldr r3, [sp, 0x1C]\n\
+ bl sub_8042BE8\n\
+ lsls r0, 24\n\
+ cmp r0, 0\n\
+ beq _08042796\n\
+ movs r2, 0\n\
+ str r2, [sp, 0x10]\n\
+_08042796:\n\
+ ldrb r1, [r5]\n\
+ movs r0, 0x8\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ beq _080427B6\n\
+ mov r0, r8\n\
+ ldr r1, [sp, 0x4]\n\
+ movs r2, 0x10\n\
+ ldr r3, [sp, 0x1C]\n\
+ bl sub_8042BE8\n\
+ lsls r0, 24\n\
+ cmp r0, 0\n\
+ beq _080427B6\n\
+ movs r0, 0\n\
+ str r0, [sp, 0x10]\n\
+_080427B6:\n\
+ ldrb r1, [r5]\n\
+ movs r0, 0x4\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ beq _080427D6\n\
+ mov r0, r8\n\
+ ldr r1, [sp, 0x4]\n\
+ movs r2, 0x20\n\
+ ldr r3, [sp, 0x1C]\n\
+ bl sub_8042BE8\n\
+ lsls r0, 24\n\
+ cmp r0, 0\n\
+ beq _080427D6\n\
+ movs r1, 0\n\
+ str r1, [sp, 0x10]\n\
+_080427D6:\n\
+ ldrb r1, [r5]\n\
+ movs r0, 0x2\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ beq _080427F6\n\
+ mov r0, r8\n\
+ ldr r1, [sp, 0x4]\n\
+ movs r2, 0x40\n\
+ ldr r3, [sp, 0x1C]\n\
+ bl sub_8042BE8\n\
+ lsls r0, 24\n\
+ cmp r0, 0\n\
+ beq _080427F6\n\
+ movs r2, 0\n\
+ str r2, [sp, 0x10]\n\
+_080427F6:\n\
+ ldrb r1, [r5]\n\
+ movs r0, 0x1\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ bne _08042802\n\
+ b _08042BCA\n\
+_08042802:\n\
+ ldr r0, _08042844 @ =gMain\n\
+ ldr r1, _08042848 @ =0x00000439\n\
+ adds r0, r1\n\
+ ldrb r1, [r0]\n\
+ movs r0, 0x2\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ bne _08042814\n\
+ b _08042BCA\n\
+_08042814:\n\
+ ldr r2, [sp, 0x1C]\n\
+ cmp r2, 0x4\n\
+ bne _0804281C\n\
+ b _08042BCA\n\
+_0804281C:\n\
+ ldr r1, _0804284C @ =gBattleMons\n\
+ movs r0, 0x58\n\
+ muls r0, r2\n\
+ adds r1, 0x50\n\
+ adds r0, r1\n\
+ ldr r0, [r0]\n\
+ movs r1, 0x7\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ bne _08042832\n\
+ b _08042BCA\n\
+_08042832:\n\
+ movs r0, 0\n\
+ str r0, [sp, 0x10]\n\
+ b _08042BCA\n\
+ .align 2, 0\n\
+_08042838: .4byte gSideTimers\n\
+_0804283C: .4byte gActiveBattler\n\
+_08042840: .4byte 0x00000f88\n\
+_08042844: .4byte gMain\n\
+_08042848: .4byte 0x00000439\n\
+_0804284C: .4byte gBattleMons\n\
+_08042850:\n\
+ ldr r1, [sp, 0x14]\n\
+ ldr r2, [sp, 0xC]\n\
+ adds r0, r1, r2\n\
+ ldrb r7, [r0]\n\
+ movs r0, 0x20\n\
+ ands r0, r7\n\
+ cmp r0, 0\n\
+ beq _080428B6\n\
+ movs r0, 0xDF\n\
+ ands r7, r0\n\
+ mov r0, r8\n\
+ movs r1, 0x15\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ adds r5, r0, 0\n\
+ ldr r0, _080428DC @ =gUnknown_825DEA1\n\
+ ldr r1, [sp, 0x8]\n\
+ adds r0, r1, r0\n\
+ ldrb r0, [r0]\n\
+ ands r5, r0\n\
+ lsls r0, r1, 1\n\
+ lsrs r5, r0\n\
+ adds r1, 0xD\n\
+ mov r0, r8\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ adds r4, r0, 0\n\
+ lsls r4, 16\n\
+ lsrs r4, 16\n\
+ mov r0, r8\n\
+ movs r1, 0x15\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ adds r1, r0, 0\n\
+ lsls r1, 24\n\
+ lsrs r1, 24\n\
+ adds r0, r4, 0\n\
+ ldr r2, [sp, 0x8]\n\
+ bl CalculatePPWithBonus\n\
+ lsls r0, 24\n\
+ lsrs r0, 24\n\
+ cmp r5, 0x2\n\
+ bhi _080428B6\n\
+ cmp r0, 0x4\n\
+ bls _080428B6\n\
+ movs r2, 0\n\
+ str r2, [sp, 0x10]\n\
+_080428B6:\n\
+ movs r0, 0\n\
+ mov r9, r0\n\
+ cmp r7, 0\n\
+ bne _080428C0\n\
+ b _08042BCA\n\
+_080428C0:\n\
+ movs r0, 0x1\n\
+ ands r0, r7\n\
+ cmp r0, 0\n\
+ bne _080428CA\n\
+ b _08042A5C\n\
+_080428CA:\n\
+ mov r1, r9\n\
+ cmp r1, 0x7\n\
+ bls _080428D2\n\
+ b _08042A5C\n\
+_080428D2:\n\
+ lsls r0, r1, 2\n\
+ ldr r1, _080428E0 @ =_080428E4\n\
+ adds r0, r1\n\
+ ldr r0, [r0]\n\
+ mov pc, r0\n\
+ .align 2, 0\n\
+_080428DC: .4byte gUnknown_825DEA1\n\
+_080428E0: .4byte _080428E4\n\
+ .align 2, 0\n\
+_080428E4:\n\
+ .4byte _08042904\n\
+ .4byte _08042904\n\
+ .4byte _08042934\n\
+ .4byte _08042996\n\
+ .4byte _08042A5C\n\
+ .4byte _08042A5C\n\
+ .4byte _08042A5C\n\
+ .4byte _08042A4A\n\
+_08042904:\n\
+ mov r0, r8\n\
+ bl GetMonEVCount\n\
+ lsls r0, 16\n\
+ ldr r1, _0804292C @ =0x01fd0000\n\
+ cmp r0, r1\n\
+ bls _08042914\n\
+ b _08042578\n\
+_08042914:\n\
+ ldr r0, _08042930 @ =sGetMonDataEVConstants\n\
+ add r0, r9\n\
+ ldrb r1, [r0]\n\
+ mov r0, r8\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ adds r5, r0, 0\n\
+ cmp r5, 0x63\n\
+ bls _0804292A\n\
+ b _08042A5C\n\
+_0804292A:\n\
+ b _08042A3A\n\
+ .align 2, 0\n\
+_0804292C: .4byte 0x01fd0000\n\
+_08042930: .4byte sGetMonDataEVConstants\n\
+_08042934:\n\
+ movs r0, 0x10\n\
+ ands r0, r7\n\
+ cmp r0, 0\n\
+ beq _08042952\n\
+ mov r0, r8\n\
+ movs r1, 0x39\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ cmp r0, 0\n\
+ beq _08042968\n\
+ mov r0, r10\n\
+ adds r0, 0x1\n\
+ lsls r0, 24\n\
+ b _080429F4\n\
+_08042952:\n\
+ mov r0, r8\n\
+ movs r1, 0x39\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ cmp r0, 0\n\
+ bne _08042968\n\
+ mov r0, r10\n\
+ adds r0, 0x1\n\
+ lsls r0, 24\n\
+ b _080429F4\n\
+_08042968:\n\
+ mov r0, r8\n\
+ movs r1, 0x3A\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ adds r4, r0, 0\n\
+ mov r0, r8\n\
+ movs r1, 0x39\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ cmp r4, r0\n\
+ beq _08042986\n\
+ movs r0, 0\n\
+ str r0, [sp, 0x10]\n\
+_08042986:\n\
+ mov r0, r10\n\
+ adds r0, 0x1\n\
+ lsls r0, 24\n\
+ lsrs r0, 24\n\
+ mov r10, r0\n\
+ movs r0, 0xEF\n\
+ ands r7, r0\n\
+ b _08042A5C\n\
+_08042996:\n\
+ movs r0, 0x2\n\
+ ands r0, r7\n\
+ cmp r0, 0\n\
+ bne _080429FA\n\
+ movs r6, 0\n\
+ movs r1, 0x1\n\
+ add r10, r1\n\
+_080429A4:\n\
+ adds r1, r6, 0\n\
+ adds r1, 0x11\n\
+ mov r0, r8\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ adds r5, r0, 0\n\
+ adds r1, r6, 0\n\
+ adds r1, 0xD\n\
+ mov r0, r8\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ adds r4, r0, 0\n\
+ lsls r4, 16\n\
+ lsrs r4, 16\n\
+ mov r0, r8\n\
+ movs r1, 0x15\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ adds r1, r0, 0\n\
+ lsls r1, 24\n\
+ lsrs r1, 24\n\
+ lsls r2, r6, 24\n\
+ lsrs r2, 24\n\
+ adds r0, r4, 0\n\
+ bl CalculatePPWithBonus\n\
+ lsls r0, 24\n\
+ lsrs r0, 24\n\
+ cmp r5, r0\n\
+ beq _080429EA\n\
+ movs r2, 0\n\
+ str r2, [sp, 0x10]\n\
+_080429EA:\n\
+ adds r6, 0x1\n\
+ cmp r6, 0x3\n\
+ ble _080429A4\n\
+ mov r1, r10\n\
+ lsls r0, r1, 24\n\
+_080429F4:\n\
+ lsrs r0, 24\n\
+ mov r10, r0\n\
+ b _08042A5C\n\
+_080429FA:\n\
+ ldr r1, [sp, 0x8]\n\
+ adds r1, 0x11\n\
+ mov r0, r8\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ adds r5, r0, 0\n\
+ ldr r1, [sp, 0x8]\n\
+ adds r1, 0xD\n\
+ mov r0, r8\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ adds r4, r0, 0\n\
+ lsls r4, 16\n\
+ lsrs r4, 16\n\
+ mov r0, r8\n\
+ movs r1, 0x15\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ adds r1, r0, 0\n\
+ lsls r1, 24\n\
+ lsrs r1, 24\n\
+ adds r0, r4, 0\n\
+ ldr r2, [sp, 0x8]\n\
+ bl CalculatePPWithBonus\n\
+ lsls r0, 24\n\
+ lsrs r0, 24\n\
+ cmp r5, r0\n\
+ beq _08042A5C\n\
+_08042A3A:\n\
+ mov r0, r10\n\
+ adds r0, 0x1\n\
+ lsls r0, 24\n\
+ lsrs r0, 24\n\
+ mov r10, r0\n\
+ movs r2, 0\n\
+ str r2, [sp, 0x10]\n\
+ b _08042A5C\n\
+_08042A4A:\n\
+ mov r0, r8\n\
+ movs r1, 0x2\n\
+ ldr r2, [sp]\n\
+ bl GetEvolutionTargetSpecies\n\
+ lsls r0, 16\n\
+ cmp r0, 0\n\
+ beq _08042A5C\n\
+ b _08042574\n\
+_08042A5C:\n\
+ movs r0, 0x1\n\
+ add r9, r0\n\
+ lsrs r7, 1\n\
+ cmp r7, 0\n\
+ beq _08042A68\n\
+ b _080428C0\n\
+_08042A68:\n\
+ b _08042BCA\n\
+_08042A6A:\n\
+ ldr r1, [sp, 0x14]\n\
+ ldr r2, [sp, 0xC]\n\
+ adds r0, r1, r2\n\
+ ldrb r7, [r0]\n\
+ movs r0, 0\n\
+ mov r9, r0\n\
+ cmp r7, 0\n\
+ bne _08042A7C\n\
+ b _08042BCA\n\
+_08042A7C:\n\
+ movs r0, 0x1\n\
+ ands r0, r7\n\
+ cmp r0, 0\n\
+ bne _08042A86\n\
+ b _08042BBE\n\
+_08042A86:\n\
+ mov r1, r9\n\
+ cmp r1, 0x7\n\
+ bls _08042A8E\n\
+ b _08042BBE\n\
+_08042A8E:\n\
+ lsls r0, r1, 2\n\
+ ldr r1, _08042A98 @ =_08042A9C\n\
+ adds r0, r1\n\
+ ldr r0, [r0]\n\
+ mov pc, r0\n\
+ .align 2, 0\n\
+_08042A98: .4byte _08042A9C\n\
+ .align 2, 0\n\
+_08042A9C:\n\
+ .4byte _08042ABC\n\
+ .4byte _08042ABC\n\
+ .4byte _08042ABC\n\
+ .4byte _08042ABC\n\
+ .4byte _08042AF4\n\
+ .4byte _08042B4C\n\
+ .4byte _08042B68\n\
+ .4byte _08042B92\n\
+_08042ABC:\n\
+ mov r0, r8\n\
+ bl GetMonEVCount\n\
+ lsls r0, 16\n\
+ ldr r1, _08042AEC @ =0x01fd0000\n\
+ cmp r0, r1\n\
+ bls _08042ACC\n\
+ b _08042578\n\
+_08042ACC:\n\
+ ldr r0, _08042AF0 @ =sGetMonDataEVConstants\n\
+ mov r1, r9\n\
+ adds r1, 0x2\n\
+ adds r1, r0\n\
+ ldrb r1, [r1]\n\
+ mov r0, r8\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ adds r5, r0, 0\n\
+ cmp r5, 0x63\n\
+ bhi _08042BBE\n\
+ movs r2, 0\n\
+ str r2, [sp, 0x10]\n\
+ b _08042BB4\n\
+ .align 2, 0\n\
+_08042AEC: .4byte 0x01fd0000\n\
+_08042AF0: .4byte sGetMonDataEVConstants\n\
+_08042AF4:\n\
+ mov r0, r8\n\
+ movs r1, 0x15\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ adds r5, r0, 0\n\
+ ldr r0, _08042B48 @ =gUnknown_825DEA1\n\
+ ldr r1, [sp, 0x8]\n\
+ adds r0, r1, r0\n\
+ ldrb r0, [r0]\n\
+ ands r5, r0\n\
+ lsls r0, r1, 1\n\
+ lsrs r5, r0\n\
+ adds r1, 0xD\n\
+ mov r0, r8\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ adds r4, r0, 0\n\
+ lsls r4, 16\n\
+ lsrs r4, 16\n\
+ mov r0, r8\n\
+ movs r1, 0x15\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ adds r1, r0, 0\n\
+ lsls r1, 24\n\
+ lsrs r1, 24\n\
+ adds r0, r4, 0\n\
+ ldr r2, [sp, 0x8]\n\
+ bl CalculatePPWithBonus\n\
+ lsls r0, 24\n\
+ lsrs r6, r0, 24\n\
+ cmp r5, 0x2\n\
+ bhi _08042BBE\n\
+ cmp r6, 0x4\n\
+ ble _08042BBE\n\
+ movs r2, 0\n\
+ str r2, [sp, 0x10]\n\
+ b _08042BBE\n\
+ .align 2, 0\n\
+_08042B48: .4byte gUnknown_825DEA1\n\
+_08042B4C:\n\
+ mov r0, r8\n\
+ movs r1, 0x20\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ cmp r0, 0x63\n\
+ bhi _08042BB4\n\
+ ldr r0, [sp, 0x10]\n\
+ cmp r0, 0\n\
+ bne _08042BB4\n\
+ ldr r1, [sp, 0x18]\n\
+ cmp r1, 0\n\
+ bne _08042BB4\n\
+ b _08042BAC\n\
+_08042B68:\n\
+ mov r0, r8\n\
+ movs r1, 0x20\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ cmp r0, 0x63\n\
+ bls _08042BB4\n\
+ mov r0, r8\n\
+ movs r1, 0x20\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ cmp r0, 0xC7\n\
+ bhi _08042BB4\n\
+ ldr r2, [sp, 0x10]\n\
+ cmp r2, 0\n\
+ bne _08042BB4\n\
+ ldr r0, [sp, 0x18]\n\
+ cmp r0, 0\n\
+ bne _08042BB4\n\
+ b _08042BAC\n\
+_08042B92:\n\
+ mov r0, r8\n\
+ movs r1, 0x20\n\
+ movs r2, 0\n\
+ bl GetMonData\n\
+ cmp r0, 0xC7\n\
+ bls _08042BB4\n\
+ ldr r1, [sp, 0x10]\n\
+ cmp r1, 0\n\
+ bne _08042BB4\n\
+ ldr r2, [sp, 0x18]\n\
+ cmp r2, 0\n\
+ bne _08042BB4\n\
+_08042BAC:\n\
+ ldr r0, [sp, 0x14]\n\
+ add r0, r10\n\
+ ldrb r0, [r0]\n\
+ str r0, [sp, 0x18]\n\
+_08042BB4:\n\
+ mov r0, r10\n\
+ adds r0, 0x1\n\
+ lsls r0, 24\n\
+ lsrs r0, 24\n\
+ mov r10, r0\n\
+_08042BBE:\n\
+ movs r0, 0x1\n\
+ add r9, r0\n\
+ lsrs r7, 1\n\
+ cmp r7, 0\n\
+ beq _08042BCA\n\
+ b _08042A7C\n\
+_08042BCA:\n\
+ ldr r1, [sp, 0xC]\n\
+ adds r1, 0x1\n\
+ str r1, [sp, 0xC]\n\
+ cmp r1, 0x5\n\
+ bgt _08042BD6\n\
+ b _08042582\n\
+_08042BD6:\n\
+ ldr r0, [sp, 0x10]\n\
+_08042BD8:\n\
+ add sp, 0x20\n\
+ pop {r3-r5}\n\
+ mov r8, r3\n\
+ mov r9, r4\n\
+ mov r10, r5\n\
+ pop {r4-r7}\n\
+ pop {r1}\n\
+ bx r1\n\
+ .syntax divided\n");
+}
+#endif
+
+bool8 sub_8042BE8(struct Pokemon *mon, u32 unused, u32 healMask, u8 battleId)
+{
+ if((GetMonData(mon, MON_DATA_STATUS, NULL) & healMask) != 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+u8 GetItemEffectParamOffset(u16 itemId, u8 effectByte, u8 effectBit)
+{
+ const u8 *temp;
+ const u8 *itemEffect;
+ u8 offset;
+ int i;
+ u8 j;
+ u8 val;
+
+ offset = 6;
+
+ temp = gItemEffectTable[itemId - 13];
+
+ if (!temp && itemId != ITEM_ENIGMA_BERRY)
+ return 0;
+
+ if (itemId == ITEM_ENIGMA_BERRY)
+ {
+ temp = gEnigmaBerries[gActiveBattler].itemEffect;
+ }
+
+ itemEffect = temp;
+
+ for (i = 0; i < 6; i++)
+ {
+ switch (i)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ if (i == effectByte)
+ return 0;
+ break;
+ case 4:
+ val = itemEffect[4];
+ if (val & 0x20)
+ val &= 0xDF;
+ j = 0;
+ while (val)
+ {
+ if (val & 1)
+ {
+ switch (j)
+ {
+ case 2:
+ if (val & 0x10)
+ val &= 0xEF;
+ case 0:
+ if (i == effectByte && (val & effectBit))
+ return offset;
+ offset++;
+ break;
+ case 1:
+ if (i == effectByte && (val & effectBit))
+ return offset;
+ offset++;
+ break;
+ case 3:
+ if (i == effectByte && (val & effectBit))
+ return offset;
+ offset++;
+ break;
+ case 7:
+ if (i == effectByte)
+ return 0;
+ break;
+ }
+ }
+ j++;
+ val >>= 1;
+ if (i == effectByte)
+ effectBit >>= 1;
+ }
+ break;
+ case 5:
+ val = itemEffect[5];
+ j = 0;
+ while (val)
+ {
+ if (val & 1)
+ {
+ switch (j)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ if (i == effectByte && (val & effectBit))
+ return offset;
+ offset++;
+ break;
+ case 7:
+ if (i == effectByte)
+ return 0;
+ break;
+ }
+ }
+ j++;
+ val >>= 1;
+ if (i == effectByte)
+ effectBit >>= 1;
+ }
+ break;
+ }
+ }
+
+ return offset;
+}
+
+void sub_8042D50(int stat)
+{
+ gBattlerTarget = gBattlerInMenuId;
+ StringCopy(gBattleTextBuff1, gUnknown_83FD5D0[gUnknown_825DFF0[stat]]);
+ StringCopy(gBattleTextBuff2, BattleText_Rose);
+ BattleStringExpandPlaceholdersToDisplayedString(BattleText_UnknownString3);
+}
+
+u8 *sub_8042DA4(u16 itemId)
+{
+ int i;
+ const u8 *itemEffect;
+
+ if (itemId == ITEM_ENIGMA_BERRY)
+ {
+ if (gMain.inBattle)
+ {
+ itemEffect = gEnigmaBerries[gBattlerInMenuId].itemEffect;
+ }
+ else
+ {
+ itemEffect = gSaveBlock1Ptr->enigmaBerry.itemEffect;
+ }
+ }
+ else
+ {
+ itemEffect = gItemEffectTable[itemId - 13];
+ }
+
+ gPotentialItemEffectBattler = gBattlerInMenuId;
+
+ for (i = 0; i < 3; i++)
+ {
+ if (itemEffect[i] & 0xF)
+ sub_8042D50(i * 2);
+ if (itemEffect[i] & 0xF0)
+ {
+ if (i)
+ {
+ sub_8042D50(i * 2 + 1);
+ }
+ else
+ {
+ sBattler_AI = gBattlerInMenuId;
+ BattleStringExpandPlaceholdersToDisplayedString(BattleText_GetPumped);
+ }
+ }
+ }
+
+ if (itemEffect[3] & 0x80)
+ {
+ sBattler_AI = gBattlerInMenuId;
+ BattleStringExpandPlaceholdersToDisplayedString(BattleText_MistShroud);
+ }
+
+ return gDisplayedStringBattle;
+}
+
+u8 GetNature(struct Pokemon *mon)
+{
+ return GetMonData(mon, MON_DATA_PERSONALITY, 0) % 25;
+}
+
+u8 GetNatureFromPersonality(u32 personality)
+{
+ return personality % 25;
+}
+
+extern bool32 sub_806E25C(void);
+
+u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 type, u16 evolutionItem)
+{
+ int i;
+ u16 targetSpecies = 0;
+ u16 species = GetMonData(mon, MON_DATA_SPECIES, 0);
+ u16 heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, 0);
+ u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, 0);
+ u8 level;
+ u16 friendship;
+ u8 beauty = GetMonData(mon, MON_DATA_BEAUTY, 0);
+ u16 upperPersonality = personality >> 16;
+ u8 holdEffect;
+
+ if (heldItem == ITEM_ENIGMA_BERRY)
+ holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
+ else
+ holdEffect = ItemId_GetHoldEffect(heldItem);
+
+ if (holdEffect == HOLD_EFFECT_PREVENT_EVOLVE && type != 3)
+ return 0;
+
+ switch (type)
+ {
+ case 0:
+ level = GetMonData(mon, MON_DATA_LEVEL, 0);
+ friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, 0);
+
+ for (i = 0; i < 5; i++)
+ {
+ switch (gEvolutionTable[species][i].method)
+ {
+ case EVO_FRIENDSHIP:
+ if (friendship >= 220)
+ targetSpecies = gEvolutionTable[species][i].targetSpecies;
+ break;
+ // FR/LG removed the time of day evolutions due to having no RTC.
+ case EVO_FRIENDSHIP_DAY:
+ /*
+ RtcCalcLocalTime();
+ if (gLocalTime.hours >= 12 && gLocalTime.hours < 24 && friendship >= 220)
+ targetSpecies = gEvolutionTable[species][i].targetSpecies;
+ */
+ break;
+ case EVO_FRIENDSHIP_NIGHT:
+ /*
+ RtcCalcLocalTime();
+ if (gLocalTime.hours >= 0 && gLocalTime.hours < 12 && friendship >= 220)
+ targetSpecies = gEvolutionTable[species][i].targetSpecies;
+ */
+ break;
+ case EVO_LEVEL:
+ if (gEvolutionTable[species][i].param <= level)
+ targetSpecies = gEvolutionTable[species][i].targetSpecies;
+ break;
+ case EVO_LEVEL_ATK_GT_DEF:
+ if (gEvolutionTable[species][i].param <= level)
+ if (GetMonData(mon, MON_DATA_ATK, 0) > GetMonData(mon, MON_DATA_DEF, 0))
+ targetSpecies = gEvolutionTable[species][i].targetSpecies;
+ break;
+ case EVO_LEVEL_ATK_EQ_DEF:
+ if (gEvolutionTable[species][i].param <= level)
+ if (GetMonData(mon, MON_DATA_ATK, 0) == GetMonData(mon, MON_DATA_DEF, 0))
+ targetSpecies = gEvolutionTable[species][i].targetSpecies;
+ break;
+ case EVO_LEVEL_ATK_LT_DEF:
+ if (gEvolutionTable[species][i].param <= level)
+ if (GetMonData(mon, MON_DATA_ATK, 0) < GetMonData(mon, MON_DATA_DEF, 0))
+ targetSpecies = gEvolutionTable[species][i].targetSpecies;
+ break;
+ case EVO_LEVEL_SILCOON:
+ if (gEvolutionTable[species][i].param <= level && (upperPersonality % 10) <= 4)
+ targetSpecies = gEvolutionTable[species][i].targetSpecies;
+ break;
+ case EVO_LEVEL_CASCOON:
+ if (gEvolutionTable[species][i].param <= level && (upperPersonality % 10) > 4)
+ targetSpecies = gEvolutionTable[species][i].targetSpecies;
+ break;
+ case EVO_LEVEL_NINJASK:
+ if (gEvolutionTable[species][i].param <= level)
+ targetSpecies = gEvolutionTable[species][i].targetSpecies;
+ break;
+ case EVO_BEAUTY:
+ if (gEvolutionTable[species][i].param <= beauty)
+ targetSpecies = gEvolutionTable[species][i].targetSpecies;
+ break;
+ }
+ }
+ break;
+ case 1:
+ for (i = 0; i < 5; i++)
+ {
+ switch (gEvolutionTable[species][i].method)
+ {
+ case EVO_TRADE:
+ targetSpecies = gEvolutionTable[species][i].targetSpecies;
+ break;
+ case EVO_TRADE_ITEM:
+ if (gEvolutionTable[species][i].param == heldItem)
+ {
+ targetSpecies = gEvolutionTable[species][i].targetSpecies;
+ if (sub_806E25C() || targetSpecies <= 151)
+ {
+ heldItem = 0;
+ SetMonData(mon, MON_DATA_HELD_ITEM, &heldItem);
+ targetSpecies = gEvolutionTable[species][i].targetSpecies;
+ }
+ }
+ break;
+ }
+ }
+ break;
+ case 2:
+ case 3:
+ for (i = 0; i < 5; i++)
+ {
+ if (gEvolutionTable[species][i].method == EVO_ITEM
+ && gEvolutionTable[species][i].param == evolutionItem)
+ {
+ targetSpecies = gEvolutionTable[species][i].targetSpecies;
+ break;
+ }
+ }
+ break;
+ }
+
+ return targetSpecies;
+}
+
+// HoennPokedexNumToSpecies, but is it really Hoenn or Kanto its checking
+// TODO: Figure this out
+u16 sub_80431B4(u16 var)
+{
+ u16 species;
+
+ if(!var)
+ return 0;
+
+ species = 0;
+
+ while(species < POKEMON_SLOTS_NUMBER - 1 && gUnknown_8251CB8[species] != var)
+ species++;
+
+ if(species == POKEMON_SLOTS_NUMBER - 1)
+ return 0;
+
+ return species + 1;
+}
+
+u16 NationalPokedexNumToSpecies(u16 nationalNum)
+{
+ u16 species;
+
+ if (!nationalNum)
+ return 0;
+
+ species = 0;
+
+ while (species < POKEMON_SLOTS_NUMBER - 1 && gUnknown_8251FEE[species] != nationalNum)
+ species++;
+
+ if (species == POKEMON_SLOTS_NUMBER - 1)
+ return 0;
+
+ return species + 1;
+}
+
+// NationalToKantoOrder?
+u16 sub_804324C(u16 nationalNum)
+{
+ u16 hoennNum;
+
+ if (!nationalNum)
+ return 0;
+
+ hoennNum = 0;
+
+ while (hoennNum < POKEMON_SLOTS_NUMBER - 1 && gUnknown_8252324[hoennNum] != nationalNum)
+ hoennNum++;
+
+ if (hoennNum == POKEMON_SLOTS_NUMBER - 1)
+ return 0;
+
+ return hoennNum + 1;
+}
+
+u16 SpeciesToNationalPokedexNum(u16 species)
+{
+ if (!species)
+ return 0;
+
+ return gUnknown_8251FEE[species - 1];
+}
+
+// these 2 functions are probably kanto and not hoenn
+// TODO: figure this out
+u16 SpeciesToHoennPokedexNum(u16 species)
+{
+ if (!species)
+ return 0;
+
+ return gUnknown_8251CB8[species - 1];
+}
+
+u16 HoennToNationalOrder(u16 hoennNum)
+{
+ if (!hoennNum)
+ return 0;
+
+ return gUnknown_8252324[hoennNum - 1];
+}
+
+u16 SpeciesToCryId(u16 species)
+{
+ if (species < SPECIES_OLD_UNOWN_B - 1)
+ return species;
+
+ if (species <= SPECIES_OLD_UNOWN_Z - 1)
+ return SPECIES_UNOWN - 1;
+
+ return gUnknown_82539D4[species - ((SPECIES_OLD_UNOWN_Z + 1) - 1)];
+}
+
+void sub_8043338(u16 species, u32 personality, u8 *dest)
+{
+ if (species == SPECIES_SPINDA
+ && dest != gMonSpritesGfxPtr->sprites[0]
+ && dest != gMonSpritesGfxPtr->sprites[2])
+ {
+ int i;
+ for (i = 0; i < 4; i++)
+ {
+ int j;
+ u8 x = gSpindaSpotGraphics[i].x + ((personality & 0x0F) - 8);
+ u8 y = gSpindaSpotGraphics[i].y + (((personality & 0xF0) >> 4) - 8);
+
+ for (j = 0; j < 16; j++)
+ {
+ int k;
+ s32 row = gSpindaSpotGraphics[i].image[j];
+
+ for (k = x; k < x + 16; k++)
+ {
+ u8 *val = dest + ((k / 8) * 32) + ((k % 8) / 2) + ((y >> 3) << 8) + ((y & 7) << 2);
+
+ if (row & 1)
+ {
+ if (k & 1)
+ {
+ if ((u8)((*val & 0xF0) - 0x10) <= 0x20)
+ *val += 0x40;
+ }
+ else
+ {
+ if ((u8)((*val & 0xF) - 0x01) <= 0x02)
+ *val += 0x04;
+ }
+ }
+
+ row >>= 1;
+ }
+
+ y++;
+ }
+
+ personality >>= 8;
+ }
+ }
+}
+
+void DrawSpindaSpots(u16 species, u32 personality, u8 *dest, u8 a4)
+{
+ if (species == SPECIES_SPINDA && a4)
+ {
+ int i;
+ for (i = 0; i < 4; i++)
+ {
+ int j;
+ u8 x = gSpindaSpotGraphics[i].x + ((personality & 0x0F) - 8);
+ u8 y = gSpindaSpotGraphics[i].y + (((personality & 0xF0) >> 4) - 8);
+
+ for (j = 0; j < 16; j++)
+ {
+ int k;
+ s32 row = gSpindaSpotGraphics[i].image[j];
+
+ for (k = x; k < x + 16; k++)
+ {
+ u8 *val = dest + ((k / 8) * 32) + ((k % 8) / 2) + ((y >> 3) << 8) + ((y & 7) << 2);
+
+ if (row & 1)
+ {
+ if (k & 1)
+ {
+ if ((u8)((*val & 0xF0) - 0x10) <= 0x20)
+ *val += 0x40;
+ }
+ else
+ {
+ if ((u8)((*val & 0xF) - 0x01) <= 0x02)
+ *val += 0x04;
+ }
+ }
+
+ row >>= 1;
+ }
+
+ y++;
+ }
+
+ personality >>= 8;
+ }
+ }
+}
+
+void EvolutionRenameMon(struct Pokemon *mon, u16 oldSpecies, u16 newSpecies)
+{
+ u8 language;
+ GetMonData(mon, MON_DATA_NICKNAME, gStringVar1);
+ language = GetMonData(mon, MON_DATA_LANGUAGE, &language);
+ if (language == GAME_LANGUAGE && !StringCompare(gSpeciesNames[oldSpecies], gStringVar1))
+ SetMonData(mon, MON_DATA_NICKNAME, gSpeciesNames[newSpecies]);
+}
+
+bool8 sub_80435E0(void)
+{
+ bool8 retVal = FALSE;
+ switch (gLinkPlayers[GetMultiplayerId()].id)
+ {
+ case 0:
+ case 3:
+ retVal = FALSE;
+ break;
+ case 1:
+ case 2:
+ retVal = TRUE;
+ break;
+ }
+ return retVal;
+}
+
+bool8 sub_8043620(u8 id)
+{
+ bool8 retVal = FALSE;
+ switch (gLinkPlayers[id].id)
+ {
+ case 0:
+ case 3:
+ retVal = FALSE;
+ break;
+ case 1:
+ case 2:
+ retVal = TRUE;
+ break;
+ }
+ return retVal;
+}
+
+s32 GetBankMultiplayerId(u16 a1)
+{
+ s32 id;
+ for (id = 0; id < MAX_LINK_PLAYERS; id++)
+ if (gLinkPlayers[id].id == a1)
+ break;
+ return id;
+}
+
+u8 sub_804367C(u16 trainer)
+{
+ return gTrainers[trainer].encounterMusic_gender & 0x7F;
+}
+
+u16 nature_stat_mod(u8 nature, u16 n, u8 statIndex)
+{
+ if (statIndex < 1 || statIndex > 5)
+ {
+ // should just be "return n", but it wouldn't match without this
+ u16 retVal = n;
+ retVal++;
+ retVal--;
+ return retVal;
+ }
+
+ switch (gNatureStatTable[nature][statIndex - 1])
+ {
+ case 1:
+ return (u16)(n * 110) / 100;
+ case -1:
+ return (u16)(n * 90) / 100;
+ }
+
+ return n;
+}
+
+// TODO: Move these to constants/trainers.h
+#define TRAINER_CLASS_ELITE_FOUR 0x54
+#define TRAINER_CLASS_LEADER 0x57
+#define TRAINER_CLASS_CHAMPION 0x5A
+
+// TODO: Move these too
+#define FRIENDSHIP_EVENT_LEAGUE_BATTLE 0x3
+#define FRIENDSHIP_EVENT_WALKING 0x5
+
+void AdjustFriendship(struct Pokemon *mon, u8 event)
+{
+ u16 species = GetMonData(mon, MON_DATA_SPECIES2, 0);
+ u16 heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, 0);
+ u8 holdEffect;
+
+ if (heldItem == ITEM_ENIGMA_BERRY)
+ {
+ if (gMain.inBattle)
+ holdEffect = gEnigmaBerries[0].holdEffect;
+ else
+ holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
+ }
+ else
+ {
+ holdEffect = ItemId_GetHoldEffect(heldItem);
+ }
+
+ if (species && species != SPECIES_EGG)
+ {
+ u8 friendshipLevel = 0;
+ s16 friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, 0);
+ if (friendship > 99)
+ friendshipLevel++;
+ if (friendship > 199)
+ friendshipLevel++;
+
+ if ((event != FRIENDSHIP_EVENT_WALKING || !(Random() & 1))
+ && (event != FRIENDSHIP_EVENT_LEAGUE_BATTLE
+ || ((gBattleTypeFlags & BATTLE_TYPE_TRAINER)
+ && (gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_ELITE_FOUR
+ || gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_LEADER
+ || gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_CHAMPION))))
+ {
+ s8 delta = sFriendshipEventDeltas[event][friendshipLevel];
+ if (delta > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
+ delta = (150 * delta) / 100;
+
+ friendship += delta;
+ if (delta > 0)
+ {
+ if (GetMonData(mon, MON_DATA_POKEBALL, 0) == ITEM_LUXURY_BALL)
+ friendship++;
+ if (GetMonData(mon, MON_DATA_MET_LOCATION, 0) == sav1_map_get_name())
+ friendship++;
+ }
+
+ if (friendship < 0)
+ friendship = 0;
+ if (friendship > 255)
+ friendship = 255;
+
+ SetMonData(mon, MON_DATA_FRIENDSHIP, &friendship);
+ }
+ }
+}
+
+void MonGainEVs(struct Pokemon *mon, u16 defeatedSpecies)
+{
+ u8 evs[NUM_STATS];
+ u16 evIncrease = 0;
+ u16 totalEVs = 0;
+ u16 heldItem;
+ u8 holdEffect;
+ int i;
+
+ for (i = 0; i < NUM_STATS; i++)
+ {
+ evs[i] = GetMonData(mon, MON_DATA_HP_EV + i, 0);
+ totalEVs += evs[i];
+ }
+
+ for (i = 0; i < NUM_STATS; i++)
+ {
+ u8 hasHadPokerus;
+ int multiplier;
+
+ if (totalEVs >= MAX_TOTAL_EVS)
+ break;
+
+ hasHadPokerus = CheckPartyHasHadPokerus(mon, 0);
+
+ if (hasHadPokerus)
+ multiplier = 2;
+ else
+ multiplier = 1;
+
+ switch (i)
+ {
+ case 0:
+ evIncrease = gBaseStats[defeatedSpecies].evYield_HP * multiplier;
+ break;
+ case 1:
+ evIncrease = gBaseStats[defeatedSpecies].evYield_Attack * multiplier;
+ break;
+ case 2:
+ evIncrease = gBaseStats[defeatedSpecies].evYield_Defense * multiplier;
+ break;
+ case 3:
+ evIncrease = gBaseStats[defeatedSpecies].evYield_Speed * multiplier;
+ break;
+ case 4:
+ evIncrease = gBaseStats[defeatedSpecies].evYield_SpAttack * multiplier;
+ break;
+ case 5:
+ evIncrease = gBaseStats[defeatedSpecies].evYield_SpDefense * multiplier;
+ break;
+ }
+
+ heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, 0);
+
+ if (heldItem == ITEM_ENIGMA_BERRY)
+ {
+ if (gMain.inBattle)
+ holdEffect = gEnigmaBerries[0].holdEffect;
+ else
+ holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
+ }
+ else
+ {
+ holdEffect = ItemId_GetHoldEffect(heldItem);
+ }
+
+ if (holdEffect == HOLD_EFFECT_MACHO_BRACE)
+ evIncrease *= 2;
+
+ if (totalEVs + (s16)evIncrease > MAX_TOTAL_EVS)
+ evIncrease = ((s16)evIncrease + MAX_TOTAL_EVS) - (totalEVs + evIncrease);
+
+ if (evs[i] + (s16)evIncrease > 255)
+ {
+ int val1 = (s16)evIncrease + 255;
+ int val2 = evs[i] + evIncrease;
+ evIncrease = val1 - val2;
+ }
+
+ evs[i] += evIncrease;
+ totalEVs += evIncrease;
+ SetMonData(mon, MON_DATA_HP_EV + i, &evs[i]);
+ }
+}
+
+u16 GetMonEVCount(struct Pokemon *mon)
+{
+ int i;
+ u16 count = 0;
+
+ for (i = 0; i < NUM_STATS; i++)
+ count += GetMonData(mon, MON_DATA_HP_EV + i, 0);
+
+ return count;
+}
+
+void sub_8043A68(void)
+{
+ u8 foo[4]; // huh?
+}
+
+u8 CheckPartyPokerus(struct Pokemon *party, u8 selection)
+{
+ u8 retVal;
+
+ int partyIndex = 0;
+ unsigned curBit = 1;
+ retVal = 0;
+
+ if (selection)
+ {
+ do
+ {
+ if ((selection & 1) && (GetMonData(&party[partyIndex], MON_DATA_POKERUS, 0) & 0xF))
+ retVal |= curBit;
+ partyIndex++;
+ curBit <<= 1;
+ selection >>= 1;
+ }
+ while (selection);
+ }
+ else if (GetMonData(&party[0], MON_DATA_POKERUS, 0) & 0xF)
+ {
+ retVal = 1;
+ }
+
+ return retVal;
+}
+
+u8 CheckPartyHasHadPokerus(struct Pokemon *party, u8 selection)
+{
+ u8 retVal;
+
+ int partyIndex = 0;
+ unsigned curBit = 1;
+ retVal = 0;
+
+ if (selection)
+ {
+ do
+ {
+ if ((selection & 1) && GetMonData(&party[partyIndex], MON_DATA_POKERUS, 0))
+ retVal |= curBit;
+ partyIndex++;
+ curBit <<= 1;
+ selection >>= 1;
+ }
+ while (selection);
+ }
+ else if (GetMonData(&party[0], MON_DATA_POKERUS, 0))
+ {
+ retVal = 1;
+ }
+
+ return retVal;
+}
+
+void sub_8043B38(void)
+{
+ u8 foo[4]; // huh?
+}
+
+void sub_8043B40(void)
+{
+ u8 foo[4]; // huh?
+}
+
+void sub_8043B48(struct Pokemon *mon, int species, u8 unused, u32 data)
+{
+ if (data > gExperienceTables[gBaseStats[species].growthRate][100])
+ {
+ data = gExperienceTables[gBaseStats[species].growthRate][100];
+ SetMonData(mon, MON_DATA_EXP, &data);
+ }
+}
+
+bool32 sub_8043B90(struct Pokemon *mon)
+{
+ u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
+ u8 level = GetMonData(mon, MON_DATA_LEVEL, NULL);
+ u8 newLevel = level + 1;
+ u32 exp = GetMonData(mon, MON_DATA_EXP, NULL);
+
+ if(level < 100)
+ {
+ if(exp > gExperienceTables[gBaseStats[species].growthRate][newLevel])
+ {
+ SetMonData(mon, MON_DATA_LEVEL, &newLevel);
+ sub_8043B48(mon, species, newLevel, exp);
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+ else
+ {
+ sub_8043B48(mon, species, level, exp);
+ return FALSE;
+ }
+}
+
+u32 CanMonLearnTMHM(struct Pokemon *mon, u8 tm)
+{
+ u16 species = GetMonData(mon, MON_DATA_SPECIES2, 0);
+ if (species == SPECIES_EGG)
+ {
+ return 0;
+ }
+ else if (tm < 32)
+ {
+ u32 mask = 1 << tm;
+ return gTMHMLearnsets[species][0] & mask;
+ }
+ else
+ {
+ u32 mask = 1 << (tm - 32);
+ return gTMHMLearnsets[species][1] & mask;
+ }
+}
+
+u8 GetMoveRelearnerMoves(struct Pokemon *mon, u16 *moves)
+{
+ u16 learnedMoves[4];
+ u8 numMoves = 0;
+ u16 species = GetMonData(mon, MON_DATA_SPECIES, 0);
+ u8 level = GetMonData(mon, MON_DATA_LEVEL, 0);
+ int i, j, k;
+
+ for (i = 0; i < 4; i++)
+ learnedMoves[i] = GetMonData(mon, MON_DATA_MOVE1 + i, 0);
+
+ for (i = 0; i < 20; i++)
+ {
+ u16 moveLevel;
+
+ if (gLevelUpLearnsets[species][i] == 0xFFFF)
+ break;
+
+ moveLevel = gLevelUpLearnsets[species][i] & 0xFE00;
+
+ if (moveLevel <= (level << 9))
+ {
+ for (j = 0; j < 4 && learnedMoves[j] != (gLevelUpLearnsets[species][i] & 0x1FF); j++)
+ ;
+
+ if (j == 4)
+ {
+ for (k = 0; k < numMoves && moves[k] != (gLevelUpLearnsets[species][i] & 0x1FF); k++)
+ ;
+
+ if (k == numMoves)
+ moves[numMoves++] = gLevelUpLearnsets[species][i] & 0x1FF;
+ }
+ }
+ }
+
+ return numMoves;
+}
+
+u8 GetLevelUpMovesBySpecies(u16 species, u16 *moves)
+{
+ u8 numMoves = 0;
+ int i;
+
+ for (i = 0; i < 20 && gLevelUpLearnsets[species][i] != 0xFFFF; i++)
+ moves[numMoves++] = gLevelUpLearnsets[species][i] & 0x1FF;
+
+ return numMoves;
+}
+
+u8 GetNumberOfRelearnableMoves(struct Pokemon *mon)
+{
+ u16 learnedMoves[4];
+ u16 moves[20];
+ u8 numMoves = 0;
+ u16 species = GetMonData(mon, MON_DATA_SPECIES2, 0);
+ u8 level = GetMonData(mon, MON_DATA_LEVEL, 0);
+ int i, j, k;
+
+ if (species == SPECIES_EGG)
+ return 0;
+
+ for (i = 0; i < 4; i++)
+ learnedMoves[i] = GetMonData(mon, MON_DATA_MOVE1 + i, 0);
+
+ for (i = 0; i < 20; i++)
+ {
+ u16 moveLevel;
+
+ if (gLevelUpLearnsets[species][i] == 0xFFFF)
+ break;
+
+ moveLevel = gLevelUpLearnsets[species][i] & 0xFE00;
+
+ if (moveLevel <= (level << 9))
+ {
+ for (j = 0; j < 4 && learnedMoves[j] != (gLevelUpLearnsets[species][i] & 0x1FF); j++)
+ ;
+
+ if (j == 4)
+ {
+ for (k = 0; k < numMoves && moves[k] != (gLevelUpLearnsets[species][i] & 0x1FF); k++)
+ ;
+
+ if (k == numMoves)
+ moves[numMoves++] = gLevelUpLearnsets[species][i] & 0x1FF;
+ }
+ }
+ }
+
+ return numMoves;
+}
+
+// SpeciesToPokedexNum?
+u16 sub_8043F90(u16 species)
+{
+ species = SpeciesToNationalPokedexNum(species);
+
+ if (!sub_806E25C() && species > 151)
+ return 0xFFFF;
+ return species;
+}
+
+void ClearBattleMonForms(void)
+{
+ int i;
+ for(i = 0; i < 4; i++)
+ gBattleMonForms[i] = 0;
+}
+
+u16 GetMUS_ForBattle(void)
+{
+ if(gBattleTypeFlags & 0x1000)
+ return 0x12A;
+ if(gBattleTypeFlags & 0x4000)
+ return 0x10A;
+ if(gBattleTypeFlags & 0x2)
+ return 0x10A;
+ if(gBattleTypeFlags & 0x8)
+ {
+ switch (gTrainers[gTrainerBattleOpponent_A].trainerClass)
+ {
+ case 0x5A:
+ return 0x12B;
+ case 0x54:
+ case 0x57:
+ return 0x128;
+ case 0x53:
+ case 0x55:
+ case 0x56:
+ case 0x58:
+ case 0x59:
+ default:
+ return 0x129;
+ }
+ }
+ return 0x12A;
+}
+
+void PlayBattleBGM(void)
+{
+ ResetMapMusic();
+ m4aMPlayAllStop();
+ PlayBGM(GetMUS_ForBattle());
+}
+
+void PlayMapChosenOrBattleBGM(u16 songId)
+{
+ ResetMapMusic();
+ m4aMPlayAllStop();
+ if (songId)
+ PlayNewMapMusic(songId);
+ else
+ PlayNewMapMusic(GetMUS_ForBattle());
+}
+
+const u8 *GetMonFrontSpritePal(struct Pokemon *mon)
+{
+ u16 species = GetMonData(mon, MON_DATA_SPECIES2, 0);
+ u32 otId = GetMonData(mon, MON_DATA_OT_ID, 0);
+ u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, 0);
+ return GetFrontSpritePalFromSpeciesAndPersonality(species, otId, personality);
+}
+
+const u8 *GetFrontSpritePalFromSpeciesAndPersonality(u16 species, u32 otId, u32 personality)
+{
+ u32 shinyValue;
+
+ if (species > SPECIES_EGG)
+ return gMonPaletteTable[0].data;
+
+ shinyValue = HIHALF(otId) ^ LOHALF(otId) ^ HIHALF(personality) ^ LOHALF(personality);
+ if (shinyValue < 8)
+ return gMonShinyPaletteTable[species].data;
+ else
+ return gMonPaletteTable[species].data;
+}
+
+const struct CompressedSpritePalette *GetMonSpritePalStruct(struct Pokemon *mon)
+{
+ u16 species = GetMonData(mon, MON_DATA_SPECIES2, 0);
+ u32 otId = GetMonData(mon, MON_DATA_OT_ID, 0);
+ u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, 0);
+ return GetMonSpritePalStructFromOtIdPersonality(species, otId, personality);
+}
+
+const struct CompressedSpritePalette *GetMonSpritePalStructFromOtIdPersonality(u16 species, u32 otId , u32 personality)
+{
+ u32 shinyValue;
+
+ shinyValue = HIHALF(otId) ^ LOHALF(otId) ^ HIHALF(personality) ^ LOHALF(personality);
+ if (shinyValue < 8)
+ return &gMonShinyPaletteTable[species];
+ else
+ return &gMonPaletteTable[species];
+}
+
+bool32 IsHMMove2(u16 move)
+{
+ int i = 0;
+ while (sHMMoves[i] != 0xFFFF)
+ {
+ if (sHMMoves[i++] == move)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool8 IsPokeSpriteNotFlipped(u16 species)
+{
+ return gBaseStats[species].noFlip;
+}
+
+s8 GetMonFlavorRelation(struct Pokemon *mon, u8 flavor)
+{
+ u8 nature = GetNature(mon);
+ return gPokeblockFlavorCompatibilityTable[nature * 5 + flavor];
+}
+
+s8 GetFlavorRelationByPersonality(u32 personality, u8 flavor)
+{
+ u8 nature = GetNatureFromPersonality(personality);
+ return gPokeblockFlavorCompatibilityTable[nature * 5 + flavor];
+}
+
+bool8 IsTradedMon(struct Pokemon *mon)
+{
+ u8 otName[7 + 1]; // change PLAYER_NAME_LENGTH to 7
+ u32 otId;
+ GetMonData(mon, MON_DATA_OT_NAME, otName);
+ otId = GetMonData(mon, MON_DATA_OT_ID, 0);
+ return IsOtherTrainer(otId, otName);
+}
+
+bool8 IsOtherTrainer(u32 otId, u8 *otName)
+{
+ if (otId ==
+ (gSaveBlock2Ptr->playerTrainerId[0]
+ | (gSaveBlock2Ptr->playerTrainerId[1] << 8)
+ | (gSaveBlock2Ptr->playerTrainerId[2] << 16)
+ | (gSaveBlock2Ptr->playerTrainerId[3] << 24)))
+ {
+ int i;
+
+ for (i = 0; otName[i] != EOS; i++)
+ if (otName[i] != gSaveBlock2Ptr->playerName[i])
+ return TRUE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void MonRestorePP(struct Pokemon *mon)
+{
+ BoxMonRestorePP(&mon->box);
+}
+
+void BoxMonRestorePP(struct BoxPokemon *boxMon)
+{
+ int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ if (GetBoxMonData(boxMon, MON_DATA_MOVE1 + i, 0))
+ {
+ u16 move = GetBoxMonData(boxMon, MON_DATA_MOVE1 + i, 0);
+ u16 bonus = GetBoxMonData(boxMon, MON_DATA_PP_BONUSES, 0);
+ u8 pp = CalculatePPWithBonus(move, bonus, i);
+ SetBoxMonData(boxMon, MON_DATA_PP1 + i, &pp);
+ }
+ }
+}
+
+// SetMonPreventsSwitchingString
+void sub_8044348(void)
+{
+#ifdef NONMATCHING
+ gLastUsedAbility = gBattleStruct -> abilityPreventingSwitchout; // fixed from the original
+#else
+ gLastUsedAbility = ((u8 *) gBattleStruct)[0xac]; // huh? why is this wrong?
+#endif
+
+ gBattleTextBuff1[0] = B_BUFF_PLACEHOLDER_BEGIN;
+ gBattleTextBuff1[1] = B_BUFF_MON_NICK_WITH_PREFIX;
+ gBattleTextBuff1[2] = gBattleStruct->battlerPreventingSwitchout;
+ gBattleTextBuff1[4] = B_BUFF_EOS;
+
+ if (GetBattlerSide(gBattleStruct->battlerPreventingSwitchout) == B_SIDE_PLAYER)
+ gBattleTextBuff1[3] = pokemon_order_func(gBattlerPartyIndexes[gBattleStruct->battlerPreventingSwitchout]);
+ else
+ gBattleTextBuff1[3] = gBattlerPartyIndexes[gBattleStruct->battlerPreventingSwitchout];
+
+ PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff2, gBattlerInMenuId, pokemon_order_func(gBattlerPartyIndexes[gBattlerInMenuId]))
+
+ BattleStringExpandPlaceholders(gText_PkmnsXPreventsSwitching, gStringVar4);
+}
+
+void SetWildMonHeldItem(void)
+{
+ // TODO: Replace 0x00010000 with the right flag that isnt BATTLE_TYPE_DOME
+ if (!(gBattleTypeFlags & (0x00010000 | BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_TRAINER)))
+ {
+ u16 rnd = Random() % 100;
+ u16 species = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES, 0);
+ if (gBaseStats[species].item1 == gBaseStats[species].item2)
+ {
+ SetMonData(&gEnemyParty[0], MON_DATA_HELD_ITEM, &gBaseStats[species].item1);
+ return;
+ }
+
+ if (rnd > 44)
+ {
+ if (rnd <= 94)
+ SetMonData(&gEnemyParty[0], MON_DATA_HELD_ITEM, &gBaseStats[species].item1);
+ else
+ SetMonData(&gEnemyParty[0], MON_DATA_HELD_ITEM, &gBaseStats[species].item2);
+ }
+ }
+}
+
+bool8 IsMonShiny(struct Pokemon *mon)
+{
+ u32 otId = GetMonData(mon, MON_DATA_OT_ID, 0);
+ u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, 0);
+ return IsShinyOtIdPersonality(otId, personality);
+}
+
+bool8 IsShinyOtIdPersonality(u32 otId, u32 personality)
+{
+ bool8 retVal = FALSE;
+ u32 shinyValue = HIHALF(otId) ^ LOHALF(otId) ^ HIHALF(personality) ^ LOHALF(personality);
+ if (shinyValue < 8)
+ retVal = TRUE;
+ return retVal;
+}
+
+u8 *sub_80444C4(void)
+{
+ u8 id = GetMultiplayerId();
+ return gLinkPlayers[GetBankMultiplayerId(gLinkPlayers[id].id ^ 2)].name;
+}
diff --git a/src/save.c b/src/save.c
new file mode 100644
index 000000000..28f6a2896
--- /dev/null
+++ b/src/save.c
@@ -0,0 +1,919 @@
+#include "global.h"
+#include "save.h"
+#include "decompress.h"
+#include "main.h"
+#include "overworld.h"
+#include "load_save.h"
+#include "task.h"
+#include "link.h"
+#include "gba/flash_internal.h"
+
+#define FILE_SIGNATURE 0x08012025 // signature value to determine if a sector is in use
+
+#define TOTAL_FLASH_SECTORS 32
+
+// Divide save blocks into individual chunks to be written to flash sectors
+
+// Each 4 KiB flash sector contains 3968 bytes of actual data followed by a 128 byte footer
+#define SECTOR_DATA_SIZE 3968
+#define SECTOR_FOOTER_SIZE 128
+
+/*
+ * Sector Layout:
+ *
+ * Sectors 0 - 13: Save Slot 1
+ * Sectors 14 - 27: Save Slot 2
+ * Sectors 28 - 29: Hall of Fame
+ * Sector 30: e-Reader/Mystery Gift Stuff (note: e-Reader is deprecated in Emerald US)
+ * Sector 31: Recorded Battle
+ *
+ * There are two save slots for saving the player's game data. We alternate between
+ * them each time the game is saved, so that if the current save slot is corrupt,
+ * we can load the previous one. We also rotate the sectors in each save slot
+ * so that the same data is not always being written to the same sector. This
+ * might be done to reduce wear on the flash memory, but I'm not sure, since all
+ * 14 sectors get written anyway.
+ */
+
+// (u8 *)structure was removed from the first statement of the macro in Emerald
+// and Fire Red/Leaf Green. This is because malloc is used to allocate addresses
+// so storing the raw addresses should not be done in the offsets information.
+#define SAVEBLOCK_CHUNK(structure, chunkNum) \
+{ \
+ chunkNum * SECTOR_DATA_SIZE, \
+ min(sizeof(structure) - chunkNum * SECTOR_DATA_SIZE, SECTOR_DATA_SIZE) \
+} \
+
+// TODO: use gSaveblock2, gSaveblock1, gPokemonStorage instead of structs
+// Will be done when load_save is decompiled.
+const struct SaveSectionOffsets gSaveSectionOffsets[] =
+{
+ SAVEBLOCK_CHUNK(struct SaveBlock2, 0),
+
+ SAVEBLOCK_CHUNK(struct SaveBlock1, 0),
+ SAVEBLOCK_CHUNK(struct SaveBlock1, 1),
+ SAVEBLOCK_CHUNK(struct SaveBlock1, 2),
+ SAVEBLOCK_CHUNK(struct SaveBlock1, 3),
+
+ SAVEBLOCK_CHUNK(struct PokemonStorage, 0),
+ SAVEBLOCK_CHUNK(struct PokemonStorage, 1),
+ SAVEBLOCK_CHUNK(struct PokemonStorage, 2),
+ SAVEBLOCK_CHUNK(struct PokemonStorage, 3),
+ SAVEBLOCK_CHUNK(struct PokemonStorage, 4),
+ SAVEBLOCK_CHUNK(struct PokemonStorage, 5),
+ SAVEBLOCK_CHUNK(struct PokemonStorage, 6),
+ SAVEBLOCK_CHUNK(struct PokemonStorage, 7),
+ SAVEBLOCK_CHUNK(struct PokemonStorage, 8)
+};
+
+extern void DoSaveFailedScreen(u8 saveType); // save_failed_screen
+extern void sub_800AB9C(void); // link
+extern bool8 sub_800A4BC(void); // link
+extern void sub_80590D8(void); // fieldmap
+extern void sub_804C1C0(void); // load_save
+extern void sav2_gender2_inplace_and_xFE(void); // load_save
+
+// Sector num to begin writing save data. Sectors are rotated each time the game is saved. (possibly to avoid wear on flash memory?)
+u16 gFirstSaveSector;
+u32 gPrevSaveCounter;
+u16 gLastKnownGoodSector;
+u32 gDamagedSaveSectors;
+u32 gSaveCounter;
+struct SaveSection *gFastSaveSection; // the pointer is in fast IWRAM but may sometimes point to the slower EWRAM.
+u16 gUnknown_3005398;
+u16 gSaveUnusedVar;
+u16 gSaveFileStatus;
+void (*gGameContinueCallback)(void);
+struct SaveBlockChunk gRamSaveSectionLocations[0xE];
+u16 gUnknown_3005420;
+
+EWRAM_DATA struct SaveSection gSaveDataBuffer = {0};
+EWRAM_DATA u32 gSaveUnusedVar2 = 0;
+
+void ClearSaveData(void)
+{
+ u16 i;
+
+ for (i = 0; i < NUM_SECTORS; i++)
+ EraseFlashSector(i);
+}
+
+void Save_ResetSaveCounters(void)
+{
+ gSaveCounter = 0;
+ gFirstSaveSector = 0;
+ gDamagedSaveSectors = 0;
+}
+
+bool32 SetSectorDamagedStatus(u8 op, u8 sectorNum)
+{
+ bool32 retVal = FALSE;
+
+ switch (op)
+ {
+ case ENABLE:
+ gDamagedSaveSectors |= (1 << sectorNum);
+ break;
+ case DISABLE:
+ gDamagedSaveSectors &= ~(1 << sectorNum);
+ break;
+ case CHECK: // unused
+ if (gDamagedSaveSectors & (1 << sectorNum))
+ retVal = TRUE;
+ break;
+ }
+
+ return retVal;
+}
+
+// If chunkId is 0xFFFF, this function will write all of the chunks pointed to by 'chunks'.
+// Otherwise, it will write a single chunk with the given 'chunkId'.
+u8 save_write_to_flash(u16 chunkId, const struct SaveBlockChunk *chunks)
+{
+ u32 retVal;
+ u16 i;
+
+ gFastSaveSection = &gSaveDataBuffer;
+
+ if (chunkId != 0xFFFF) // write single chunk
+ {
+ retVal = HandleWriteSector(chunkId, chunks);
+ }
+ else // write all chunks
+ {
+ gLastKnownGoodSector = gFirstSaveSector; // backup the current written sector before attempting to write.
+ gPrevSaveCounter = gSaveCounter;
+ gFirstSaveSector++;
+ gFirstSaveSector %= NUM_SECTORS_PER_SAVE_SLOT; // array count save sector locations
+ gSaveCounter++;
+ retVal = SAVE_STATUS_OK;
+
+ for (i = 0; i < NUM_SECTORS_PER_SAVE_SLOT; i++)
+ HandleWriteSector(i, chunks);
+
+ // Check for any bad sectors
+ if (gDamagedSaveSectors != 0) // skip the damaged sector.
+ {
+ retVal = SAVE_STATUS_ERROR;
+ gFirstSaveSector = gLastKnownGoodSector;
+ gSaveCounter = gPrevSaveCounter;
+ }
+ }
+
+ return retVal;
+}
+
+u8 HandleWriteSector(u16 chunkId, const struct SaveBlockChunk *chunks)
+{
+ u16 i;
+ u16 sectorNum;
+ u8 *chunkData;
+ u16 chunkSize;
+
+ // select sector number
+ sectorNum = chunkId + gFirstSaveSector;
+ sectorNum %= NUM_SECTORS_PER_SAVE_SLOT;
+ // select save slot
+ sectorNum += NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2);
+
+ chunkData = chunks[chunkId].data;
+ chunkSize = chunks[chunkId].size;
+
+ // clear save section.
+ for (i = 0; i < sizeof(struct SaveSection); i++)
+ ((char *)gFastSaveSection)[i] = 0;
+
+ gFastSaveSection->id = chunkId;
+ gFastSaveSection->signature = FILE_SIGNATURE;
+ gFastSaveSection->counter = gSaveCounter;
+
+ for (i = 0; i < chunkSize; i++)
+ gFastSaveSection->data[i] = chunkData[i];
+
+ gFastSaveSection->checksum = CalculateChecksum(chunkData, chunkSize);
+ return TryWriteSector(sectorNum, gFastSaveSection->data);
+}
+
+u8 HandleWriteSectorNBytes(u8 sector, u8 *data, u16 size)
+{
+ u16 i;
+ struct SaveSection *section = &gSaveDataBuffer;
+
+ for (i = 0; i < sizeof(struct SaveSection); i++)
+ ((char *)section)[i] = 0;
+
+ section->signature = FILE_SIGNATURE;
+
+ for (i = 0; i < size; i++)
+ section->data[i] = data[i];
+
+ section->id = CalculateChecksum(data, size); // though this appears to be incorrect, it might be some sector checksum instead of a whole save checksum and only appears to be relevent to HOF data, if used.
+ return TryWriteSector(sector, section->data);
+}
+
+u8 TryWriteSector(u8 sectorNum, u8 *data)
+{
+ if (ProgramFlashSectorAndVerify(sectorNum, data) != 0) // is damaged?
+ {
+ SetSectorDamagedStatus(ENABLE, sectorNum); // set damaged sector bits.
+ return SAVE_STATUS_ERROR;
+ }
+ else
+ {
+ SetSectorDamagedStatus(DISABLE, sectorNum); // unset damaged sector bits. it's safe now.
+ return SAVE_STATUS_OK;
+ }
+}
+
+u32 RestoreSaveBackupVarsAndIncrement(const struct SaveBlockChunk *chunk) // chunk is unused
+{
+ gFastSaveSection = &gSaveDataBuffer;
+ gLastKnownGoodSector = gFirstSaveSector;
+ gPrevSaveCounter = gSaveCounter;
+ gFirstSaveSector++;
+ gFirstSaveSector %= NUM_SECTORS_PER_SAVE_SLOT;
+ gSaveCounter++;
+ gUnknown_3005398 = 0;
+ gDamagedSaveSectors = 0;
+ return 0;
+}
+
+u32 RestoreSaveBackupVars(const struct SaveBlockChunk *chunk) // chunk is unused
+{
+ gFastSaveSection = &gSaveDataBuffer;
+ gLastKnownGoodSector = gFirstSaveSector;
+ gPrevSaveCounter = gSaveCounter;
+ gUnknown_3005398 = 0;
+ gDamagedSaveSectors = 0;
+ return 0;
+}
+
+u8 sub_80D9AA4(u16 a1, const struct SaveBlockChunk *chunk)
+{
+ u8 retVal;
+
+ if (gUnknown_3005398 < a1 - 1)
+ {
+ retVal = SAVE_STATUS_OK;
+ HandleWriteSector(gUnknown_3005398, chunk);
+ gUnknown_3005398++;
+ if (gDamagedSaveSectors)
+ {
+ retVal = SAVE_STATUS_ERROR;
+ gFirstSaveSector = gLastKnownGoodSector;
+ gSaveCounter = gPrevSaveCounter;
+ }
+ }
+ else
+ {
+ retVal = SAVE_STATUS_ERROR;
+ }
+
+ return retVal;
+}
+
+u8 sub_80D9B04(u16 a1, const struct SaveBlockChunk *chunk)
+{
+ u8 retVal = SAVE_STATUS_OK;
+
+ ClearSaveData_2(a1 - 1, chunk);
+
+ if (gDamagedSaveSectors)
+ {
+ retVal = SAVE_STATUS_ERROR;
+ gFirstSaveSector = gLastKnownGoodSector;
+ gSaveCounter = gPrevSaveCounter;
+ }
+ return retVal;
+}
+
+u8 ClearSaveData_2(u16 chunkId, const struct SaveBlockChunk *chunks)
+{
+ u16 i;
+ u16 sector;
+ u8 *data;
+ u16 size;
+ u8 status;
+
+ // select sector number
+ sector = chunkId + gFirstSaveSector;
+ sector %= NUM_SECTORS_PER_SAVE_SLOT;
+ // select save slot
+ sector += NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2);
+
+ data = chunks[chunkId].data;
+ size = chunks[chunkId].size;
+
+ // clear temp save section.
+ for (i = 0; i < sizeof(struct SaveSection); i++)
+ ((char *)gFastSaveSection)[i] = 0;
+
+ gFastSaveSection->id = chunkId;
+ gFastSaveSection->signature = FILE_SIGNATURE;
+ gFastSaveSection->counter = gSaveCounter;
+
+ // set temp section's data.
+ for (i = 0; i < size; i++)
+ gFastSaveSection->data[i] = data[i];
+
+ // calculate checksum.
+ gFastSaveSection->checksum = CalculateChecksum(data, size);
+
+ EraseFlashSector(sector);
+
+ status = SAVE_STATUS_OK;
+
+ for (i = 0; i < sizeof(struct UnkSaveSection); i++)
+ {
+ if (ProgramFlashByte(sector, i, gFastSaveSection->data[i]))
+ {
+ status = SAVE_STATUS_ERROR;
+ break;
+ }
+ }
+
+ if (status == SAVE_STATUS_ERROR)
+ {
+ SetSectorDamagedStatus(ENABLE, sector);
+ return SAVE_STATUS_ERROR;
+ }
+ else
+ {
+ status = SAVE_STATUS_OK;
+
+ for (i = 0; i < 7; i++)
+ {
+ if (ProgramFlashByte(sector, 0xFF9 + i, ((u8 *)gFastSaveSection)[0xFF9 + i]))
+ {
+ status = SAVE_STATUS_ERROR;
+ break;
+ }
+ }
+
+ if (status == SAVE_STATUS_ERROR)
+ {
+ SetSectorDamagedStatus(ENABLE, sector);
+ return SAVE_STATUS_ERROR;
+ }
+ else
+ {
+ SetSectorDamagedStatus(DISABLE, sector);
+ return SAVE_STATUS_OK;
+ }
+ }
+}
+
+u8 sav12_xor_get(u16 a1, const struct SaveBlockChunk *chunk)
+{
+ u16 sector;
+
+ // select sector number
+ sector = a1 + gFirstSaveSector - 1;
+ sector %= NUM_SECTORS_PER_SAVE_SLOT;
+ // select save slot
+ sector += NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2);
+
+ if (ProgramFlashByte(sector, sizeof(struct UnkSaveSection), ((u8 *)gFastSaveSection)[sizeof(struct UnkSaveSection)]))
+ {
+ // sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter.
+ SetSectorDamagedStatus(ENABLE, sector);
+ gFirstSaveSector = gLastKnownGoodSector;
+ gSaveCounter = gPrevSaveCounter;
+ return SAVE_STATUS_ERROR;
+ }
+ else
+ {
+ SetSectorDamagedStatus(DISABLE, sector);
+ return SAVE_STATUS_OK;
+ }
+}
+
+u8 sub_80D9D88(u16 a1, const struct SaveBlockChunk *chunk)
+{
+ u16 sector;
+
+ sector = a1 + gFirstSaveSector - 1;
+ sector %= NUM_SECTORS_PER_SAVE_SLOT;
+ sector += NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2);
+
+ if (ProgramFlashByte(sector, sizeof(struct UnkSaveSection), 0x25))
+ {
+ // sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter.
+ SetSectorDamagedStatus(ENABLE, sector);
+ gFirstSaveSector = gLastKnownGoodSector;
+ gSaveCounter = gPrevSaveCounter;
+ return SAVE_STATUS_ERROR;
+ }
+ else
+ {
+ SetSectorDamagedStatus(DISABLE, sector);
+ return SAVE_STATUS_OK;
+ }
+}
+
+u8 sub_80D9E14(u16 a1, const struct SaveBlockChunk *chunk)
+{
+ u8 retVal;
+ gFastSaveSection = &gSaveDataBuffer;
+ if (a1 != 0xFFFF)
+ {
+ retVal = SAVE_STATUS_ERROR;
+ }
+ else
+ {
+ retVal = GetSaveValidStatus(chunk);
+ sub_80D9E54(0xFFFF, chunk);
+ }
+
+ return retVal;
+}
+
+u8 sub_80D9E54(u16 a1, const struct SaveBlockChunk *chunks)
+{
+ u16 i;
+ u16 checksum;
+ u16 sector = NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2);
+ u16 id;
+
+ for (i = 0; i < NUM_SECTORS_PER_SAVE_SLOT; i++)
+ {
+ DoReadFlashWholeSection(i + sector, gFastSaveSection);
+ id = gFastSaveSection->id;
+ if (id == 0)
+ gFirstSaveSector = i;
+ checksum = CalculateChecksum(gFastSaveSection->data, chunks[id].size);
+ if (gFastSaveSection->signature == FILE_SIGNATURE
+ && gFastSaveSection->checksum == checksum)
+ {
+ u16 j;
+ for (j = 0; j < chunks[id].size; j++)
+ chunks[id].data[j] = gFastSaveSection->data[j];
+ }
+ }
+
+ return 1;
+}
+
+u8 GetSaveValidStatus(const struct SaveBlockChunk *chunks)
+{
+ u16 sector;
+ bool8 signatureValid;
+ u16 checksum;
+ u32 slot1saveCounter = 0;
+ u32 slot2saveCounter = 0;
+ u8 slot1Status;
+ u8 slot2Status;
+ u32 validSectors;
+ const u32 ALL_SECTORS = (1 << NUM_SECTORS_PER_SAVE_SLOT) - 1; // bitmask of all saveblock sectors
+
+ // check save slot 1.
+ validSectors = 0;
+ signatureValid = FALSE;
+ for (sector = 0; sector < NUM_SECTORS_PER_SAVE_SLOT; sector++)
+ {
+ DoReadFlashWholeSection(sector, gFastSaveSection);
+ if (gFastSaveSection->signature == FILE_SIGNATURE)
+ {
+ signatureValid = TRUE;
+ checksum = CalculateChecksum(gFastSaveSection->data, chunks[gFastSaveSection->id].size);
+ if (gFastSaveSection->checksum == checksum)
+ {
+ slot1saveCounter = gFastSaveSection->counter;
+ validSectors |= 1 << gFastSaveSection->id;
+ }
+ }
+ }
+
+ if (signatureValid)
+ {
+ if (validSectors == ALL_SECTORS)
+ slot1Status = SAVE_STATUS_OK;
+ else
+ slot1Status = SAVE_STATUS_ERROR;
+ }
+ else
+ {
+ slot1Status = SAVE_STATUS_EMPTY;
+ }
+
+ // check save slot 2.
+ validSectors = 0;
+ signatureValid = FALSE;
+ for (sector = 0; sector < NUM_SECTORS_PER_SAVE_SLOT; sector++)
+ {
+ DoReadFlashWholeSection(NUM_SECTORS_PER_SAVE_SLOT + sector, gFastSaveSection);
+ if (gFastSaveSection->signature == FILE_SIGNATURE)
+ {
+ signatureValid = TRUE;
+ checksum = CalculateChecksum(gFastSaveSection->data, chunks[gFastSaveSection->id].size);
+ if (gFastSaveSection->checksum == checksum)
+ {
+ slot2saveCounter = gFastSaveSection->counter;
+ validSectors |= 1 << gFastSaveSection->id;
+ }
+ }
+ }
+
+ if (signatureValid)
+ {
+ if (validSectors == ALL_SECTORS)
+ slot2Status = SAVE_STATUS_OK;
+ else
+ slot2Status = SAVE_STATUS_ERROR;
+ }
+ else
+ {
+ slot2Status = SAVE_STATUS_EMPTY;
+ }
+
+ if (slot1Status == SAVE_STATUS_OK && slot2Status == SAVE_STATUS_OK)
+ {
+ // Choose counter of the most recent save file
+ if ((slot1saveCounter == -1 && slot2saveCounter == 0) || (slot1saveCounter == 0 && slot2saveCounter == -1))
+ {
+ if ((unsigned)(slot1saveCounter + 1) < (unsigned)(slot2saveCounter + 1))
+ gSaveCounter = slot2saveCounter;
+ else
+ gSaveCounter = slot1saveCounter;
+ }
+ else
+ {
+ if (slot1saveCounter < slot2saveCounter)
+ gSaveCounter = slot2saveCounter;
+ else
+ gSaveCounter = slot1saveCounter;
+ }
+ return SAVE_STATUS_OK;
+ }
+
+ if (slot1Status == SAVE_STATUS_OK)
+ {
+ gSaveCounter = slot1saveCounter;
+ if (slot2Status == SAVE_STATUS_ERROR)
+ return SAVE_STATUS_ERROR;
+ else
+ return SAVE_STATUS_OK;
+ }
+
+ if (slot2Status == SAVE_STATUS_OK)
+ {
+ gSaveCounter = slot2saveCounter;
+ if (slot1Status == SAVE_STATUS_ERROR)
+ return SAVE_STATUS_ERROR;
+ else
+ return SAVE_STATUS_OK;
+ }
+
+ if (slot1Status == SAVE_STATUS_EMPTY && slot2Status == SAVE_STATUS_EMPTY)
+ {
+ gSaveCounter = 0;
+ gFirstSaveSector = 0;
+ return SAVE_STATUS_EMPTY;
+ }
+
+ gSaveCounter = 0;
+ gFirstSaveSector = 0;
+ return 2;
+}
+
+u8 sub_80DA120(u8 sector, u8 *data, u16 size)
+{
+ u16 i;
+ struct SaveSection *section = &gSaveDataBuffer;
+
+ DoReadFlashWholeSection(sector, section);
+ if (section->signature == FILE_SIGNATURE)
+ {
+ u16 checksum = CalculateChecksum(section->data, size);
+ if (section->id == checksum)
+ {
+ for (i = 0; i < size; i++)
+ data[i] = section->data[i];
+ return SAVE_STATUS_OK;
+ }
+ else
+ {
+ return SAVE_STATUS_INVALID;
+ }
+ }
+ else
+ {
+ return SAVE_STATUS_EMPTY;
+ }
+}
+
+u8 DoReadFlashWholeSection(u8 sector, struct SaveSection *section)
+{
+ ReadFlash(sector, 0, section->data, sizeof(struct SaveSection));
+ return 1;
+}
+
+u16 CalculateChecksum(void *data, u16 size)
+{
+ u16 i;
+ u32 checksum = 0;
+
+ for (i = 0; i < (size / 4); i++)
+ checksum += *((u32 *)data)++;
+
+ return ((checksum >> 16) + checksum);
+}
+
+void UpdateSaveAddresses(void)
+{
+ int i = 0;
+
+ gRamSaveSectionLocations[i].data = (void*)(gSaveBlock2Ptr) + gSaveSectionOffsets[i].toAdd;
+ gRamSaveSectionLocations[i].size = gSaveSectionOffsets[i].size;
+
+ for (i = 1; i < 5; i++)
+ {
+ gRamSaveSectionLocations[i].data = (void*)(gSaveBlock1Ptr) + gSaveSectionOffsets[i].toAdd;
+ gRamSaveSectionLocations[i].size = gSaveSectionOffsets[i].size;
+ }
+
+ for (i = 5; i < 14; i++)
+ {
+ gRamSaveSectionLocations[i].data = (void*)(gPokemonStoragePtr) + gSaveSectionOffsets[i].toAdd;
+ gRamSaveSectionLocations[i].size = gSaveSectionOffsets[i].size;
+
+ i++;i--; // needed to match
+ }
+}
+
+u8 HandleSavingData(u8 saveType)
+{
+ u8 i;
+ u32 *backupPtr = gMain.vblankCounter1;
+ u8 *tempAddr;
+
+ gMain.vblankCounter1 = NULL;
+ UpdateSaveAddresses();
+ switch (saveType)
+ {
+ case SAVE_HALL_OF_FAME_ERASE_BEFORE: // deletes HOF before overwriting HOF completely. unused
+ for (i = 0xE * 2 + 0; i < 32; i++)
+ EraseFlashSector(i);
+ // fallthrough
+ case SAVE_HALL_OF_FAME: // hall of fame.
+ if (GetGameStat(GAME_STAT_ENTERED_HOF) < 999)
+ IncrementGameStat(GAME_STAT_ENTERED_HOF);
+ tempAddr = gDecompressionBuffer;
+ HandleWriteSectorNBytes(0x1C, tempAddr, 0xF80);
+ HandleWriteSectorNBytes(0x1D, tempAddr + 0xF80, 0xF80);
+ // fallthrough
+ case SAVE_NORMAL: // normal save. also called by overwriting your own save.
+ default:
+ SaveSerializedGame();
+ save_write_to_flash(0xFFFF, gRamSaveSectionLocations);
+ break;
+ case SAVE_LINK: // _081532C4
+ SaveSerializedGame();
+ for(i = 0; i < 5; i++)
+ save_write_to_flash(i, gRamSaveSectionLocations);
+ break;
+ case EREADER_SAVE:
+ SaveSerializedGame();
+ save_write_to_flash(0, gRamSaveSectionLocations);
+ break;
+ case SAVE_OVERWRITE_DIFFERENT_FILE:
+ for (i = (0xE * 2 + 0); i < 32; i++)
+ EraseFlashSector(i); // erase HOF.
+ SaveSerializedGame();
+ save_write_to_flash(0xFFFF, gRamSaveSectionLocations);
+ break;
+ }
+ gMain.vblankCounter1 = backupPtr;
+ return 0;
+}
+
+u8 TrySavingData(u8 saveType)
+{
+ if(gFlashMemoryPresent == TRUE)
+ {
+ HandleSavingData(saveType);
+ if(gDamagedSaveSectors)
+ DoSaveFailedScreen(saveType);
+ else
+ goto OK; // really?
+ }
+ gUnknown_3005420 = 0xFF;
+ return 0xFF;
+
+OK:
+ gUnknown_3005420 = 1;
+ return 1;
+}
+
+u8 sub_80DA3AC(void)
+{
+ if (gFlashMemoryPresent != TRUE)
+ return 1;
+ UpdateSaveAddresses();
+ SaveSerializedGame();
+ RestoreSaveBackupVarsAndIncrement(gRamSaveSectionLocations);
+ return 0;
+}
+
+bool8 sub_80DA3D8(void)
+{
+ u8 retVal = sub_80D9AA4(0xE, gRamSaveSectionLocations);
+ if (gDamagedSaveSectors)
+ DoSaveFailedScreen(0);
+ if (retVal == 0xFF)
+ return 1;
+ else
+ return 0;
+}
+
+u8 sub_80DA40C(void)
+{
+ sub_80D9B04(0xE, gRamSaveSectionLocations);
+ if (gDamagedSaveSectors)
+ DoSaveFailedScreen(0);
+ return 0;
+}
+
+u8 sub_80DA434(void)
+{
+ sav12_xor_get(0xE, gRamSaveSectionLocations);
+ if (gDamagedSaveSectors)
+ DoSaveFailedScreen(0);
+ return 0;
+}
+
+u8 sub_80DA45C(void)
+{
+ if (gFlashMemoryPresent != TRUE)
+ return 1;
+
+ UpdateSaveAddresses();
+ SaveSerializedGame();
+ RestoreSaveBackupVars(gRamSaveSectionLocations);
+ sub_80D9B04(gUnknown_3005398 + 1, gRamSaveSectionLocations);
+ return 0;
+}
+
+bool8 sub_80DA4A0(void)
+{
+ u8 retVal = FALSE;
+ u16 val = ++gUnknown_3005398;
+ if (val <= 4)
+ {
+ sub_80D9B04(gUnknown_3005398 + 1, gRamSaveSectionLocations);
+ sub_80D9D88(val, gRamSaveSectionLocations);
+ }
+ else
+ {
+ sub_80D9D88(val, gRamSaveSectionLocations);
+ retVal = TRUE;
+ }
+ if (gDamagedSaveSectors)
+ DoSaveFailedScreen(1);
+ return retVal;
+}
+
+u8 Save_LoadGameData(u8 a1)
+{
+ u8 result;
+
+ if (gFlashMemoryPresent != TRUE)
+ {
+ gSaveFileStatus = 4;
+ return 0xFF;
+ }
+
+ UpdateSaveAddresses();
+ switch (a1)
+ {
+ case 0:
+ default:
+ result = sub_80D9E14(0xFFFF, gRamSaveSectionLocations);
+ LoadSerializedGame();
+ gSaveFileStatus = result;
+ gGameContinueCallback = 0;
+ break;
+ case 3:
+ result = sub_80DA120(0x1C, gDecompressionBuffer, 0xF80);
+ if(result == 1)
+ result = sub_80DA120(0x1D, gDecompressionBuffer + 0xF80, 0xF80);
+ break;
+ }
+
+ return result;
+}
+
+u32 TryCopySpecialSaveSection(u8 sector, u8* dst)
+{
+ s32 i;
+ s32 size;
+ u8* savData;
+
+ if (sector != 30 && sector != 31)
+ return 0xFF;
+ ReadFlash(sector, 0, (u8 *)&gSaveDataBuffer, sizeof(struct SaveSection));
+ if (*(u32*)(&gSaveDataBuffer.data[0]) != 0xB39D)
+ return 0xFF;
+ // copies whole save section except u32 counter
+ i = 0;
+ size = 0xFFB;
+ savData = &gSaveDataBuffer.data[4];
+ for (; i <= size; i++)
+ dst[i] = savData[i];
+ return 1;
+}
+
+u32 sub_80DA5E0(u8 sector, u8* src)
+{
+ s32 i;
+ s32 size;
+ u8* savData;
+ void* savDataBuffer;
+
+ if (sector != 30 && sector != 31)
+ return 0xFF;
+
+ savDataBuffer = &gSaveDataBuffer;
+ *(u32*)(savDataBuffer) = 0xB39D;
+
+ // copies whole save section except u32 counter
+ i = 0;
+ size = 0xFFB;
+ savData = &gSaveDataBuffer.data[4];
+ for (; i <= size; i++)
+ savData[i] = src[i];
+ if (ProgramFlashSectorAndVerify(sector, savDataBuffer) != 0)
+ return 0xFF;
+ return 1;
+}
+
+void sub_80DA634(u8 taskId)
+{
+ switch (gTasks[taskId].data[0])
+ {
+ case 0:
+ gSoftResetDisabled = TRUE;
+ gTasks[taskId].data[0] = 1;
+ break;
+ case 1:
+ sub_800AB9C();
+ gTasks[taskId].data[0] = 2;
+ break;
+ case 2:
+ if (sub_800A4BC())
+ {
+ sub_80590D8();
+ gTasks[taskId].data[0] = 3;
+ }
+ break;
+ case 3:
+ sub_804C1C0();
+ sub_80DA3AC();
+ gTasks[taskId].data[0] = 4;
+ break;
+ case 4:
+ if (++gTasks[taskId].data[1] == 5)
+ {
+ gTasks[taskId].data[1] = 0;
+ gTasks[taskId].data[0] = 5;
+ }
+ break;
+ case 5:
+ if (sub_80DA3D8())
+ gTasks[taskId].data[0] = 6;
+ else
+ gTasks[taskId].data[0] = 4;
+ break;
+ case 6:
+ sub_80DA40C();
+ gTasks[taskId].data[0] = 7;
+ break;
+ case 7:
+ sav2_gender2_inplace_and_xFE();
+ sub_800AB9C();
+ gTasks[taskId].data[0] = 8;
+ break;
+ case 8:
+ if (sub_800A4BC())
+ {
+ sub_80DA434();
+ gTasks[taskId].data[0] = 9;
+ }
+ break;
+ case 9:
+ sub_800AB9C();
+ gTasks[taskId].data[0] = 10;
+ break;
+ case 10:
+ if (sub_800A4BC())
+ gTasks[taskId].data[0]++;
+ break;
+ case 11:
+ if (++gTasks[taskId].data[1] > 5)
+ {
+ gSoftResetDisabled = FALSE;
+ DestroyTask(taskId);
+ }
+ break;
+ }
+}
diff --git a/src/vs_seeker.c b/src/vs_seeker.c
index 75534d308..17a13ab01 100644
--- a/src/vs_seeker.c
+++ b/src/vs_seeker.c
@@ -64,7 +64,7 @@ struct VsSeekerStruct
};
extern u16 gUnknown_20370D2;
-extern struct MapObject gUnknown_2036E38[MAP_OBJECTS_COUNT];
+extern struct MapObject gMapObjects[MAP_OBJECTS_COUNT];
extern u8 gUnknown_3005074;
// static declarations
@@ -642,8 +642,8 @@ static void GatherNearbyTrainerInfo(void)
sVsSeeker->trainerInfo[vsSeekerObjectIdx].localId = templates[mapObjectIdx].localId;
TryGetFieldObjectIdByLocalIdAndMap(templates[mapObjectIdx].localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &fieldObjectId);
sVsSeeker->trainerInfo[vsSeekerObjectIdx].fieldObjectId = fieldObjectId;
- sVsSeeker->trainerInfo[vsSeekerObjectIdx].xCoord = gUnknown_2036E38[fieldObjectId].coords2.x - 7;
- sVsSeeker->trainerInfo[vsSeekerObjectIdx].yCoord = gUnknown_2036E38[fieldObjectId].coords2.y - 7;
+ sVsSeeker->trainerInfo[vsSeekerObjectIdx].xCoord = gMapObjects[fieldObjectId].coords2.x - 7;
+ sVsSeeker->trainerInfo[vsSeekerObjectIdx].yCoord = gMapObjects[fieldObjectId].coords2.y - 7;
sVsSeeker->trainerInfo[vsSeekerObjectIdx].graphicsId = templates[mapObjectIdx].graphicsId;
vsSeekerObjectIdx++;
}
@@ -731,7 +731,7 @@ static u8 GetVsSeekerResponseInArea(const VsSeekerData * a0)
else
{
gSaveBlock1Ptr->trainerRematches[sVsSeeker->trainerInfo[vsSeekerIdx].localId] = r7;
- npc_coords_shift_still(&gUnknown_2036E38[sVsSeeker->trainerInfo[vsSeekerIdx].fieldObjectId]);
+ npc_coords_shift_still(&gMapObjects[sVsSeeker->trainerInfo[vsSeekerIdx].fieldObjectId]);
StartTrainerObjectMovementScript(&sVsSeeker->trainerInfo[vsSeekerIdx], gUnknown_8453F64);
sVsSeeker->trainerIdxArray[sVsSeeker->numRematchableTrainers] = r8;
sVsSeeker->runningBehaviourEtcArray[sVsSeeker->numRematchableTrainers] = GetRunningBehaviorFromGraphicsId(sVsSeeker->trainerInfo[vsSeekerIdx].graphicsId);
@@ -891,7 +891,7 @@ static u8 GetVsSeekerResponseInArea(const VsSeekerData * a0)
"\tlsls r0, r1, 3\n"
"\tadds r0, r1\n"
"\tlsls r0, 2\n"
- "\tldr r1, _0810CB5C @ =gUnknown_2036E38\n"
+ "\tldr r1, _0810CB5C @ =gMapObjects\n"
"\tadds r0, r1\n"
"\tbl npc_coords_shift_still\n"
"\tldr r0, [r6]\n"
@@ -965,7 +965,7 @@ static u8 GetVsSeekerResponseInArea(const VsSeekerData * a0)
"\t.align 2, 0\n"
"_0810CB54: .4byte gSaveBlock1Ptr\n"
"_0810CB58: .4byte 0x0000063a\n"
- "_0810CB5C: .4byte gUnknown_2036E38\n"
+ "_0810CB5C: .4byte gMapObjects\n"
"_0810CB60: .4byte gUnknown_8453F64\n"
"_0810CB64: .4byte 0x00000431\n"
"_0810CB68: .4byte sVsSeeker\n"
@@ -1008,7 +1008,7 @@ void sub_810CB90(void)
struct MapObject *r4_2;
TryGetFieldObjectIdByLocalIdAndMap(r4[r8].localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &sp0);
- r4_2 = &gUnknown_2036E38[sp0];
+ r4_2 = &gMapObjects[sp0];
sub_810CF54(&r4[r8]); // You are using this function incorrectly. Please consult the manual.
sub_805FE7C(r4_2, gUnknown_8453F67[r4_2->mapobj_unk_18]);
gSaveBlock1Ptr->trainerRematches[r4[r8].localId] = 0;
@@ -1155,7 +1155,7 @@ static bool8 sub_810CED0(const VsSeekerData * a0, u16 a1)
bool8 sub_810CF04(u8 a0)
{
- struct MapObject *r1 = &gUnknown_2036E38[a0];
+ struct MapObject *r1 = &gMapObjects[a0];
if (r1->active && gMapHeader.events->mapObjectCount >= r1->localId && gSprites[r1->spriteId].data[0] == a0)
return TRUE;
@@ -1331,7 +1331,7 @@ static u8 GetRematchableTrainerLocalId(void)
static void StartTrainerObjectMovementScript(struct VsSeekerTrainerInfo * trainerInfo, const u8 * script)
{
- npc_sync_anim_pause_bits(&gUnknown_2036E38[trainerInfo->fieldObjectId]);
+ npc_sync_anim_pause_bits(&gMapObjects[trainerInfo->fieldObjectId]);
ScriptMovement_StartObjectMovementScript(trainerInfo->localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, script);
}
@@ -1367,7 +1367,7 @@ static void StartAllRespondantIdleMovements(void)
{
if (sVsSeeker->trainerInfo[j].trainerIdx == sVsSeeker->trainerIdxArray[i])
{
- struct MapObject *r4 = &gUnknown_2036E38[sVsSeeker->trainerInfo[j].fieldObjectId];
+ struct MapObject *r4 = &gMapObjects[sVsSeeker->trainerInfo[j].fieldObjectId];
if (sub_810CF04(sVsSeeker->trainerInfo[j].fieldObjectId) == 1)
npc_set_running_behaviour_etc(r4, sVsSeeker->runningBehaviourEtcArray[i]);