#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 "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 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[]; 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. 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) { gUnknown_085DF96C[gSpecialVar_0x8004](); } static void sub_8161F94(void) { u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; u32 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); gSaveBlock2Ptr->frontier.field_CA8 = 1; 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.towerWinStreaks[battleMode][lvlMode] = 0; ValidateBattleTowerRecordChecksums(); saved_warp2_set(0, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum, -1); gTrainerBattleOpponent_A = 0; } static void sub_8162054(void) { u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; u32 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); switch (gSpecialVar_0x8005) { case 0: break; case 1: gSpecialVar_Result = GetCurrentBattleTowerWinStreak(lvlMode, battleMode); break; case 2: gSpecialVar_Result = ((gSaveBlock2Ptr->frontier.field_CDC & gUnknown_085DF9AC[battleMode][lvlMode]) != 0); break; case 3: gSaveBlock2Ptr->frontier.field_D07 = gSaveBlock2Ptr->frontier.lvlMode; break; } } static void sub_81620F4(void) { u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; u32 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); switch (gSpecialVar_0x8005) { case 0: break; case 1: gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode] = gSpecialVar_0x8006; break; case 2: if (gSpecialVar_0x8006) gSaveBlock2Ptr->frontier.field_CDC |= gUnknown_085DF9AC[battleMode][lvlMode]; else gSaveBlock2Ptr->frontier.field_CDC &= gUnknown_085DF9CC[battleMode][lvlMode]; break; case 3: gSaveBlock2Ptr->frontier.field_D07 = gSaveBlock2Ptr->frontier.lvlMode; 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]); } } } }