diff options
author | Slawter666 <38655737+Slawter666@users.noreply.github.com> | 2018-11-12 14:14:50 +0000 |
---|---|---|
committer | Slawter666 <38655737+Slawter666@users.noreply.github.com> | 2018-11-12 14:14:50 +0000 |
commit | 1b895ff52c011d51a9516791148cd5088ba411b5 (patch) | |
tree | 3f1dee86f96098d38131426e11a0f9c39a1244fe /src/battle_tower.c | |
parent | be33878b94cc38913447682d3e34e674df68619f (diff) | |
parent | 65f053fd89e09b13e407ac53488043b728660e6e (diff) |
Merge branch 'master' of https://github.com/pret/pokeemerald into synchronise
Diffstat (limited to 'src/battle_tower.c')
-rw-r--r-- | src/battle_tower.c | 2795 |
1 files changed, 2780 insertions, 15 deletions
diff --git a/src/battle_tower.c b/src/battle_tower.c index e70d4af91..e95eaf156 100644 --- a/src/battle_tower.c +++ b/src/battle_tower.c @@ -1,19 +1,200 @@ #include "global.h" +#include "battle_tower.h" +#include "apprentice.h" #include "event_data.h" #include "battle_setup.h" #include "overworld.h" #include "random.h" -#include "battle_tower.h" +#include "text.h" +#include "main.h" +#include "international_string_util.h" +#include "battle.h" +#include "frontier_util.h" +#include "recorded_battle.h" +#include "easy_chat.h" +#include "gym_leader_rematch.h" +#include "battle_transition.h" +#include "trainer_see.h" +#include "new_game.h" +#include "string_util.h" +#include "data2.h" +#include "link.h" +#include "field_message_box.h" +#include "tv.h" +#include "constants/battle_frontier.h" +#include "constants/trainers.h" +#include "constants/event_objects.h" +#include "constants/moves.h" +#include "constants/species.h" + +extern u16 gUnknown_03006298[]; + +extern void sub_81A6CD0(void); +extern void sub_81A4C30(void); +extern u8 sub_81A6CA8(u8, u8); +extern void SetMonMoveAvoidReturn(struct Pokemon *mon, u16 move, u8 moveSlot); -extern void sub_81A3ACC(void); +extern const u8 *const *const gUnknown_085DD690[]; +extern const u16 gBattleFrontierHeldItems[]; +extern const u8 sRubyFacilityClassToEmerald[82][2]; +extern const u16 gUnknown_085DFA46[]; +extern const struct FacilityMon gBattleFrontierMons[]; +extern const struct FacilityMon gSlateportBattleTentMons[]; +extern const struct FacilityMon gVerdanturfBattleTentMons[]; +extern const struct FacilityMon gFallarborBattleTentMons[]; +extern const struct BattleFrontierTrainer gBattleFrontierTrainers[]; +extern const struct BattleFrontierTrainer gSlateportBattleTentTrainers[]; +extern const struct BattleFrontierTrainer gVerdanturfBattleTentTrainers[]; +extern const struct BattleFrontierTrainer gFallarborBattleTentTrainers[]; -extern const u32 gUnknown_085DF9AC[][2]; -extern const u32 gUnknown_085DF9CC[][2]; -extern void (* const gUnknown_085DF96C[])(void); +struct +{ + u32 facilityClass; + const u8 *const *strings; +} extern const gUnknown_085DD500[50]; + +struct +{ + u16 species; + u8 fixedIV; + u8 level; + u8 nature; + u8 evs[6]; + u16 moves[4]; +} extern const sStevenMons[3]; + +extern const u8 MossdeepCity_SpaceCenter_2F_EventScript_224157[]; +extern const u8 MossdeepCity_SpaceCenter_2F_EventScript_224166[]; + +// EWRAM vars. +EWRAM_DATA const struct BattleFrontierTrainer *gFacilityTrainers = NULL; +EWRAM_DATA const struct FacilityMon *gFacilityTrainerMons = NULL; // This file's functions. -void sub_8164ED8(void); -u16 sub_8164FCC(u8, u8); +static void sub_8161F94(void); +static void sub_8162054(void); +static void sub_81620F4(void); +static void ChooseNextBattleTowerTrainer(void); +static void sub_81621C0(void); +static void AwardBattleTowerRibbons(void); +static void SaveBattleTowerProgress(void); +static void sub_8163914(void); +static void nullsub_61(void); +static void nullsub_116(void); +static void sub_81642A0(void); +static void sub_8164828(void); +static void sub_8164B74(void); +static void sub_8164DCC(void); +static void sub_8164DE4(void); +static void sub_8164E04(void); +static void ValidateBattleTowerRecordChecksums(void); +static void SaveCurrentWinStreak(void); +static void ValidateApprenticesChecksums(void); +static void sub_8165E18(void); +static void CopyEReaderTrainerFarewellMessage(void); +static void ClearBattleTowerRecord(struct EmeraldBattleTowerRecord *record); +static void FillTrainerParty(u16 trainerId, u8 firstMonId, u8 monCount); +static void FillTentTrainerParty_(u16 trainerId, u8 firstMonId, u8 monCount); +static void FillFactoryFrontierTrainerParty(u16 trainerId, u8 firstMonId); +static void FillFactoryTentTrainerParty(u16 trainerId, u8 firstMonId); +static u8 GetFrontierTrainerFixedIvs(u16 trainerId); +static void FillPartnerParty(u16 trainerId); +static void SetEReaderTrainerChecksum(struct BattleTowerEReaderTrainer *ereaderTrainer); +static u8 SetTentPtrsGetLevel(void); + +// Const rom data. +static void (* const gUnknown_085DF96C[])(void) = +{ + sub_8161F94, + sub_8162054, + sub_81620F4, + ChooseNextBattleTowerTrainer, + sub_81621C0, + AwardBattleTowerRibbons, + SaveBattleTowerProgress, + sub_8163914, + nullsub_61, + nullsub_116, + sub_81642A0, + sub_8164828, + sub_8164B74, + sub_8164DCC, + sub_8164DE4, + sub_8164E04, +}; + +static const u32 gUnknown_085DF9AC[][2] = +{ + {0x00000001, 0x00000002}, + {0x00004000, 0x00008000}, + {0x00010000, 0x00020000}, + {0x00040000, 0x00080000}, +}; + +static const u32 gUnknown_085DF9CC[][2] = +{ + {0xfffffffe, 0xfffffffd}, + {0xffffbfff, 0xffff7fff}, + {0xfffeffff, 0xfffdffff}, + {0xfffbffff, 0xfff7ffff}, +}; + +static const u8 gUnknown_085DF9EC[] = +{ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x08, 0x09, 0x0a, 0x0b, 0x0c +}; + +static const u8 gUnknown_085DF9F6[] = +{ + [FRONTIER_MODE_SINGLES] = 3, + [FRONTIER_MODE_DOUBLES] = 4, + [FRONTIER_MODE_MULTIS] = 2, + [FRONTIER_MODE_LINK_MULTIS] = 2, +}; + +static const u16 gUnknown_085DF9FA[][2] = +{ + {0x0000, 0x0063}, + {0x0050, 0x0077}, + {0x0064, 0x008b}, + {0x0078, 0x009f}, + {0x008c, 0x00b3}, + {0x00a0, 0x00c7}, + {0x00b4, 0x00db}, + {0x00c8, 0x012b}, +}; + +static const u16 gUnknown_085DFA1A[][2] = +{ + {0x0064, 0x0077}, + {0x0078, 0x008b}, + {0x008c, 0x009f}, + {0x00a0, 0x00b3}, + {0x00b4, 0x00c7}, + {0x00c8, 0x00db}, + {0x00dc, 0x00ef}, + {0x00c8, 0x012b}, + {0x00b3, 0x008d}, + {0x00c8, 0x00b7}, +}; + +static const u8 gUnknown_085DFA42[4] = +{ + [FRONTIER_MODE_SINGLES] = 3, + [FRONTIER_MODE_DOUBLES] = 4, + [FRONTIER_MODE_MULTIS] = 2, + [FRONTIER_MODE_LINK_MULTIS] = 2, +}; + +static const u16 gUnknown_085DFA46[] = +{ + 0x0c3a, 0x0c3a, 0x0c01, 0x0a2a, 0x0607, 0x0c01 +}; + +static const u16 gUnknown_085DFA52[] = +{ + 0x1039, 0x122e, 0x0c04, 0x0a3d, 0x0630, 0x0c04 +}; // code void sub_8161F74(void) @@ -21,25 +202,25 @@ void sub_8161F74(void) gUnknown_085DF96C[gSpecialVar_0x8004](); } -void sub_8161F94(void) +static void sub_8161F94(void) { u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; u32 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); gSaveBlock2Ptr->frontier.field_CA8 = 1; - gSaveBlock2Ptr->frontier.field_CB2 = 0; + gSaveBlock2Ptr->frontier.curChallengeBattleNum = 0; gSaveBlock2Ptr->frontier.field_CA9_a = 0; gSaveBlock2Ptr->frontier.field_CA9_b = 0; sub_81A3ACC(); if (!(gSaveBlock2Ptr->frontier.field_CDC & gUnknown_085DF9AC[battleMode][lvlMode])) - gSaveBlock2Ptr->frontier.field_CE0[battleMode][lvlMode] = 0; + gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode] = 0; - sub_8164ED8(); + ValidateBattleTowerRecordChecksums(); saved_warp2_set(0, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum, -1); gTrainerBattleOpponent_A = 0; } -void sub_8162054(void) +static void sub_8162054(void) { u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; u32 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); @@ -49,7 +230,7 @@ void sub_8162054(void) case 0: break; case 1: - gSpecialVar_Result = sub_8164FCC(lvlMode, battleMode); + gSpecialVar_Result = GetCurrentBattleTowerWinStreak(lvlMode, battleMode); break; case 2: gSpecialVar_Result = ((gSaveBlock2Ptr->frontier.field_CDC & gUnknown_085DF9AC[battleMode][lvlMode]) != 0); @@ -60,7 +241,7 @@ void sub_8162054(void) } } -void sub_81620F4(void) +static void sub_81620F4(void) { u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; u32 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); @@ -70,7 +251,7 @@ void sub_81620F4(void) case 0: break; case 1: - gSaveBlock2Ptr->frontier.field_CE0[battleMode][lvlMode] = gSpecialVar_0x8006; + gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode] = gSpecialVar_0x8006; break; case 2: if (gSpecialVar_0x8006) @@ -83,3 +264,2587 @@ void sub_81620F4(void) break; } } + +static void sub_81621C0(void) +{ + if (gTrainerBattleOpponent_A == TRAINER_EREADER) + ClearEReaderTrainer(&gSaveBlock2Ptr->frontier.ereaderTrainer); + + if (gSaveBlock2Ptr->frontier.field_D04 < 9999) + gSaveBlock2Ptr->frontier.field_D04++; + + gSaveBlock2Ptr->frontier.curChallengeBattleNum++; + SaveCurrentWinStreak(); + gSpecialVar_Result = gSaveBlock2Ptr->frontier.curChallengeBattleNum; +} + +static bool8 ChooseSpecialBattleTowerTrainer(void) +{ + s32 i, j, validMons; + s32 trainerIds[9]; + s32 idsCount = 0; + s32 winStreak = 0; + u8 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; + u8 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); + + if (VarGet(VAR_FRONTIER_FACILITY) != FRONTIER_FACILITY_TOWER) + return FALSE; + + winStreak = GetCurrentBattleTowerWinStreak(lvlMode, battleMode); + for (i = 0; i < 5; i++) + { + u32 *record = (u32*)(&gSaveBlock2Ptr->frontier.towerRecords[i]); + u32 recordHasData = 0; + u32 checksum = 0; + for (j = 0; j < (sizeof(struct EmeraldBattleTowerRecord) - 4) / 4; j++) // - 4, because of the last field being the checksum itself. + { + recordHasData |= record[j]; + checksum += record[j]; + } + validMons = 0; + for (j = 0; j < 4; j++) + { + if (gSaveBlock2Ptr->frontier.towerRecords[i].party[j].species != 0 + && gSaveBlock2Ptr->frontier.towerRecords[i].party[j].level <= GetFrontierEnemyMonLevel(lvlMode)) + validMons++; + } + + if (validMons >= gUnknown_085DF9F6[battleMode] + && gSaveBlock2Ptr->frontier.towerRecords[i].winStreak == winStreak + && gSaveBlock2Ptr->frontier.towerRecords[i].lvlMode == lvlMode + && recordHasData + && gSaveBlock2Ptr->frontier.towerRecords[i].checksum == checksum) + { + trainerIds[idsCount] = i + TRAINER_RECORD_MIXING_FRIEND; + idsCount++; + } + } + + if (battleMode == FRONTIER_MODE_SINGLES) + { + ValidateApprenticesChecksums(); + for (i = 0; i < 4; i++) + { + if (gSaveBlock2Ptr->apprentices[i].lvlMode != 0 + && gUnknown_085DF9EC[gSaveBlock2Ptr->apprentices[i].field_1] == winStreak + && gSaveBlock2Ptr->apprentices[i].lvlMode - 1 == lvlMode) + { + trainerIds[idsCount] = i + TRAINER_RECORD_MIXING_APPRENTICE; + idsCount++; + } + } + } + + if (idsCount != 0) + { + gTrainerBattleOpponent_A = trainerIds[Random() % idsCount]; + return TRUE; + } + else + { + return FALSE; + } +} + +static void ChooseNextBattleTowerTrainer(void) +{ + u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; + if (lvlMode == FRONTIER_LVL_TENT) + { + sub_8165E18(); + } + else + { + u16 id; + u32 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); + u16 winStreak = GetCurrentFacilityWinStreak(); + u32 challengeNum = winStreak / 7; + SetFacilityPtrsGetLevel(); + + if (battleMode == FRONTIER_MODE_MULTIS || battleMode == FRONTIER_MODE_LINK_MULTIS) + { + id = gSaveBlock2Ptr->frontier.curChallengeBattleNum; + gTrainerBattleOpponent_A = gSaveBlock2Ptr->frontier.field_CB4[id * 2]; + gTrainerBattleOpponent_B = gSaveBlock2Ptr->frontier.field_CB4[id * 2 + 1]; + SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_A, 0); + SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_B, 1); + } + else if (ChooseSpecialBattleTowerTrainer()) + { + SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_A, 0); + gSaveBlock2Ptr->frontier.field_CB4[gSaveBlock2Ptr->frontier.curChallengeBattleNum] = gTrainerBattleOpponent_A; + } + else + { + s32 i; + while (1) + { + id = sub_8162548(challengeNum, gSaveBlock2Ptr->frontier.curChallengeBattleNum); + + // Ensure trainer wasn't previously fought in this challenge. + for (i = 0; i < gSaveBlock2Ptr->frontier.curChallengeBattleNum; i++) + { + if (gSaveBlock2Ptr->frontier.field_CB4[i] == id) + break; + } + if (i == gSaveBlock2Ptr->frontier.curChallengeBattleNum) + break; + } + + gTrainerBattleOpponent_A = id; + SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_A, 0); + if (gSaveBlock2Ptr->frontier.curChallengeBattleNum + 1 < 7) + gSaveBlock2Ptr->frontier.field_CB4[gSaveBlock2Ptr->frontier.curChallengeBattleNum] = gTrainerBattleOpponent_A; + } + } +} + +u16 sub_8162548(u8 challengeNum, u8 battleNum) +{ + u16 trainerId; + + if (challengeNum <= 7) + { + if (battleNum == 6) + { + trainerId = (gUnknown_085DFA1A[challengeNum][1] - gUnknown_085DFA1A[challengeNum][0]) + 1; + trainerId = gUnknown_085DFA1A[challengeNum][0] + (Random() % trainerId); + } + else + { + trainerId = (gUnknown_085DF9FA[challengeNum][1] - gUnknown_085DF9FA[challengeNum][0]) + 1; + trainerId = gUnknown_085DF9FA[challengeNum][0] + (Random() % trainerId); + } + } + else + { + trainerId = (gUnknown_085DF9FA[7][1] - gUnknown_085DF9FA[7][0]) + 1; + trainerId = gUnknown_085DF9FA[7][0] + (Random() % trainerId); + } + + return trainerId; +} + +#ifdef NONMATCHING +static u16 sub_81625B4(u8 challengeNum, u8 battleNum, u16 *trainerIdPtr, u8 *arg3) // Unused +{ + register u16 trainerId, count; + + if (challengeNum <= 7) + { + if (battleNum == 6) + { + count = (gUnknown_085DFA1A[challengeNum][1] - gUnknown_085DFA1A[challengeNum][0]) + 1; + trainerId = gUnknown_085DFA1A[challengeNum][0]; + } + else + { + count = (gUnknown_085DF9FA[challengeNum][1] - gUnknown_085DF9FA[challengeNum][0]) + 1; + trainerId = gUnknown_085DF9FA[challengeNum][0]; + } + } + else + { + count = (gUnknown_085DF9FA[7][1] - gUnknown_085DF9FA[7][0]) + 1; + trainerId = gUnknown_085DF9FA[7][0]; + } + + *trainerIdPtr = trainerId; + *arg3 = count; +} +#else +NAKED +static u16 sub_81625B4(u8 challengeNum, u8 battleNum, u16 *trainerIdPtr, u8 *arg3) +{ + asm_unified(" push {r4,lr}\n\ + adds r4, r2, 0\n\ + lsls r0, 24\n\ + lsrs r0, 24\n\ + adds r2, r0, 0\n\ + lsls r1, 24\n\ + lsrs r1, 24\n\ + cmp r0, 0x7\n\ + bhi _081625F4\n\ + cmp r1, 0x6\n\ + bne _081625D4\n\ + ldr r1, =gUnknown_085DFA1A\n\ + lsls r2, r0, 2\n\ + b _081625D8\n\ + .pool\n\ +_081625D4:\n\ + ldr r1, =gUnknown_085DF9FA\n\ + lsls r2, 2\n\ +_081625D8:\n\ + adds r0, r1, 0x2\n\ + adds r0, r2, r0\n\ + adds r2, r1\n\ + ldrh r0, [r0]\n\ + ldrh r1, [r2]\n\ + subs r0, r1\n\ + adds r0, 0x1\n\ + lsls r0, 16\n\ + lsrs r1, r0, 16\n\ + ldrh r0, [r2]\n\ + b _08162604\n\ + .pool\n\ +_081625F4:\n\ + ldr r0, =gUnknown_085DF9FA\n\ + ldrh r1, [r0, 0x1E]\n\ + ldrh r2, [r0, 0x1C]\n\ + subs r1, r2\n\ + adds r1, 0x1\n\ + lsls r1, 16\n\ + lsrs r1, 16\n\ + ldrh r0, [r0, 0x1C]\n\ +_08162604:\n\ + strh r0, [r4]\n\ + strb r1, [r3]\n\ + pop {r4}\n\ + pop {r0}\n\ + bx r0\n\ + .pool"); +} +#endif + +void SetBattleFacilityTrainerGfxId(u16 trainerId, u8 tempVarId) +{ + u32 i; + u8 facilityClass; + u8 trainerObjectGfxId; + + SetFacilityPtrsGetLevel(); + if (trainerId == TRAINER_EREADER) + { + facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass; + } + else if (trainerId == TRAINER_FRONTIER_BRAIN) + { + SetFrontierBrainEventObjGfx_2(); + return; + } + else if (trainerId < TRAINER_RECORD_MIXING_FRIEND) + { + facilityClass = gFacilityTrainers[trainerId].facilityClass; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + facilityClass = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass; + } + else + { + facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass; + } + + // Search male classes. + for (i = 0; i < ARRAY_COUNT(gTowerMaleFacilityClasses); i++) + { + if (gTowerMaleFacilityClasses[i] == facilityClass) + break; + } + if (i != ARRAY_COUNT(gTowerMaleFacilityClasses)) + { + trainerObjectGfxId = gTowerMaleTrainerGfxIds[i]; + switch (tempVarId) + { + case 0: + default: + VarSet(VAR_OBJ_GFX_ID_0, trainerObjectGfxId); + return; + case 1: + VarSet(VAR_OBJ_GFX_ID_1, trainerObjectGfxId); + return; + case 15: + VarSet(VAR_OBJ_GFX_ID_E, trainerObjectGfxId); + return; + } + } + + // Search female classes. + for (i = 0; i < ARRAY_COUNT(gTowerFemaleFacilityClasses); i++) + { + if (gTowerFemaleFacilityClasses[i] == facilityClass) + break; + } + if (i != ARRAY_COUNT(gTowerFemaleFacilityClasses)) + { + trainerObjectGfxId = gTowerFemaleTrainerGfxIds[i]; + switch (tempVarId) + { + case 0: + default: + VarSet(VAR_OBJ_GFX_ID_0, trainerObjectGfxId); + return; + case 1: + VarSet(VAR_OBJ_GFX_ID_1, trainerObjectGfxId); + return; + case 15: + VarSet(VAR_OBJ_GFX_ID_E, trainerObjectGfxId); + return; + } + } + + switch (tempVarId) + { + case 0: + default: + VarSet(VAR_OBJ_GFX_ID_0, EVENT_OBJ_GFX_BOY_1); + return; + case 1: + VarSet(VAR_OBJ_GFX_ID_1, EVENT_OBJ_GFX_BOY_1); + return; + case 15: + VarSet(VAR_OBJ_GFX_ID_E, EVENT_OBJ_GFX_BOY_1); + return; + } +} + +void SetEReaderTrainerGfxId(void) +{ + SetBattleFacilityTrainerGfxId(TRAINER_EREADER, 0); +} + +u8 GetBattleFacilityTrainerGfxId(u16 trainerId) +{ + u32 i; + u8 facilityClass; + u8 trainerObjectGfxId; + + SetFacilityPtrsGetLevel(); + if (trainerId == TRAINER_EREADER) + { + facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass; + } + else if (trainerId < TRAINER_RECORD_MIXING_FRIEND) + { + facilityClass = gFacilityTrainers[trainerId].facilityClass; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + facilityClass = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass; + } + else + { + facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass; + } + + // Search male classes. + for (i = 0; i < ARRAY_COUNT(gTowerMaleFacilityClasses); i++) + { + if (gTowerMaleFacilityClasses[i] == facilityClass) + break; + } + if (i != ARRAY_COUNT(gTowerMaleFacilityClasses)) + { + trainerObjectGfxId = gTowerMaleTrainerGfxIds[i]; + return trainerObjectGfxId; + } + + // Search female classes. + for (i = 0; i < ARRAY_COUNT(gTowerFemaleFacilityClasses); i++) + { + if (gTowerFemaleFacilityClasses[i] == facilityClass) + break; + } + if (i != ARRAY_COUNT(gTowerFemaleFacilityClasses)) + { + trainerObjectGfxId = gTowerFemaleTrainerGfxIds[i]; + return trainerObjectGfxId; + } + else + { + return EVENT_OBJ_GFX_BOY_1; + } +} + +void PutNewBattleTowerRecord(struct EmeraldBattleTowerRecord *newRecordEm) +{ + u16 slotValues[6]; + u16 slotIds[6]; + s32 i, j, k; + s32 slotsCount = 0; + struct EmeraldBattleTowerRecord *newRecord = newRecordEm; // Needed to match. + + // Find a record slot of the same player and replace it. + for (i = 0; i < 5; i++) + { + k = 0; + for (j = 0; j < 4; j++) + { + if (gSaveBlock2Ptr->frontier.towerRecords[i].trainerId[j] != newRecord->trainerId[j]) + break; + } + if (j == 4) + { + for (k = 0; k < PLAYER_NAME_LENGTH; k++) + { + // BUG: Wrong variable used, 'j' instead of 'k'. + if (gSaveBlock2Ptr->frontier.towerRecords[i].name[j] != newRecord->name[j]) + break; + if (newRecord->name[j] == EOS) + { + k = PLAYER_NAME_LENGTH; + break; + } + } + } + + if (k == PLAYER_NAME_LENGTH) + break; + } + if (i < 5) + { + gSaveBlock2Ptr->frontier.towerRecords[i] = *newRecord; + return; + } + + // Find an empty record slot. + for (i = 0; i < 5; i++) + { + if (gSaveBlock2Ptr->frontier.towerRecords[i].winStreak == 0) + break; + } + if (i < 5) + { + gSaveBlock2Ptr->frontier.towerRecords[i] = *newRecord; + return; + } + + // Find possible slots to replace the record. + slotValues[0] = gSaveBlock2Ptr->frontier.towerRecords[0].winStreak; + slotIds[0] = 0; + slotsCount++; + + for (i = 1; i < 5; i++) + { + for (j = 0; j < slotsCount; j++) + { + if (gSaveBlock2Ptr->frontier.towerRecords[i].winStreak < slotValues[j]) + { + j = 0; + slotsCount = 1; + slotValues[0] = gSaveBlock2Ptr->frontier.towerRecords[i].winStreak; + slotIds[0] = i; + break; + } + else if (gSaveBlock2Ptr->frontier.towerRecords[i].winStreak > slotValues[j]) + { + break; + } + } + + if (j == slotsCount) + { + slotValues[slotsCount] = gSaveBlock2Ptr->frontier.towerRecords[i].winStreak; + slotIds[slotsCount] = i; + slotsCount++; + } + } + + i = Random() % slotsCount; + gSaveBlock2Ptr->frontier.towerRecords[slotIds[i]] = *newRecord; +} + +u8 GetFrontierTrainerFrontSpriteId(u16 trainerId) +{ + SetFacilityPtrsGetLevel(); + + if (trainerId == TRAINER_EREADER) + { + return gFacilityClassToPicIndex[gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass]; + } + else if (trainerId == TRAINER_FRONTIER_BRAIN) + { + return GetFrontierBrainTrainerPicIndex(); + } + else if (trainerId < TRAINER_RECORD_MIXING_FRIEND) + { + return gFacilityClassToPicIndex[gFacilityTrainers[trainerId].facilityClass]; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + return gFacilityClassToPicIndex[GetRecordedBattleRecordMixFriendClass()]; + else + return gFacilityClassToPicIndex[gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass]; + } + else + { + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + return gFacilityClassToPicIndex[gApprentices[GetRecordedBattleApprenticeId()].facilityClass]; + else + return gFacilityClassToPicIndex[gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass]; + } +} + +u8 GetFrontierOpponentClass(u16 trainerId) +{ + u8 trainerClass = 0; + SetFacilityPtrsGetLevel(); + + if (trainerId == TRAINER_EREADER) + { + trainerClass = gFacilityClassToTrainerClass[gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass]; + } + else if (trainerId == TRAINER_FRONTIER_BRAIN) + { + trainerClass = GetFrontierBrainTrainerClass(); + } + else if (trainerId == TRAINER_STEVEN_PARTNER) + { + trainerClass = gTrainers[TRAINER_STEVEN].trainerClass; + } + else if (trainerId < TRAINER_RECORD_MIXING_FRIEND) + { + trainerClass = gFacilityClassToTrainerClass[gFacilityTrainers[trainerId].facilityClass]; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + { + trainerClass = gFacilityClassToTrainerClass[GetRecordedBattleRecordMixFriendClass()]; + } + else + { + trainerClass = gFacilityClassToTrainerClass[gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass]; + asm(""); + } + } + else + { + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + { + trainerClass = gFacilityClassToTrainerClass[gApprentices[GetRecordedBattleApprenticeId()].facilityClass]; + } + else + { + trainerClass = gFacilityClassToTrainerClass[gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass]; + asm(""); + } + } + + return trainerClass; +} + +static u8 GetFrontierTrainerFacilityClass(u16 trainerId) +{ + u8 facilityClass; + SetFacilityPtrsGetLevel(); + + if (trainerId == TRAINER_EREADER) + { + facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass; + } + else if (trainerId < TRAINER_RECORD_MIXING_FRIEND) + { + facilityClass = gFacilityTrainers[trainerId].facilityClass; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + facilityClass = GetRecordedBattleRecordMixFriendClass(); + else + facilityClass = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass; + } + else + { + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + facilityClass = gApprentices[GetRecordedBattleApprenticeId()].facilityClass; + else + facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass; + } + + return facilityClass; +} + +void GetFrontierTrainerName(u8 *dst, u16 trainerId) +{ + s32 i = 0; + SetFacilityPtrsGetLevel(); + + if (trainerId == TRAINER_EREADER) + { + for (i = 0; i < PLAYER_NAME_LENGTH; i++) + dst[i] = gSaveBlock2Ptr->frontier.ereaderTrainer.name[i]; + } + else if (trainerId == TRAINER_FRONTIER_BRAIN) + { + CopyFrontierBrainTrainerName(dst); + return; + } + else if (trainerId == TRAINER_STEVEN_PARTNER) + { + for (i = 0; i < PLAYER_NAME_LENGTH; i++) + dst[i] = gTrainers[TRAINER_STEVEN].trainerName[i]; + } + else if (trainerId < TRAINER_RECORD_MIXING_FRIEND) + { + for (i = 0; i < PLAYER_NAME_LENGTH; i++) + dst[i] = gFacilityTrainers[trainerId].trainerName[i]; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + { + sub_8186468(dst); + return; + } + else + { + struct EmeraldBattleTowerRecord *record = &gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND]; + TVShowConvertInternationalString(dst, record->name, record->language); + return; + } + } + else + { + u8 id, language; + + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + { + id = GetRecordedBattleApprenticeId(); + language = GetRecordedBattleApprenticeLanguage(); + } + else + { + struct Apprentice *apprentice = &gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE]; + id = apprentice->id; + language = apprentice->language; + } + TVShowConvertInternationalString(dst, GetApprenticeNameInLanguage(id, language), language); + return; + } + + dst[i] = EOS; +} + +static bool8 IsFrontierTrainerFemale(u16 trainerId) +{ + u32 i; + u8 facilityClass; + + SetFacilityPtrsGetLevel(); + if (trainerId == TRAINER_EREADER) + { + facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass; + } + else if (trainerId == TRAINER_FRONTIER_BRAIN) + { + return IsFrontierBrainFemale(); + } + else if (trainerId < TRAINER_RECORD_MIXING_FRIEND) + { + facilityClass = gFacilityTrainers[trainerId].facilityClass; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + facilityClass = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass; + } + else + { + facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass; + } + + // Search female classes. + for (i = 0; i < ARRAY_COUNT(gTowerFemaleFacilityClasses); i++) + { + if (gTowerFemaleFacilityClasses[i] == facilityClass) + break; + } + if (i != ARRAY_COUNT(gTowerFemaleFacilityClasses)) + return TRUE; + else + return FALSE; +} + +void FillFrontierTrainerParty(u8 monsCount) +{ + ZeroEnemyPartyMons(); + FillTrainerParty(gTrainerBattleOpponent_A, 0, monsCount); +} + +void FillFrontierTrainersParties(u8 monsCount) +{ + ZeroEnemyPartyMons(); + FillTrainerParty(gTrainerBattleOpponent_A, 0, monsCount); + FillTrainerParty(gTrainerBattleOpponent_B, 3, monsCount); +} + +static void FillTentTrainerParty(u8 monsCount) +{ + ZeroEnemyPartyMons(); + FillTentTrainerParty_(gTrainerBattleOpponent_A, 0, monsCount); +} + +static void FillTrainerParty(u16 trainerId, u8 firstMonId, u8 monCount) +{ + s32 i, j; + u16 chosenMonIndices[4]; + u8 friendship = 0xFF; + u8 level = SetFacilityPtrsGetLevel(); + u8 fixedIV = 0; + u8 bfMonCount; + const u16 *bfMonPool = NULL; + u32 otID = 0; + + if (trainerId < TRAINER_RECORD_MIXING_FRIEND) + { + // Normal battle frontier trainer. + fixedIV = GetFrontierTrainerFixedIvs(trainerId); + bfMonPool = gFacilityTrainers[gTrainerBattleOpponent_A].bfMonPool; + } + else if (trainerId == TRAINER_EREADER) + { + for (i = firstMonId; i < firstMonId + 3; i++) + sub_806819C(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.ereaderTrainer.party[i - firstMonId]); + return; + } + else if (trainerId == TRAINER_FRONTIER_BRAIN) + { + CreateFrontierBrainPokemon(); + return; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + // Record mixed player. + for (j = 0, i = firstMonId; i < firstMonId + monCount; j++, i++) + { + if (gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[j].species != 0 + && gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[j].level <= level) + { + sub_8068338(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[j], FALSE); + } + } + return; + } + else + { + // Apprentice. + for (i = firstMonId; i < firstMonId + 3; i++) + CreateApprenticeMon(&gEnemyParty[i], &gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE], i - firstMonId); + return; + } + + // Regular battle frontier trainer. + // Attempt to fill the trainer's party with random Pokemon until 3 have been + // successfully chosen. The trainer's party may not have duplicate pokemon species + // or duplicate held items. + for (bfMonCount = 0; bfMonPool[bfMonCount] != 0xFFFF; bfMonCount++) + ; + i = 0; + otID = Random32(); + while (i != monCount) + { + u16 monPoolId = bfMonPool[Random() % bfMonCount]; + if ((level == 50 || level == 20) && monPoolId > 849) + continue; + + // Ensure this pokemon species isn't a duplicate. + for (j = 0; j < i + firstMonId; j++) + { + if (GetMonData(&gEnemyParty[j], MON_DATA_SPECIES, NULL) == gFacilityTrainerMons[monPoolId].species) + break; + } + if (j != i + firstMonId) + continue; + + // Ensure this Pokemon's held item isn't a duplicate. + for (j = 0; j < i + firstMonId; j++) + { + if (GetMonData(&gEnemyParty[j], MON_DATA_HELD_ITEM, NULL) != 0 + && GetMonData(&gEnemyParty[j], MON_DATA_HELD_ITEM, NULL) == gBattleFrontierHeldItems[gFacilityTrainerMons[monPoolId].itemTableId]) + break; + } + if (j != i + firstMonId) + continue; + + // Ensure this exact pokemon index isn't a duplicate. This check doesn't seem necessary + // because the species and held items were already checked directly above. + for (j = 0; j < i; j++) + { + if (chosenMonIndices[j] == monPoolId) + break; + } + if (j != i) + continue; + + chosenMonIndices[i] = monPoolId; + + // Place the chosen pokemon into the trainer's party. + CreateMonWithEVSpreadPersonalityOTID(&gEnemyParty[i + firstMonId], + gFacilityTrainerMons[monPoolId].species, + level, + gFacilityTrainerMons[monPoolId].nature, + fixedIV, + gFacilityTrainerMons[monPoolId].evSpread, + otID); + + friendship = 255; + // Give the chosen pokemon its specified moves. + for (j = 0; j < 4; j++) + { + SetMonMoveSlot(&gEnemyParty[i + firstMonId], gFacilityTrainerMons[monPoolId].moves[j], j); + if (gFacilityTrainerMons[monPoolId].moves[j] == MOVE_FRUSTRATION) + friendship = 0; // Frustration is more powerful the lower the pokemon's friendship is. + } + + SetMonData(&gEnemyParty[i + firstMonId], MON_DATA_FRIENDSHIP, &friendship); + SetMonData(&gEnemyParty[i + firstMonId], MON_DATA_HELD_ITEM, &gBattleFrontierHeldItems[gFacilityTrainerMons[monPoolId].itemTableId]); + + // The pokemon was successfully added to the trainer's party, so it's safe to move on to + // the next party slot. + i++; + } +} + +// Probably an early draft before the 'CreateApprenticeMon' was written. +static void Unused_CreateApprenticeMons(u16 trainerId, u8 firstMonId) +{ + s32 i, j; + u8 friendship = 0xFF; + u8 level = 0; + u8 fixedIV = 0; + struct Apprentice *apprentice = &gSaveBlock2Ptr->apprentices[0]; + + if (apprentice->field_1 < 5) + fixedIV = 6; + else + fixedIV = 9; + + if (gSaveBlock2Ptr->frontier.lvlMode != FRONTIER_LVL_50) + level = 100; + else + level = 50; + + for (i = 0; i != 3; i++) + { + CreateMonWithEVSpread(&gEnemyParty[firstMonId + i], apprentice->party[i].species, level, fixedIV, 8); + friendship = 0xFF; + for (j = 0; j < 4; j++) + { + if (apprentice->party[i].moves[j] == MOVE_FRUSTRATION) + friendship = 0; + } + SetMonData(&gEnemyParty[firstMonId + i], MON_DATA_FRIENDSHIP, &friendship); + SetMonData(&gEnemyParty[firstMonId + i], MON_DATA_HELD_ITEM, &apprentice->party[i].item); + } +} + +u16 RandomizeFacilityTrainerMonId(u16 trainerId) +{ + u8 level = SetFacilityPtrsGetLevel(); + const u16 *bfMonPool = gFacilityTrainers[trainerId].bfMonPool; + u8 bfMonCount = 0; + u32 monPoolId = bfMonPool[bfMonCount]; + + while (monPoolId != 0xFFFF) + { + bfMonCount++; + monPoolId = bfMonPool[bfMonCount]; + if (monPoolId == 0xFFFF) + break; + } + + do + { + monPoolId = bfMonPool[Random() % bfMonCount]; + } while((level == 50 || level == 20) && monPoolId > 849); + + return monPoolId; +} + +static void FillFactoryTrainerParty(void) +{ + ZeroEnemyPartyMons(); + if (gSaveBlock2Ptr->frontier.lvlMode != FRONTIER_LVL_TENT) + FillFactoryFrontierTrainerParty(gTrainerBattleOpponent_A, 0); + else + FillFactoryTentTrainerParty(gTrainerBattleOpponent_A, 0); +} + +static void FillFactoryFrontierTrainerParty(u16 trainerId, u8 firstMonId) +{ + u8 i, j; + u8 friendship; + u8 level; + u8 fixedIV; + u32 otID; + + if (trainerId < TRAINER_RECORD_MIXING_FRIEND) + { + u8 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; // Unused variable. + u8 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); + u8 challengeNum = gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][0] / 7; + if (gSaveBlock2Ptr->frontier.curChallengeBattleNum < 6) + fixedIV = sub_81A6CA8(challengeNum, 0); + else + fixedIV = sub_81A6CA8(challengeNum, 1); + } + else if (trainerId == TRAINER_EREADER) + { + for (i = firstMonId; i < firstMonId + 3; i++) + sub_806819C(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.ereaderTrainer.party[i - firstMonId]); + return; + } + else if (trainerId == TRAINER_FRONTIER_BRAIN) + { + sub_81A6CD0(); + return; + } + else + { + fixedIV = 31; + } + + + level = SetFacilityPtrsGetLevel(); + otID = T1_READ_32(gSaveBlock2Ptr->playerTrainerId); + for (i = 0; i < 3; i++) + { + u16 poolId = gUnknown_03006298[i]; + CreateMonWithEVSpreadPersonalityOTID(&gEnemyParty[firstMonId + i], + gFacilityTrainerMons[poolId].species, + level, + gFacilityTrainerMons[poolId].nature, + fixedIV, + gFacilityTrainerMons[poolId].evSpread, + otID); + + friendship = 0; + for (j = 0; j < 4; j++) + SetMonMoveAvoidReturn(&gEnemyParty[firstMonId + i], gFacilityTrainerMons[poolId].moves[j], j); + + SetMonData(&gEnemyParty[firstMonId + i], MON_DATA_FRIENDSHIP, &friendship); + SetMonData(&gEnemyParty[firstMonId + i], MON_DATA_HELD_ITEM, &gBattleFrontierHeldItems[gFacilityTrainerMons[poolId].itemTableId]); + } +} + +static void FillFactoryTentTrainerParty(u16 trainerId, u8 firstMonId) +{ + u8 i, j; + u8 friendship; + u8 level = 30; + u8 fixedIV = 0; + u32 otID = T1_READ_32(gSaveBlock2Ptr->playerTrainerId); + + for (i = 0; i < 3; i++) + { + u16 poolId = gUnknown_03006298[i]; + CreateMonWithEVSpreadPersonalityOTID(&gEnemyParty[firstMonId + i], + gFacilityTrainerMons[poolId].species, + level, + gFacilityTrainerMons[poolId].nature, + fixedIV, + gFacilityTrainerMons[poolId].evSpread, + otID); + + friendship = 0; + for (j = 0; j < 4; j++) + { + SetMonMoveAvoidReturn(&gEnemyParty[firstMonId + i], gFacilityTrainerMons[poolId].moves[j], j); + if (gFacilityTrainerMons[poolId].moves[j] == MOVE_FRUSTRATION) + friendship = 0; + } + + SetMonData(&gEnemyParty[firstMonId + i], MON_DATA_FRIENDSHIP, &friendship); + SetMonData(&gEnemyParty[firstMonId + i], MON_DATA_HELD_ITEM, &gBattleFrontierHeldItems[gFacilityTrainerMons[poolId].itemTableId]); + } +} + +void FrontierSpeechToString(const u16 *words) +{ + ConvertEasyChatWordsToString(gStringVar4, words, 3, 2); + if (GetStringWidth(1, gStringVar4, -1) > 204) + { + s32 i = 0; + + ConvertEasyChatWordsToString(gStringVar4, words, 2, 3); + while (gStringVar4[i++] != CHAR_NEWLINE) + ; + while (gStringVar4[i] != CHAR_NEWLINE) + i++; + + gStringVar4[i] = CHAR_PROMPT_SCROLL; + } +} + +static void sub_8163914(void) +{ + u16 trainerId; + SetFacilityPtrsGetLevel(); + + if (gSpecialVar_0x8005) + trainerId = gTrainerBattleOpponent_B; + else + trainerId = gTrainerBattleOpponent_A; + + if (trainerId == TRAINER_EREADER) + FrontierSpeechToString(gSaveBlock2Ptr->frontier.ereaderTrainer.greeting); + else if (trainerId < TRAINER_RECORD_MIXING_FRIEND) + FrontierSpeechToString(gFacilityTrainers[trainerId].speechBefore); + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + FrontierSpeechToString(gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].greeting); + else + CopyFriendsApprenticeChallengeText(trainerId - TRAINER_RECORD_MIXING_APPRENTICE); +} + +static void HandleSpecialTrainerBattleEnd(void) +{ + s32 i; + + RecordedBattle_SaveBattleOutcome(); + switch (gBattleScripting.specialTrainerBattleType) + { + case SPECIAL_BATTLE_TOWER: + case SPECIAL_BATTLE_DOME: + case SPECIAL_BATTLE_PALACE: + case SPECIAL_BATTLE_ARENA: + case SPECIAL_BATTLE_FACTORY: + case SPECIAL_BATTLE_PIKE_SINGLE: + case SPECIAL_BATTLE_PIKE_DOUBLE: + case SPECIAL_BATTLE_PYRAMID: + if (gSaveBlock2Ptr->frontier.battlesCount < 0xFFFFFF) + { + gSaveBlock2Ptr->frontier.battlesCount++; + if (gSaveBlock2Ptr->frontier.battlesCount % 20 == 0) + UpdateGymLeaderRematch(); + } + else + { + gSaveBlock2Ptr->frontier.battlesCount = 0xFFFFFF; + } + break; + case SPECIAL_BATTLE_SECRET_BASE: + for (i = 0; i < PARTY_SIZE; i++) + { + u16 itemBefore = GetMonData(&gSaveBlock1Ptr->playerParty[i], MON_DATA_HELD_ITEM); + SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &itemBefore); + } + break; + case SPECIAL_BATTLE_EREADER: + CopyEReaderTrainerFarewellMessage(); + break; + } + + SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); +} + +static void Task_StartBattleAfterTransition(u8 taskId) +{ + if (IsBattleTransitionDone() == TRUE) + { + gMain.savedCallback = HandleSpecialTrainerBattleEnd; + SetMainCallback2(CB2_InitBattle); + DestroyTask(taskId); + } +} + +void DoSpecialTrainerBattle(void) +{ + s32 i; + + gBattleScripting.specialTrainerBattleType = gSpecialVar_0x8004; + switch (gSpecialVar_0x8004) + { + case SPECIAL_BATTLE_TOWER: + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_BATTLE_TOWER; + switch (VarGet(VAR_FRONTIER_BATTLE_MODE)) + { + case FRONTIER_MODE_SINGLES: + FillFrontierTrainerParty(3); + break; + case FRONTIER_MODE_DOUBLES: + FillFrontierTrainerParty(4); + gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; + break; + case FRONTIER_MODE_MULTIS: + FillFrontierTrainersParties(2); + gPartnerTrainerId = gSaveBlock2Ptr->frontier.field_CB4[17]; + FillPartnerParty(gPartnerTrainerId); + gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_MULTI | BATTLE_TYPE_TWO_OPPONENTS; + break; + case FRONTIER_MODE_LINK_MULTIS: + gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_LINK | BATTLE_TYPE_MULTI | BATTLE_TYPE_x800000; + FillFrontierTrainersParties(2); + break; + } + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(sub_80B100C(0)); + break; + case SPECIAL_BATTLE_SECRET_BASE: + for (i = 0; i < PARTY_SIZE; i++) + { + u16 itemBefore = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM); + SetMonData(&gSaveBlock1Ptr->playerParty[i], MON_DATA_HELD_ITEM, &itemBefore); + } + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(sub_80B100C(12)); + break; + case SPECIAL_BATTLE_EREADER: + ZeroEnemyPartyMons(); + for (i = 0; i < 3; i++) + sub_806819C(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.ereaderTrainer.party[i]); + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_EREADER_TRAINER; + gTrainerBattleOpponent_A = 0; + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(sub_80B100C(13)); + break; + case SPECIAL_BATTLE_DOME: + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOME; + if (VarGet(VAR_FRONTIER_BATTLE_MODE) == FRONTIER_MODE_DOUBLES) + gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; + if (gTrainerBattleOpponent_A == TRAINER_FRONTIER_BRAIN) + FillFrontierTrainerParty(2); + CreateTask(Task_StartBattleAfterTransition, 1); + sub_806E694(0); + BattleTransition_StartOnField(sub_80B100C(3)); + break; + case SPECIAL_BATTLE_PALACE: + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_PALACE; + if (VarGet(VAR_FRONTIER_BATTLE_MODE) == FRONTIER_MODE_DOUBLES) + gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; + if (gSaveBlock2Ptr->frontier.lvlMode != FRONTIER_LVL_TENT) + FillFrontierTrainerParty(3); + else + FillTentTrainerParty(3); + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(sub_80B100C(4)); + break; + case SPECIAL_BATTLE_ARENA: + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_ARENA; + if (gSaveBlock2Ptr->frontier.lvlMode != FRONTIER_LVL_TENT) + FillFrontierTrainerParty(3); + else + FillTentTrainerParty(3); + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(sub_80B100C(5)); + break; + case SPECIAL_BATTLE_FACTORY: + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_FACTORY; + if (VarGet(VAR_FRONTIER_BATTLE_MODE) == FRONTIER_MODE_DOUBLES) + gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; + FillFactoryTrainerParty(); + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(sub_80B100C(6)); + break; + case SPECIAL_BATTLE_PIKE_SINGLE: + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_BATTLE_TOWER; + FillFrontierTrainerParty(3); + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(sub_80B100C(7)); + break; + case SPECIAL_BATTLE_PYRAMID: + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_PYRAMID; + FillFrontierTrainerParty(3); + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(sub_80B100C(10)); + break; + case SPECIAL_BATTLE_PIKE_DOUBLE: + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_BATTLE_TOWER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS; + FillFrontierTrainersParties(1); + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(sub_80B100C(7)); + break; + case SPECIAL_BATTLE_STEVEN: + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER; + FillPartnerParty(TRAINER_STEVEN_PARTNER); + gApproachingTrainerId = 0; + BattleSetup_ConfigureTrainerBattle(MossdeepCity_SpaceCenter_2F_EventScript_224157 + 1); + gApproachingTrainerId = 1; + BattleSetup_ConfigureTrainerBattle(MossdeepCity_SpaceCenter_2F_EventScript_224166 + 1); + gPartnerTrainerId = TRAINER_STEVEN_PARTNER; + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(B_TRANSITION_MAGMA); + break; + } +} + +static void SaveCurrentWinStreak(void) +{ + u8 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; + u8 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); + u16 winStreak = GetCurrentBattleTowerWinStreak(lvlMode, battleMode); + + if (gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode] < winStreak) + gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode] = winStreak; +} + +static void sub_8163EE4(void) +{ + s32 i; + u8 lvlMode, battleMode, class; + struct EmeraldBattleTowerRecord *playerRecord = &gSaveBlock2Ptr->frontier.towerPlayer; + + ClearBattleTowerRecord(playerRecord); + lvlMode = gSaveBlock2Ptr->frontier.lvlMode; + battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); + if (gSaveBlock2Ptr->playerGender != MALE) + { + class = gTowerFemaleFacilityClasses[(gSaveBlock2Ptr->playerTrainerId[0] + + gSaveBlock2Ptr->playerTrainerId[1] + + gSaveBlock2Ptr->playerTrainerId[2] + + gSaveBlock2Ptr->playerTrainerId[3]) % ARRAY_COUNT(gTowerFemaleFacilityClasses)]; + } + else + { + class = gTowerMaleFacilityClasses[(gSaveBlock2Ptr->playerTrainerId[0] + + gSaveBlock2Ptr->playerTrainerId[1] + + gSaveBlock2Ptr->playerTrainerId[2] + + gSaveBlock2Ptr->playerTrainerId[3]) % ARRAY_COUNT(gTowerMaleFacilityClasses)]; + } + playerRecord->lvlMode = lvlMode; + playerRecord->facilityClass = class; + CopyTrainerId(playerRecord->trainerId, gSaveBlock2Ptr->playerTrainerId); + StringCopy7(playerRecord->name, gSaveBlock2Ptr->playerName); + playerRecord->winStreak = GetCurrentBattleTowerWinStreak(lvlMode, battleMode); + + for (i = 0; i < 6; i++) + { + playerRecord->greeting[i] = gSaveBlock1Ptr->unk2BBC[i]; + playerRecord->speechWon[i] = gSaveBlock1Ptr->unk2BC8[i]; + playerRecord->speechLost[i] = gSaveBlock1Ptr->unk2BD4[i]; + } + + for (i = 0; i < 4; i++) + { + if (gSaveBlock2Ptr->frontier.selectedPartyMons[i] != 0) + sub_80686FC(&gPlayerParty[gSaveBlock2Ptr->frontier.selectedPartyMons[i] - 1], &playerRecord->party[i]); + } + + playerRecord->language = gGameLanguage; + CalcEmeraldBattleTowerChecksum(&gSaveBlock2Ptr->frontier.towerPlayer); + SaveCurrentWinStreak(); +} + +static void SaveBattleTowerProgress(void) +{ + u16 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; + u16 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); + s32 challengeNum = (signed)(gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode] / 7); + + if (gSpecialVar_0x8005 == 0 && (challengeNum > 1 || gSaveBlock2Ptr->frontier.curChallengeBattleNum != 0)) + sub_8163EE4(); + + gSaveBlock2Ptr->frontier.field_CA8 =gSpecialVar_0x8005; + VarSet(VAR_TEMP_0, 0); + gSaveBlock2Ptr->frontier.field_CA9_a = 1; + sub_81A4C30(); +} + +static void nullsub_61(void) +{ + +} + +static void nullsub_116(void) +{ + +} + +static void sub_81640E0(u16 trainerId) +{ + s32 i, count; + u32 validSpecies[3]; + u16 species1 = GetMonData(&gPlayerParty[0], MON_DATA_SPECIES, NULL); + u16 species2 = GetMonData(&gPlayerParty[1], MON_DATA_SPECIES, NULL); + + count = 0; + for (i = 0; i < 3; i++) + { + u16 apprenticeSpecies = gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].party[i].species; + if (apprenticeSpecies != species1 && apprenticeSpecies != species2) + { + validSpecies[count] = i; + count++; + } + } + + gUnknown_03006298[0] = validSpecies[Random() % count]; + do + { + gUnknown_03006298[1] = validSpecies[Random() % count]; + } while (gUnknown_03006298[0] == gUnknown_03006298[1]); +} + +static void sub_8164188(u16 trainerId) +{ + s32 i, count; + u32 validSpecies[3]; + u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; + u16 species1 = GetMonData(&gPlayerParty[0], MON_DATA_SPECIES, NULL); + u16 species2 = GetMonData(&gPlayerParty[1], MON_DATA_SPECIES, NULL); + + count = 0; + for (i = 0; i < 4; i++) + { + if (gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[i].species != species1 + && gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[i].species != species2 + && gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[i].level <= GetFrontierEnemyMonLevel(lvlMode) + && gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[i].species != 0) + { + validSpecies[count] = i; + count++; + } + } + + gUnknown_03006298[2] = validSpecies[Random() % count]; + do + { + gUnknown_03006298[3] = validSpecies[Random() % count]; + } while (gUnknown_03006298[2] == gUnknown_03006298[3]); +} + +static void sub_81642A0(void) +{ + s32 i, j, k; + u32 spArray[5]; + s32 r10; + u16 trainerId; + u16 monPoolId; + u32 lvlMode, battleMode; + s32 challengeNum; + u32 species1, species2; + u32 level; + struct EventObjectTemplate *eventObjTemplates; + + eventObjTemplates = gSaveBlock1Ptr->eventObjectTemplates; + lvlMode = gSaveBlock2Ptr->frontier.lvlMode; + battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); + challengeNum = gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode] / 7; + species1 = GetMonData(&gPlayerParty[0], MON_DATA_SPECIES, NULL); + species2 = GetMonData(&gPlayerParty[1], MON_DATA_SPECIES, NULL); + level = SetFacilityPtrsGetLevel(); + + j = 0; + do + { + do + { + trainerId = sub_8162548(challengeNum, 0); + for (i = 0; i < j; i++) + { + if (gSaveBlock2Ptr->frontier.field_CB4[i] == trainerId) + break; + if (gFacilityTrainers[gSaveBlock2Ptr->frontier.field_CB4[i]].facilityClass == gFacilityTrainers[trainerId].facilityClass) + break; + } + } while (i != j); + gSaveBlock2Ptr->frontier.field_CB4[j] = trainerId; + j++; + } while (j < 6); + + r10 = 8; + for (i = 0; i < 6; i++) + { + trainerId = gSaveBlock2Ptr->frontier.field_CB4[i]; + eventObjTemplates[i + 1].graphicsId = GetBattleFacilityTrainerGfxId(trainerId); + for (j = 0; j < 2; j++) + { + while (1) + { + monPoolId = RandomizeFacilityTrainerMonId(trainerId); + if (j % 2 != 0 && gFacilityTrainerMons[gSaveBlock2Ptr->frontier.field_CB4[r10 - 1]].itemTableId == gFacilityTrainerMons[monPoolId].itemTableId) + continue; + + for (k = 8; k < r10; k++) + { + if (gFacilityTrainerMons[gSaveBlock2Ptr->frontier.field_CB4[k]].species == gFacilityTrainerMons[monPoolId].species) + break; + if (species1 == gFacilityTrainerMons[monPoolId].species) + break; + if (species2 == gFacilityTrainerMons[monPoolId].species) + break; + } + if (k == r10) + break; + } + + gSaveBlock2Ptr->frontier.field_CB4[r10] = monPoolId; + r10++; + } + } + + r10 = 0; + ValidateApprenticesChecksums(); + for (i = 0; i < 4; i++) + { + if (gSaveBlock2Ptr->apprentices[i].lvlMode != 0 + && gUnknown_085DF9EC[gSaveBlock2Ptr->apprentices[i].field_1] / 7 <= challengeNum + && gSaveBlock2Ptr->apprentices[i].lvlMode - 1 == lvlMode) + { + k = 0; + for (j = 0; j < 3; j++) + { + if (species1 != gSaveBlock2Ptr->apprentices[i].party[j].species + && species2 != gSaveBlock2Ptr->apprentices[i].party[j].species) + { + k++; + } + } + if (k > 2) + { + spArray[r10] = i + TRAINER_RECORD_MIXING_APPRENTICE; + r10++; + } + } + } + if (r10 != 0) + { + gSaveBlock2Ptr->frontier.field_CB4[6] = spArray[Random() % r10]; + eventObjTemplates[7].graphicsId = GetBattleFacilityTrainerGfxId(gSaveBlock2Ptr->frontier.field_CB4[6]); + FlagClear(FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_ALT_1); + sub_81640E0(gSaveBlock2Ptr->frontier.field_CB4[6]); + } + + r10 = 0; + for (i = 0; i < 5; i++) + { + u32 *record = (u32*)(&gSaveBlock2Ptr->frontier.towerRecords[i]); + u32 recordHasData = 0; + u32 checksum = 0; + for (j = 0; j < (sizeof(struct EmeraldBattleTowerRecord) - 4) / 4; j++) // - 4, because of the last field being the checksum itself. + { + recordHasData |= record[j]; + checksum += record[j]; + } + + if (gSaveBlock2Ptr->frontier.towerRecords[i].winStreak / 7 <= challengeNum + && gSaveBlock2Ptr->frontier.towerRecords[i].lvlMode == lvlMode + && recordHasData + && gSaveBlock2Ptr->frontier.towerRecords[i].checksum == checksum) + { + k = 0; + for (j = 0; j < 4; j++) + { + if (species1 != gSaveBlock2Ptr->frontier.towerRecords[i].party[j].species + && species2 != gSaveBlock2Ptr->frontier.towerRecords[i].party[j].species + && gSaveBlock2Ptr->frontier.towerRecords[i].party[j].level <= GetFrontierEnemyMonLevel(lvlMode) + && gSaveBlock2Ptr->frontier.towerRecords[i].party[j].species != 0) + { + k++; + } + } + if (k > 1) + { + spArray[r10] = i + TRAINER_RECORD_MIXING_FRIEND; + r10++; + } + } + } + if (r10 != 0) + { + gSaveBlock2Ptr->frontier.field_CB4[7] = spArray[Random() % r10]; + eventObjTemplates[8].graphicsId = GetBattleFacilityTrainerGfxId(gSaveBlock2Ptr->frontier.field_CB4[7]); + FlagClear(FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_ALT_2); + sub_8164188(gSaveBlock2Ptr->frontier.field_CB4[7]); + } +} + +static void sub_81646BC(u16 trainerId, u16 monPoolId) +{ + u16 move = 0; + u16 species = 0; + SetFacilityPtrsGetLevel(); + + if (trainerId != TRAINER_EREADER) + { + if (trainerId < TRAINER_RECORD_MIXING_FRIEND) + { + move = gFacilityTrainerMons[monPoolId].moves[0]; + species = gFacilityTrainerMons[monPoolId].species; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + move = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[gUnknown_03006298[gSpecialVar_0x8005 + 1]].moves[0]; + species = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[gUnknown_03006298[gSpecialVar_0x8005 + 1]].species; + } + else + { + s32 i; + + move = gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].party[gUnknown_03006298[gSpecialVar_0x8005 - 1]].moves[0]; + species = gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].party[gUnknown_03006298[gSpecialVar_0x8005 - 1]].species; + for (i = 0; i < PLAYER_NAME_LENGTH; i++) + gStringVar3[i] = gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].playerName[i]; + gStringVar3[i] = EOS; + ConvertInternationalString(gStringVar3, gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].language); + } + } + + StringCopy(gStringVar1, gMoveNames[move]); + StringCopy(gStringVar2, gSpeciesNames[species]); +} + +static void sub_8164828(void) +{ + s32 i, j, arrId; + s32 monPoolId; + s32 level = SetFacilityPtrsGetLevel(); + u16 winStreak = GetCurrentFacilityWinStreak(); + s32 challengeNum = winStreak / 7; + s32 k = gSpecialVar_LastTalked - 2; + s32 trainerId = gSaveBlock2Ptr->frontier.field_CB4[k]; + + for (arrId = 0; arrId < ARRAY_COUNT(gUnknown_085DD500); arrId++) + { + if (gUnknown_085DD500[arrId].facilityClass == GetFrontierTrainerFacilityClass(trainerId)) + break; + } + + switch (gSpecialVar_0x8005) + { + case 0: + if (trainerId == TRAINER_EREADER) + return; + if (trainerId < TRAINER_RECORD_MIXING_FRIEND) + { + GetFrontierTrainerName(gStringVar1, trainerId); + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + GetFrontierTrainerName(gStringVar1, trainerId); + } + else + { + s32 i; + for (i = 0; i < PLAYER_NAME_LENGTH; i++) + gStringVar1[i] = gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].playerName[i]; + gStringVar1[i] = EOS; + ConvertInternationalString(gStringVar1, gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].language); + ConvertIntToDecimalStringN(gStringVar2, gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].number, STR_CONV_MODE_LEFT_ALIGN, 3); + GetFrontierTrainerName(gStringVar3, trainerId); + } + break; + case 1: + monPoolId = gSaveBlock2Ptr->frontier.field_CB4[8 + k * 2]; + sub_81646BC(trainerId, monPoolId); + break; + case 2: + monPoolId = gSaveBlock2Ptr->frontier.field_CB4[9 + k * 2]; + sub_81646BC(trainerId, monPoolId); + break; + case 3: + gPartnerTrainerId = trainerId; + if (trainerId < TRAINER_RECORD_MIXING_FRIEND) + { + gSaveBlock2Ptr->frontier.field_CB4[18] = gSaveBlock2Ptr->frontier.field_CB4[8 + k * 2]; + gSaveBlock2Ptr->frontier.field_CB4[19] = gSaveBlock2Ptr->frontier.field_CB4[9 + k * 2]; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + gSaveBlock2Ptr->frontier.field_CB4[18] = gUnknown_03006298[2]; + gSaveBlock2Ptr->frontier.field_CB4[19] = gUnknown_03006298[3]; + } + else + { + gSaveBlock2Ptr->frontier.field_CB4[18] = gUnknown_03006298[0]; + gSaveBlock2Ptr->frontier.field_CB4[19] = gUnknown_03006298[1]; + } + for (k = 0; k < 14; k++) + { + while (1) + { + i = sub_8162548(challengeNum, k / 2); + if (gPartnerTrainerId == i) + continue; + + for (j = 0; j < k; j++) + { + if (gSaveBlock2Ptr->frontier.field_CB4[j] == i) + break; + } + if (j == k) + break; + } + gSaveBlock2Ptr->frontier.field_CB4[k] = i; + } + gSaveBlock2Ptr->frontier.field_CB4[17] = trainerId; + break; + case 4: + break; + } + + if (trainerId == TRAINER_EREADER) + return; + + if (trainerId < TRAINER_RECORD_MIXING_FRIEND) + { + ShowFieldMessage(gUnknown_085DD500[arrId].strings[gSpecialVar_0x8005]); + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + ShowFieldMessage(gUnknown_085DD500[arrId].strings[gSpecialVar_0x8005]); + } + else + { + u8 id = gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id; + ShowFieldMessage(gUnknown_085DD690[id][gSpecialVar_0x8005]); + } +} + +static void sub_8164B74(void) +{ + s32 challengeNum; + s32 i, j; + s32 trainerId = 0; + u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; + u32 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); + u32 battleNum = gSaveBlock2Ptr->frontier.curChallengeBattleNum; + GetMultiplayerId(); // Yet another pointless function call. + + switch (gSpecialVar_Result) + { + case 0: + if (battleMode == FRONTIER_MODE_LINK_MULTIS) + { + challengeNum = gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode] / 7; + if (sub_800A520()) + { + SendBlock(bitmask_all_link_players_but_self(), &challengeNum, sizeof(challengeNum)); + gSpecialVar_Result = 1; + } + } + else + { + gSpecialVar_Result = 6; + } + break; + case 1: + if ((GetBlockReceivedStatus() & 3) == 3) + { + ResetBlockReceivedFlags(); + if (gBlockRecvBuffer[0][0] > gBlockRecvBuffer[1][0]) + challengeNum = gBlockRecvBuffer[0][0]; + else + challengeNum = gBlockRecvBuffer[1][0]; + for (i = 0; i < 14; i++) + { + do + { + trainerId = sub_8162548(challengeNum, i / 2); + for (j = 0; j < i; j++) + { + if (gSaveBlock2Ptr->frontier.field_CB4[j] == trainerId) + break; + } + } while (i != j); + if (i == j) // This condition is always true, because of the loop above. + gSaveBlock2Ptr->frontier.field_CB4[i] = trainerId; + } + gSpecialVar_Result = 2; + } + break; + case 2: + if (sub_800A520()) + { + SendBlock(bitmask_all_link_players_but_self(), &gSaveBlock2Ptr->frontier.field_CB4, sizeof(gSaveBlock2Ptr->frontier.field_CB4)); + gSpecialVar_Result = 3; + } + break; + case 3: + if ((GetBlockReceivedStatus() & 3) == 3) + { + ResetBlockReceivedFlags(); + memcpy(&gSaveBlock2Ptr->frontier.field_CB4, gBlockRecvBuffer, sizeof(gSaveBlock2Ptr->frontier.field_CB4)); + gTrainerBattleOpponent_A = gSaveBlock2Ptr->frontier.field_CB4[battleNum * 2]; + gTrainerBattleOpponent_B = gSaveBlock2Ptr->frontier.field_CB4[battleNum * 2 + 1]; + SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_A, 0); + SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_B, 1); + if (gReceivedRemoteLinkPlayers != 0 && gWirelessCommType == 0) + gSpecialVar_Result = 4; + else + gSpecialVar_Result = 6; + } + break; + case 4: + sub_800AC34(); + gSpecialVar_Result = 5; + break; + case 5: + if (gReceivedRemoteLinkPlayers == 0) + { + gSpecialVar_Result = 6; + } + break; + case 6: + return; + } +} + +static void sub_8164DCC(void) +{ + if (gWirelessCommType != 0) + sub_800AC34(); +} + +static void sub_8164DE4(void) +{ + SetBattleFacilityTrainerGfxId(gSaveBlock2Ptr->frontier.field_CB4[17], 0xF); +} + +static void sub_8164E04(void) +{ + s32 i; + u8 text[32]; + + if (VarGet(VAR_FRONTIER_BATTLE_MODE) != FRONTIER_MODE_SINGLES) + return; + + GetFrontierTrainerName(text, gTrainerBattleOpponent_A); + StripExtCtrlCodes(text); + StringCopy(gSaveBlock2Ptr->frontier.field_BD8, text); + GetBattleTowerTrainerLanguage(&gSaveBlock2Ptr->frontier.field_BEB, gTrainerBattleOpponent_A); + gSaveBlock2Ptr->frontier.field_BD6 = GetMonData(&gEnemyParty[gBattlerPartyIndexes[1]], MON_DATA_SPECIES, NULL); + gSaveBlock2Ptr->frontier.field_BD4 = GetMonData(&gPlayerParty[gBattlerPartyIndexes[0]], MON_DATA_SPECIES, NULL); + for (i = 0; i < POKEMON_NAME_LENGTH + 1; i++) + gSaveBlock2Ptr->frontier.field_BE0[i] = gBattleMons[0].nickname[i]; + gSaveBlock2Ptr->frontier.field_D06 = gBattleOutcome; +} + +static void ValidateBattleTowerRecordChecksums(void) +{ + s32 i, j; + u32 *record = (u32*)(&gSaveBlock2Ptr->frontier.towerPlayer); + u32 checksum = 0; + + for (j = 0; j < (sizeof(struct EmeraldBattleTowerRecord) - 4) / 4; j++) // - 4, because of the last field being the checksum itself. + { + checksum += record[j]; + } + if (gSaveBlock2Ptr->frontier.towerPlayer.checksum != checksum) + ClearBattleTowerRecord(&gSaveBlock2Ptr->frontier.towerPlayer); + + for (i = 0; i < 5; i++) + { + record = (u32*)(&gSaveBlock2Ptr->frontier.towerRecords[i]); + checksum = 0; + for (j = 0; j < (sizeof(struct EmeraldBattleTowerRecord) - 4) / 4; j++) // - 4, because of the last field being the checksum itself. + { + checksum += record[j]; + } + if (gSaveBlock2Ptr->frontier.towerRecords[i].checksum != checksum) + ClearBattleTowerRecord(&gSaveBlock2Ptr->frontier.towerRecords[i]); + } +} + +void CalcEmeraldBattleTowerChecksum(struct EmeraldBattleTowerRecord *record) +{ + u32 i; + + record->checksum = 0; + for (i = 0; i < (sizeof(struct EmeraldBattleTowerRecord) - 4) / 4; i++) // - 4, because of the last field being the checksum itself. + record->checksum += ((u32 *)record)[i]; +} + +void CalcRubyBattleTowerChecksum(struct RSBattleTowerRecord *record) +{ + u32 i; + + record->checksum = 0; + for (i = 0; i < (sizeof(struct RSBattleTowerRecord) - 4) / 4; i++) // - 4, because of the last field being the checksum itself. + record->checksum += ((u32 *)record)[i]; +} + +static void ClearBattleTowerRecord(struct EmeraldBattleTowerRecord *record) +{ + u32 i; + + for (i = 0; i < sizeof(struct EmeraldBattleTowerRecord) / 4; i++) + ((u32 *)record)[i] = 0; +} + +u16 GetCurrentBattleTowerWinStreak(u8 lvlMode, u8 battleMode) +{ + u16 winStreak = gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode]; + + if (winStreak > 9999) + return 9999; + else + return winStreak; +} + +static u8 GetMonCountForBattleMode(u8 battleMode) +{ + u8 sp[ARRAY_COUNT(gUnknown_085DFA42)]; + memcpy(sp, gUnknown_085DFA42, sizeof(gUnknown_085DFA42)); + + if (battleMode < ARRAY_COUNT(gUnknown_085DFA42)) + return sp[battleMode]; + else + return 3; +} + +struct RibbonCounter +{ + u8 partyIndex; + u8 count; +}; + +static void AwardBattleTowerRibbons(void) +{ + s32 i; + u32 partyIndex; + struct RibbonCounter ribbons[3]; // BUG: 4 Pokemon can receive ribbons in a double battle mode. + u8 ribbonType = 0; + u8 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; + u8 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); + u8 monCount = GetMonCountForBattleMode(battleMode); + + if (lvlMode != FRONTIER_LVL_50) + ribbonType = MON_DATA_VICTORY_RIBBON; + else + ribbonType = MON_DATA_WINNING_RIBBON; + + gSpecialVar_Result = FALSE; + + if (GetCurrentBattleTowerWinStreak(lvlMode, battleMode) > 55) + { + for (i = 0; i < monCount; i++) + { + partyIndex = gSaveBlock2Ptr->frontier.selectedPartyMons[i] - 1; + ribbons[i].partyIndex = partyIndex; + ribbons[i].count = 0; + if (!GetMonData(&gSaveBlock1Ptr->playerParty[partyIndex], ribbonType)) + { + gSpecialVar_Result = TRUE; + SetMonData(&gSaveBlock1Ptr->playerParty[partyIndex], ribbonType, &gSpecialVar_Result); + ribbons[i].count = GetRibbonCount(&gSaveBlock1Ptr->playerParty[partyIndex]); + } + } + } + + if (gSpecialVar_Result) + { + IncrementGameStat(GAME_STAT_RECEIVED_RIBBONS); + for (i = 1; i < monCount; i++) + { + if (ribbons[i].count > ribbons[0].count) + { + struct RibbonCounter prevBest = ribbons[0]; + ribbons[0] = ribbons[i]; + ribbons[i] = prevBest; + } + } + if (ribbons[0].count > 4) + { + sub_80EE4DC(&gSaveBlock1Ptr->playerParty[ribbons[0].partyIndex], ribbonType); + } + } +} + +// This is a leftover debugging function that is used to populate the E-Reader +// trainer with the player's current data. +static void FillEReaderTrainerWithPlayerData(void) +{ + struct BattleTowerEReaderTrainer *ereaderTrainer = &gSaveBlock2Ptr->frontier.ereaderTrainer; + s32 i, j; + + if (gSaveBlock2Ptr->playerGender != MALE) + { + ereaderTrainer->facilityClass = gTowerFemaleFacilityClasses[(gSaveBlock2Ptr->playerTrainerId[0] + gSaveBlock2Ptr->playerTrainerId[1] + + gSaveBlock2Ptr->playerTrainerId[2] + gSaveBlock2Ptr->playerTrainerId[3]) % ARRAY_COUNT(gTowerFemaleFacilityClasses)]; + } + else + { + ereaderTrainer->facilityClass = gTowerMaleFacilityClasses[(gSaveBlock2Ptr->playerTrainerId[0] + gSaveBlock2Ptr->playerTrainerId[1] + + gSaveBlock2Ptr->playerTrainerId[2] + gSaveBlock2Ptr->playerTrainerId[3]) % ARRAY_COUNT(gTowerMaleFacilityClasses)]; + } + + CopyTrainerId(ereaderTrainer->trainerId, gSaveBlock2Ptr->playerTrainerId); + StringCopy7(ereaderTrainer->name, gSaveBlock2Ptr->playerName); + + ereaderTrainer->winStreak = 1; + + j = 7; + for (i = 0; i < 6; i++) + { + ereaderTrainer->greeting[i] = gSaveBlock1Ptr->unk2BBC[i]; + ereaderTrainer->farewellPlayerLost[i] = j; + ereaderTrainer->farewellPlayerWon[i] = j + 6; + j++; + } + + for (i = 0; i < 3; i++) + sub_80686FC(&gPlayerParty[i], &ereaderTrainer->party[i]); + + SetEReaderTrainerChecksum(ereaderTrainer); +} + +u8 GetEreaderTrainerFrontSpriteId(void) +{ + return gFacilityClassToPicIndex[gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass]; +} + +u8 GetEreaderTrainerClassId(void) +{ + return gFacilityClassToTrainerClass[gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass]; +} + +void GetEreaderTrainerName(u8 *dst) +{ + s32 i; + + for (i = 0; i < 5; i++) + dst[i] = gSaveBlock2Ptr->frontier.ereaderTrainer.name[i]; + + dst[i] = EOS; +} + +// Checks if the saved E-Reader trainer is valid. +void ValidateEReaderTrainer(void) +{ + u32 i; + u32 checksum; + struct BattleTowerEReaderTrainer *ereaderTrainer; + + gSpecialVar_Result = FALSE; + ereaderTrainer = &gSaveBlock2Ptr->frontier.ereaderTrainer; + + checksum = 0; + for (i = 0; i < (sizeof(struct BattleTowerEReaderTrainer) - 4) / 4; i++) // - 4, because of the last field being the checksum itself. + checksum |= ((u32 *)ereaderTrainer)[i]; + + if (checksum == 0) + { + gSpecialVar_Result = TRUE; + return; + } + + checksum = 0; + for (i = 0; i < (sizeof(struct BattleTowerEReaderTrainer) - 4) / 4; i++) // - 4, because of the last field being the checksum itself. + checksum += ((u32 *)ereaderTrainer)[i]; + + if (gSaveBlock2Ptr->frontier.ereaderTrainer.checksum != checksum) + { + ClearEReaderTrainer(&gSaveBlock2Ptr->frontier.ereaderTrainer); + gSpecialVar_Result = TRUE; + } +} + +static void SetEReaderTrainerChecksum(struct BattleTowerEReaderTrainer *ereaderTrainer) +{ + s32 i; + + ereaderTrainer->checksum = 0; + for (i = 0; i < (sizeof(struct BattleTowerEReaderTrainer) - 4) / 4; i++) // - 4, because of the last field being the checksum itself. + ereaderTrainer->checksum += ((u32 *)ereaderTrainer)[i]; +} + +void ClearEReaderTrainer(struct BattleTowerEReaderTrainer *ereaderTrainer) +{ + u32 i; + + for (i = 0; i < (sizeof(struct BattleTowerEReaderTrainer)) / 4; i++) + ((u32 *)ereaderTrainer)[i] = 0; +} + +void CopyEReaderTrainerGreeting(void) +{ + FrontierSpeechToString(gSaveBlock2Ptr->frontier.ereaderTrainer.greeting); +} + +static void CopyEReaderTrainerFarewellMessage(void) +{ + if (gBattleOutcome == B_OUTCOME_DREW) + gStringVar4[0] = EOS; + else if (gBattleOutcome == B_OUTCOME_WON) + FrontierSpeechToString(gSaveBlock2Ptr->frontier.ereaderTrainer.farewellPlayerWon); + else + FrontierSpeechToString(gSaveBlock2Ptr->frontier.ereaderTrainer.farewellPlayerLost); +} + +void sub_81653CC(void) +{ + if (gSaveBlock2Ptr->frontier.field_CA8 == 1) + sub_80F01B8(); + if (FlagGet(FLAG_0x077) == TRUE) + { + sub_80F01B8(); + FlagClear(FLAG_0x077); + } +} + +#define STEVEN_OTID 61226 + +static void FillPartnerParty(u16 trainerId) +{ + s32 i, j; + u32 ivs, level; + u32 friendship; + u16 monPoolId; + u32 otID; + u8 trainerName[PLAYER_NAME_LENGTH + 1]; + SetFacilityPtrsGetLevel(); + + if (trainerId == TRAINER_STEVEN_PARTNER) + { + for (i = 0; i < 3; i++) + { + do + { + j = Random32(); + } while (IsShinyOtIdPersonality(STEVEN_OTID, j) || sStevenMons[i].nature != GetNatureFromPersonality(j)); + CreateMon(&gPlayerParty[3 + i], + sStevenMons[i].species, + sStevenMons[i].level, + sStevenMons[i].fixedIV, + TRUE, i, // BUG: personality was stored in the 'j' variable. As a result, Steven's pokemon do not have the intended natures. + TRUE, STEVEN_OTID); + for (j = 0; j < 6; j++) + SetMonData(&gPlayerParty[3 + i], MON_DATA_HP_EV + j, &sStevenMons[i].evs[j]); + for (j = 0; j < 4; j++) + SetMonMoveSlot(&gPlayerParty[3 + i], sStevenMons[i].moves[j], j); + SetMonData(&gPlayerParty[3 + i], MON_DATA_OT_NAME, gTrainers[TRAINER_STEVEN].trainerName); + j = MALE; + SetMonData(&gPlayerParty[3 + i], MON_DATA_OT_GENDER, &j); + CalculateMonStats(&gPlayerParty[3 + i]); + } + } + else if (trainerId == TRAINER_EREADER) + { + // Scrapped, lol. + trainerName[0] = gGameLanguage; + } + else if (trainerId < TRAINER_RECORD_MIXING_FRIEND) + { + level = SetFacilityPtrsGetLevel(); + ivs = GetFrontierTrainerFixedIvs(trainerId); + otID = Random32(); + for (i = 0; i < 2; i++) + { + monPoolId = gSaveBlock2Ptr->frontier.field_CB4[i + 18]; + CreateMonWithEVSpreadPersonalityOTID(&gPlayerParty[3 + i], + gFacilityTrainerMons[monPoolId].species, + level, + gFacilityTrainerMons[monPoolId].nature, + ivs, + gFacilityTrainerMons[monPoolId].evSpread, + otID); + friendship = 0xFF; + for (j = 0; j < 4; j++) + { + SetMonMoveSlot(&gPlayerParty[3 + i], gFacilityTrainerMons[monPoolId].moves[j], j); + if (gFacilityTrainerMons[monPoolId].moves[j] == MOVE_FRUSTRATION) + friendship = 0; + } + SetMonData(&gPlayerParty[3 + i], MON_DATA_FRIENDSHIP, &friendship); + SetMonData(&gPlayerParty[3 + i], MON_DATA_HELD_ITEM, &gBattleFrontierHeldItems[gFacilityTrainerMons[monPoolId].itemTableId]); + for (j = 0; j < PLAYER_NAME_LENGTH + 1; j++) + trainerName[j] = gFacilityTrainers[trainerId].trainerName[j]; + SetMonData(&gPlayerParty[3 + i], MON_DATA_OT_NAME, &trainerName); + j = IsFrontierTrainerFemale(trainerId); + SetMonData(&gPlayerParty[3 + i], MON_DATA_OT_GENDER, &j); + } + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + trainerId -= TRAINER_RECORD_MIXING_FRIEND; + for (i = 0; i < 2; i++) + { + struct EmeraldBattleTowerRecord *record = &gSaveBlock2Ptr->frontier.towerRecords[trainerId]; + struct UnknownPokemonStruct monData = record->party[gSaveBlock2Ptr->frontier.field_CB4[18 + i]]; + StringCopy(trainerName, record->name); + if (record->language == LANGUAGE_JAPANESE) + { + if (monData.nickname[0] != EXT_CTRL_CODE_BEGIN || monData.nickname[1] != EXT_CTRL_CODE_JPN) + { + monData.nickname[5] = EOS; + ConvertInternationalString(monData.nickname, LANGUAGE_JAPANESE); + } + } + else + { + if (monData.nickname[0] == EXT_CTRL_CODE_BEGIN && monData.nickname[1] == EXT_CTRL_CODE_JPN) + trainerName[5] = EOS; + } + sub_8068338(&gPlayerParty[3 + i], &monData, TRUE); + SetMonData(&gPlayerParty[3 + i], MON_DATA_OT_NAME, trainerName); + j = IsFrontierTrainerFemale(trainerId + TRAINER_RECORD_MIXING_FRIEND); + SetMonData(&gPlayerParty[3 + i], MON_DATA_OT_GENDER, &j); + } + } + else + { + trainerId -= TRAINER_RECORD_MIXING_APPRENTICE; + for (i = 0; i < 2; i++) + { + CreateApprenticeMon(&gPlayerParty[3 + i], &gSaveBlock2Ptr->apprentices[trainerId], gSaveBlock2Ptr->frontier.field_CB4[18 + i]); + j = IsFrontierTrainerFemale(trainerId + TRAINER_RECORD_MIXING_APPRENTICE); + SetMonData(&gPlayerParty[3 + i], MON_DATA_OT_GENDER, &j); + } + } +} + +bool32 RubyBattleTowerRecordToEmerald(struct RSBattleTowerRecord *src, struct EmeraldBattleTowerRecord *dst) +{ + s32 i, validMons = 0; + + for (i = 0; i < 3; i++) + { + if (src->party[i].species) + validMons++; + } + + if (validMons != 3) + { + memset(dst, 0, sizeof(*dst)); + return FALSE; + } + else + { + dst->lvlMode = src->lvlMode; + dst->winStreak = src->winStreak; + for (i = 0; i < (signed) ARRAY_COUNT(sRubyFacilityClassToEmerald); i++) + { + if (sRubyFacilityClassToEmerald[i][0] == src->facilityClass) + break; + } + if (i != ARRAY_COUNT(sRubyFacilityClassToEmerald)) + dst->facilityClass = sRubyFacilityClassToEmerald[i][1]; + else + dst->facilityClass = FACILITY_CLASS_YOUNGSTER; + + for (i = 0; i < PLAYER_NAME_LENGTH + 1; i++) + dst->name[i] = src->name[i]; + for (i = 0; i < 4; i++) + dst->trainerId[i] = src->trainerId[i]; + for (i = 0; i < 6; i++) + dst->greeting[i] = src->greeting[i]; + for (i = 0; i < 6; i++) + dst->speechWon[i] = gUnknown_085DFA46[i]; + for (i = 0; i < 6; i++) + dst->speechLost[i] = gUnknown_085DFA52[i]; + for (i = 0; i < 3; i++) + dst->party[i] = src->party[i]; + + CpuFill32(0, &dst->party[3], sizeof(dst->party[3])); + CalcEmeraldBattleTowerChecksum(dst); + return TRUE; + } +} + +bool32 EmeraldBattleTowerRecordToRuby(struct EmeraldBattleTowerRecord *src, struct RSBattleTowerRecord *dst) +{ + s32 i, validMons = 0; + + for (i = 0; i < 3; i++) + { + if (src->party[i].species) + validMons++; + } + + if (validMons != 3) + { + memset(dst, 0, sizeof(*dst)); + return FALSE; + } + else + { + dst->lvlMode = src->lvlMode; + dst->winStreak = src->winStreak; + for (i = 0; i < (signed) ARRAY_COUNT(sRubyFacilityClassToEmerald); i++) + { + if (sRubyFacilityClassToEmerald[i][1] == src->facilityClass) + break; + } + if (i != ARRAY_COUNT(sRubyFacilityClassToEmerald)) + dst->facilityClass = sRubyFacilityClassToEmerald[i][0]; + else + dst->facilityClass = 0x24; // FACILITY_CLASS_YOUNGSTER in Ruby/Sapphire. + + for (i = 0; i < PLAYER_NAME_LENGTH + 1; i++) + dst->name[i] = src->name[i]; + for (i = 0; i < 4; i++) + dst->trainerId[i] = src->trainerId[i]; + for (i = 0; i < 6; i++) + dst->greeting[i] = src->greeting[i]; + for (i = 0; i < 3; i++) + dst->party[i] = src->party[i]; + + CalcRubyBattleTowerChecksum(dst); + return TRUE; + } +} + +void CalcApprenticeChecksum(struct Apprentice *apprentice) +{ + s32 i; + + apprentice->checksum = 0; + for (i = 0; i < (sizeof(struct Apprentice) - 4) / 4; i++) + apprentice->checksum += ((u32 *)apprentice)[i]; +} + +static void ClearApprentice(struct Apprentice *apprentice) +{ + s32 i; + + for (i = 0; i < (sizeof(struct Apprentice)) / 4; i++) + ((u32 *)apprentice)[i] = 0; + ResetApprenticeStruct(apprentice); +} + +static void ValidateApprenticesChecksums(void) +{ + s32 i, j; + + for (i = 0; i < 4; i++) + { + u32 *data = (u32*) &gSaveBlock2Ptr->apprentices[i]; + u32 checksum = 0; + for (j = 0; j < (sizeof(struct Apprentice) - 4) / 4; j++) + checksum += data[j]; + if (gSaveBlock2Ptr->apprentices[i].checksum != checksum) + ClearApprentice(&gSaveBlock2Ptr->apprentices[i]); + } +} + +void GetBattleTowerTrainerLanguage(u8 *dst, u16 trainerId) +{ + if (trainerId == TRAINER_EREADER) + { + *dst = gGameLanguage; + } + else if (trainerId < TRAINER_RECORD_MIXING_FRIEND) + { + *dst = gGameLanguage; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + *dst = GetRecordedBattleRecordMixFriendLanguage(); + else + *dst = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].language; + } + else + { + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + *dst = GetRecordedBattleApprenticeLanguage(); + else + *dst = gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].language; + } +} + +u8 SetFacilityPtrsGetLevel(void) +{ + if (gSaveBlock2Ptr->frontier.lvlMode == FRONTIER_LVL_TENT) + { + return SetTentPtrsGetLevel(); + } + else + { + gFacilityTrainers = gBattleFrontierTrainers; + gFacilityTrainerMons = gBattleFrontierMons; + return GetFrontierEnemyMonLevel(gSaveBlock2Ptr->frontier.lvlMode); + } +} + +u8 GetFrontierEnemyMonLevel(u8 lvlMode) +{ + u8 level; + + switch (lvlMode) + { + default: + case FRONTIER_LVL_50: + level = 50; + break; + case FRONTIER_LVL_OPEN: + level = GetHighestLevelInPlayerParty(); + if (level < 60) + level = 60; + break; + } + + return level; +} + +s32 GetHighestLevelInPlayerParty(void) +{ + s32 highestLevel = 0; + s32 i; + + for (i = 0; i < PARTY_SIZE; i++) + { + if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL) + && GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2, NULL) != SPECIES_EGG) + { + s32 level = GetMonData(&gPlayerParty[i], MON_DATA_LEVEL, NULL); + if (level > highestLevel) + highestLevel = level; + } + } + + return highestLevel; +} + +static u8 GetFrontierTrainerFixedIvs(u16 trainerId) +{ + u8 fixedIV = 0; + + if (trainerId < 100) + fixedIV = 3; + else if (trainerId < 120) + fixedIV = 6; + else if (trainerId < 140) + fixedIV = 9; + else if (trainerId < 160) + fixedIV = 12; + else if (trainerId < 180) + fixedIV = 15; + else if (trainerId < 200) + fixedIV = 18; + else if (trainerId < 220) + fixedIV = 21; + else + fixedIV = 31; + + return fixedIV; +} + +static u16 sub_8165D40(void) +{ + u32 facility = VarGet(VAR_FRONTIER_FACILITY); + + if (facility == FRONTIER_FACILITY_PALACE) + return Random() % 30; + else if (facility == FRONTIER_FACILITY_ARENA) + return Random() % 30; + else if (facility == FRONTIER_FACILITY_FACTORY) + return Random() % 30; + else if (facility == FRONTIER_FACILITY_TOWER) + return 0; + else + return 0; +} + +static u8 SetTentPtrsGetLevel(void) +{ + u8 level = 30; + u32 tentFacility = VarGet(VAR_FRONTIER_FACILITY); + + if (tentFacility == TENT_SLATEPORT) + { + gFacilityTrainers = gSlateportBattleTentTrainers; + gFacilityTrainerMons = gSlateportBattleTentMons; + } + else if (tentFacility == TENT_VERDANTURF) + { + gFacilityTrainers = gVerdanturfBattleTentTrainers; + gFacilityTrainerMons = gVerdanturfBattleTentMons; + } + else if (tentFacility == TENT_FALLARBOR) + { + gFacilityTrainers = gFallarborBattleTentTrainers; + gFacilityTrainerMons = gFallarborBattleTentMons; + } + else + { + gFacilityTrainers = gBattleFrontierTrainers; + gFacilityTrainerMons = gBattleFrontierMons; + } + + level = GetHighestLevelInPlayerParty(); + if (level < 30) + level = 30; + + return level; +} + +static void sub_8165E18(void) +{ + s32 i; + u16 trainerId; + + do + { + trainerId = sub_8165D40(); + for (i = 0; i < gSaveBlock2Ptr->frontier.curChallengeBattleNum; i++) + { + if (gSaveBlock2Ptr->frontier.field_CB4[i] == trainerId) + break; + } + } while (i != gSaveBlock2Ptr->frontier.curChallengeBattleNum); + + gTrainerBattleOpponent_A = trainerId; + SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_A, 0); + if (gSaveBlock2Ptr->frontier.curChallengeBattleNum + 1 < 3) + gSaveBlock2Ptr->frontier.field_CB4[gSaveBlock2Ptr->frontier.curChallengeBattleNum] = gTrainerBattleOpponent_A; +} + +static void FillTentTrainerParty_(u16 trainerId, u8 firstMonId, u8 monCount) +{ + s32 i, j; + u16 chosenMonIndices[4]; + u8 friendship; + u8 level = SetTentPtrsGetLevel(); + u8 fixedIV = 0; + u8 bfMonCount; + const u16 *bfMonPool = NULL; + u32 otID = 0; + u16 monPoolId; + + bfMonPool = gFacilityTrainers[gTrainerBattleOpponent_A].bfMonPool; + + bfMonCount = 0; + monPoolId = bfMonPool[bfMonCount]; + while (monPoolId != 0xFFFF) + { + bfMonCount++; + monPoolId = bfMonPool[bfMonCount]; + if (monPoolId == 0xFFFF) + break; + } + + i = 0; + otID = Random32(); + while (i != monCount) + { + u16 monPoolId = bfMonPool[Random() % bfMonCount]; + + // Ensure this pokemon species isn't a duplicate. + for (j = 0; j < i + firstMonId; j++) + { + if (GetMonData(&gEnemyParty[j], MON_DATA_SPECIES, NULL) == gFacilityTrainerMons[monPoolId].species) + break; + } + if (j != i + firstMonId) + continue; + + // Ensure this Pokemon's held item isn't a duplicate. + for (j = 0; j < i + firstMonId; j++) + { + if (GetMonData(&gEnemyParty[j], MON_DATA_HELD_ITEM, NULL) != 0 + && GetMonData(&gEnemyParty[j], MON_DATA_HELD_ITEM, NULL) == gBattleFrontierHeldItems[gFacilityTrainerMons[monPoolId].itemTableId]) + break; + } + if (j != i + firstMonId) + continue; + + // Ensure this exact pokemon index isn't a duplicate. This check doesn't seem necessary + // because the species and held items were already checked directly above. + for (j = 0; j < i; j++) + { + if (chosenMonIndices[j] == monPoolId) + break; + } + if (j != i) + continue; + + chosenMonIndices[i] = monPoolId; + + // Place the chosen pokemon into the trainer's party. + CreateMonWithEVSpreadPersonalityOTID(&gEnemyParty[i + firstMonId], + gFacilityTrainerMons[monPoolId].species, + level, + gFacilityTrainerMons[monPoolId].nature, + fixedIV, + gFacilityTrainerMons[monPoolId].evSpread, + otID); + + friendship = 255; + // Give the chosen pokemon its specified moves. + for (j = 0; j < 4; j++) + { + SetMonMoveSlot(&gEnemyParty[i + firstMonId], gFacilityTrainerMons[monPoolId].moves[j], j); + if (gFacilityTrainerMons[monPoolId].moves[j] == MOVE_FRUSTRATION) + friendship = 0; // Frustration is more powerful the lower the pokemon's friendship is. + } + + SetMonData(&gEnemyParty[i + firstMonId], MON_DATA_FRIENDSHIP, &friendship); + SetMonData(&gEnemyParty[i + firstMonId], MON_DATA_HELD_ITEM, &gBattleFrontierHeldItems[gFacilityTrainerMons[monPoolId].itemTableId]); + + // The pokemon was successfully added to the trainer's party, so it's safe to move on to + // the next party slot. + i++; + } +} + +u8 sub_81660B8(u8 facilityClass) +{ + u8 trainerObjectGfxId; + u8 i; + + // Search male classes. + for (i = 0; i < ARRAY_COUNT(gTowerMaleFacilityClasses); i++) + { + if (gTowerMaleFacilityClasses[i] == facilityClass) + break; + } + if (i != ARRAY_COUNT(gTowerMaleFacilityClasses)) + { + trainerObjectGfxId = gTowerMaleTrainerGfxIds[i]; + return trainerObjectGfxId; + } + + // Search female classes. + for (i = 0; i < ARRAY_COUNT(gTowerFemaleFacilityClasses); i++) + { + if (gTowerFemaleFacilityClasses[i] == facilityClass) + break; + } + if (i != ARRAY_COUNT(gTowerFemaleFacilityClasses)) + { + trainerObjectGfxId = gTowerFemaleTrainerGfxIds[i]; + return trainerObjectGfxId; + } + else + { + return EVENT_OBJ_GFX_BOY_1; + } +} + +bool32 ValidateBattleTowerRecord(u8 recordId) // unused +{ + s32 i; + u32 *record = (u32*)(&gSaveBlock2Ptr->frontier.towerRecords[recordId]); + u32 checksum = 0; + u32 hasData = 0; + for (i = 0; i < (sizeof(struct EmeraldBattleTowerRecord) - 4) / 4; i++) // - 4, because of the last fjeld bejng the checksum jtself. + { + checksum += record[i]; + hasData |= record[i]; + } + + if (checksum == 0 && hasData == 0) + { + return FALSE; + } + else if (gSaveBlock2Ptr->frontier.towerRecords[recordId].checksum != checksum) + { + ClearBattleTowerRecord(&gSaveBlock2Ptr->frontier.towerRecords[recordId]); + return FALSE; + } + else + { + return TRUE; + } +} + +void sub_8166188(void) +{ + if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_x2000000)) + { + s32 i; + u8 enemyLevel = SetFacilityPtrsGetLevel(); + + for (i = 0; i < PARTY_SIZE; i++) + { + u32 species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES, NULL); + if (species) + { + SetMonData(&gEnemyParty[i], MON_DATA_EXP, &gExperienceTables[gBaseStats[species].growthRate][enemyLevel]); + CalculateMonStats(&gEnemyParty[i]); + } + } + } +} |