#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 "international_string_util.h" #include "battle.h" #include "battle_frontier_1.h" #include "battle_frontier_2.h" #include "recorded_battle.h" #include "easy_chat.h" #include "constants/battle_frontier.h" #include "constants/trainers.h" #include "constants/event_objects.h" #include "constants/moves.h" extern void sub_81A3ACC(void); extern void CreateFrontierBrainPokemon(void); extern void sub_81A6CD0(void); extern u8 GetFrontierEnemyMonLevel(u8); extern u8 GetFacilityEnemyMonLevel(void); extern u16 sub_81A39C4(void); extern void SetFrontierBrainTrainerGfxId(void); extern u8 GetFrontierBrainTrainerPicIndex(void); extern u8 GetFrontierBrainTrainerClass(void); extern u8 IsFrontierBrainFemale(void); extern void CopyFrontierBrainTrainerName(u8 *dst); extern void SetMonMoveAvoidReturn(struct Pokemon *mon, u16 move, u8 moveSlot); extern const u32 gUnknown_085DF9AC[][2]; extern const u32 gUnknown_085DF9CC[][2]; extern void (* const gUnknown_085DF96C[])(void); extern const u8 gUnknown_085DF9F6[]; extern const u8 gUnknown_085DF9EC[]; extern const u16 gBattleFrontierHeldItems[]; // This file's functions. void sub_8164ED8(void); void sub_8163E90(void); void sub_8165B20(void); void sub_8165E18(void); u16 GetCurrentBattleTowerWinStreak(u8 lvlMode, u8 battleMode); void sub_816534C(void *); u16 sub_8162548(u8, u8); void sub_81630C4(u16 trainerId, u8 firstMonId, u8 monCount); void sub_8165EA4(u16 trainerId, u8 firstMonId, u8 monCount); void sub_81635D4(u16 trainerId, u8 firstMonId); void sub_816379C(u16 trainerId, u8 firstMonId); u8 GetFrontierTrainerFixedIvs(u16 trainerId); // code void sub_8161F74(void) { gUnknown_085DF96C[gSpecialVar_0x8004](); } 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.field_CE0[battleMode][lvlMode] = 0; sub_8164ED8(); saved_warp2_set(0, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum, -1); gTrainerBattleOpponent_A = 0; } 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; } } 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.field_CE0[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; } } void sub_81621C0(void) { if (gTrainerBattleOpponent_A == BATTLE_TOWER_EREADER_TRAINER_ID) sub_816534C(&gSaveBlock2Ptr->frontier.ereaderTrainer); if (gSaveBlock2Ptr->frontier.field_D04 < 9999) gSaveBlock2Ptr->frontier.field_D04++; gSaveBlock2Ptr->frontier.curChallengeBattleNum++; sub_8163E90(); gSpecialVar_Result = gSaveBlock2Ptr->frontier.curChallengeBattleNum; } 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.records[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.records[i].party[j].species != 0 && gSaveBlock2Ptr->frontier.records[i].party[j].level <= GetFrontierEnemyMonLevel(lvlMode)) validMons++; } if (validMons >= gUnknown_085DF9F6[battleMode] && gSaveBlock2Ptr->frontier.records[i].winStreak == winStreak && gSaveBlock2Ptr->frontier.records[i].lvlMode == lvlMode && recordHasData && gSaveBlock2Ptr->frontier.records[i].checksum == checksum) { trainerIds[idsCount] = i + BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID; idsCount++; } } if (battleMode == FRONTIER_MODE_SINGLES) { sub_8165B20(); 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 + BATTLE_TOWER_RECORD_APPRENTICE_BASE_ID; idsCount++; } } } if (idsCount != 0) { gTrainerBattleOpponent_A = trainerIds[Random() % idsCount]; return TRUE; } else { return FALSE; } } 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); u32 r5 = sub_81A39C4() / 7; GetFacilityEnemyMonLevel(); // Pointless function call. if (battleMode == FRONTIER_MODE_MULTIS || battleMode == FRONTIER_MODE_LINK_MULTIS) { id = gSaveBlock2Ptr->frontier.curChallengeBattleNum; gTrainerBattleOpponent_A = gSaveBlock2Ptr->frontier.battledTrainerIds[id * 2]; gTrainerBattleOpponent_B = gSaveBlock2Ptr->frontier.battledTrainerIds[id * 2 + 1]; SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_A, 0); SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_B, 1); } else if (ChooseSpecialBattleTowerTrainer()) { SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_A, 0); gSaveBlock2Ptr->frontier.battledTrainerIds[gSaveBlock2Ptr->frontier.curChallengeBattleNum] = gTrainerBattleOpponent_A; } else { s32 i; while (1) { id = sub_8162548(r5, gSaveBlock2Ptr->frontier.curChallengeBattleNum); // Ensure trainer wasn't previously fought in this challenge. for (i = 0; i < gSaveBlock2Ptr->frontier.curChallengeBattleNum; i++) { if (gSaveBlock2Ptr->frontier.battledTrainerIds[i] == id) break; } if (i == gSaveBlock2Ptr->frontier.curChallengeBattleNum) break; } gTrainerBattleOpponent_A = id; SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_A, 0); if (gSaveBlock2Ptr->frontier.curChallengeBattleNum + 1 < 7) gSaveBlock2Ptr->frontier.battledTrainerIds[gSaveBlock2Ptr->frontier.curChallengeBattleNum] = gTrainerBattleOpponent_A; } } } extern const u16 gUnknown_085DFA1A[][2]; extern const u16 gUnknown_085DF9FA[][2]; 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 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 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; GetFacilityEnemyMonLevel(); // Pointless function call. if (trainerId == BATTLE_TOWER_EREADER_TRAINER_ID) { facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass; } else if (trainerId == TRAINER_FRONTIER_BRAIN) { SetFrontierBrainTrainerGfxId(); return; } else if (trainerId < BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID) { facilityClass = gFacilityTrainers[trainerId].facilityClass; } else if (trainerId < BATTLE_TOWER_RECORD_APPRENTICE_BASE_ID) { facilityClass = gSaveBlock2Ptr->frontier.records[trainerId - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID].facilityClass; } else { facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - BATTLE_TOWER_RECORD_APPRENTICE_BASE_ID].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(BATTLE_TOWER_EREADER_TRAINER_ID, 0); } u8 GetBattleFacilityTrainerGfxId(u16 trainerId) { u32 i; u8 facilityClass; u8 trainerObjectGfxId; GetFacilityEnemyMonLevel(); // Pointless function call. if (trainerId == BATTLE_TOWER_EREADER_TRAINER_ID) { facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass; } else if (trainerId < BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID) { facilityClass = gFacilityTrainers[trainerId].facilityClass; } else if (trainerId < BATTLE_TOWER_RECORD_APPRENTICE_BASE_ID) { facilityClass = gSaveBlock2Ptr->frontier.records[trainerId - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID].facilityClass; } else { facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - BATTLE_TOWER_RECORD_APPRENTICE_BASE_ID].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(union BattleTowerRecord *newRecord) { u16 slotValues[6]; u16 slotIds[6]; s32 i, j, k; s32 slotsCount = 0; struct EmeraldBattleTowerRecord *newRecordEm = &newRecord->emerald; // 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.records[i].trainerId[j] != newRecordEm->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.records[i].name[j] != newRecordEm->name[j]) break; if (newRecordEm->name[j] == EOS) { k = PLAYER_NAME_LENGTH; break; } } } if (k == PLAYER_NAME_LENGTH) break; } if (i < 5) { gSaveBlock2Ptr->frontier.records[i] = *newRecordEm; return; } // Find an empty record slot. for (i = 0; i < 5; i++) { if (gSaveBlock2Ptr->frontier.records[i].winStreak == 0) break; } if (i < 5) { gSaveBlock2Ptr->frontier.records[i] = *newRecordEm; return; } // Find possible slots to replace the record. slotValues[0] = gSaveBlock2Ptr->frontier.records[0].winStreak; slotIds[0] = 0; slotsCount++; for (i = 1; i < 5; i++) { for (j = 0; j < slotsCount; j++) { if (gSaveBlock2Ptr->frontier.records[i].winStreak < slotValues[j]) { j = 0; slotsCount = 1; slotValues[0] = gSaveBlock2Ptr->frontier.records[i].winStreak; slotIds[0] = i; break; } else if (gSaveBlock2Ptr->frontier.records[i].winStreak > slotValues[j]) { break; } } if (j == slotsCount) { slotValues[slotsCount] = gSaveBlock2Ptr->frontier.records[i].winStreak; slotIds[slotsCount] = i; slotsCount++; } } i = Random() % slotsCount; gSaveBlock2Ptr->frontier.records[slotIds[i]] = *newRecordEm; } u8 GetFrontierTrainerFrontSpriteId(u16 trainerId) { GetFacilityEnemyMonLevel(); // Pointless function call if (trainerId == BATTLE_TOWER_EREADER_TRAINER_ID) { return gFacilityClassToPicIndex[gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass]; } else if (trainerId == TRAINER_FRONTIER_BRAIN) { return GetFrontierBrainTrainerPicIndex(); } else if (trainerId < BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID) { return gFacilityClassToPicIndex[gFacilityTrainers[trainerId].facilityClass]; } else if (trainerId < BATTLE_TOWER_RECORD_APPRENTICE_BASE_ID) { if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) return gFacilityClassToPicIndex[sub_818649C()]; else return gFacilityClassToPicIndex[gSaveBlock2Ptr->frontier.records[trainerId - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID].facilityClass]; } else { if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) return gFacilityClassToPicIndex[gApprentices[sub_81864A8()].facilityClass]; else return gFacilityClassToPicIndex[gApprentices[gSaveBlock2Ptr->apprentices[trainerId - BATTLE_TOWER_RECORD_APPRENTICE_BASE_ID].id].facilityClass]; } } u8 GetFrontierOpponentClass(u16 trainerId) { u8 trainerClass = 0; GetFacilityEnemyMonLevel(); // Pointless function call. if (trainerId == BATTLE_TOWER_EREADER_TRAINER_ID) { 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 < BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID) { trainerClass = gFacilityClassToTrainerClass[gFacilityTrainers[trainerId].facilityClass]; } else if (trainerId < BATTLE_TOWER_RECORD_APPRENTICE_BASE_ID) { if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) { trainerClass = gFacilityClassToTrainerClass[sub_818649C()]; } else { trainerClass = gFacilityClassToTrainerClass[gSaveBlock2Ptr->frontier.records[trainerId - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID].facilityClass]; asm(""); } } else { if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) { trainerClass = gFacilityClassToTrainerClass[gApprentices[sub_81864A8()].facilityClass]; } else { trainerClass = gFacilityClassToTrainerClass[gApprentices[gSaveBlock2Ptr->apprentices[trainerId - BATTLE_TOWER_RECORD_APPRENTICE_BASE_ID].id].facilityClass]; asm(""); } } return trainerClass; } u8 GetFrontierTrainerFacilityClass(u16 trainerId) { u8 facilityClass; GetFacilityEnemyMonLevel(); // Pointless function call. if (trainerId == BATTLE_TOWER_EREADER_TRAINER_ID) { facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass; } else if (trainerId < BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID) { facilityClass = gFacilityTrainers[trainerId].facilityClass; } else if (trainerId < BATTLE_TOWER_RECORD_APPRENTICE_BASE_ID) { if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) facilityClass = sub_818649C(); else facilityClass = gSaveBlock2Ptr->frontier.records[trainerId - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID].facilityClass; } else { if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) facilityClass = gApprentices[sub_81864A8()].facilityClass; else facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - BATTLE_TOWER_RECORD_APPRENTICE_BASE_ID].id].facilityClass; } return facilityClass; } void GetFrontierTrainerName(u8 *dst, u16 trainerId) { s32 i = 0; GetFacilityEnemyMonLevel(); // Pointless function call. if (trainerId == BATTLE_TOWER_EREADER_TRAINER_ID) { 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 < BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID) { for (i = 0; i < PLAYER_NAME_LENGTH; i++) dst[i] = gFacilityTrainers[trainerId].trainerName[i]; } else if (trainerId < BATTLE_TOWER_RECORD_APPRENTICE_BASE_ID) { if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) { sub_8186468(dst); return; } else { struct EmeraldBattleTowerRecord *record = &gSaveBlock2Ptr->frontier.records[trainerId - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID]; TVShowConvertInternationalString(dst, record->name, record->language); return; } } else { u8 id, language; if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) { id = sub_81864A8(); language = sub_81864C0(); } else { struct Apprentice *apprentice = &gSaveBlock2Ptr->apprentices[trainerId - BATTLE_TOWER_RECORD_APPRENTICE_BASE_ID]; id = apprentice->id; language = apprentice->language; } TVShowConvertInternationalString(dst, GetApprenticeNameInLanguage(id, language), language); return; } dst[i] = EOS; } bool8 IsFrontierTrainerFemale(u16 trainerId) { u32 i; u8 facilityClass; GetFacilityEnemyMonLevel(); // Pointless function call. if (trainerId == BATTLE_TOWER_EREADER_TRAINER_ID) { facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass; } else if (trainerId == TRAINER_FRONTIER_BRAIN) { return IsFrontierBrainFemale(); } else if (trainerId < BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID) { facilityClass = gFacilityTrainers[trainerId].facilityClass; } else if (trainerId < BATTLE_TOWER_RECORD_APPRENTICE_BASE_ID) { facilityClass = gSaveBlock2Ptr->frontier.records[trainerId - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID].facilityClass; } else { facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - BATTLE_TOWER_RECORD_APPRENTICE_BASE_ID].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 sub_8163048(u8 monsCount) { ZeroEnemyPartyMons(); sub_81630C4(gTrainerBattleOpponent_A, 0, monsCount); } void sub_816306C(u8 monsCount) { ZeroEnemyPartyMons(); sub_81630C4(gTrainerBattleOpponent_A, 0, monsCount); sub_81630C4(gTrainerBattleOpponent_B, 3, monsCount); } void sub_81630A0(u8 monsCount) { ZeroEnemyPartyMons(); sub_8165EA4(gTrainerBattleOpponent_A, 0, monsCount); } void sub_81630C4(u16 trainerId, u8 firstMonId, u8 monCount) { s32 i, j; u16 chosenMonIndices[4]; u8 friendship = 0xFF; u8 level = GetFacilityEnemyMonLevel(); u8 fixedIV = 0; u8 bfMonCount; const u16 *bfMonPool = NULL; u32 otID = 0; if (trainerId < BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID) { // Normal battle frontier trainer. fixedIV = GetFrontierTrainerFixedIvs(trainerId); bfMonPool = gFacilityTrainers[gTrainerBattleOpponent_A].bfMonPool; } else if (trainerId == BATTLE_TOWER_EREADER_TRAINER_ID) { 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 < BATTLE_TOWER_RECORD_APPRENTICE_BASE_ID) { // Record mixed player. for (j = 0, i = firstMonId; i < firstMonId + monCount; j++, i++) { if (gSaveBlock2Ptr->frontier.records[trainerId - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID].party[j].species != 0 && gSaveBlock2Ptr->frontier.records[trainerId - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID].party[j].level <= level) { sub_8068338(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.records[trainerId - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID].party[j], FALSE); } } return; } else { // Apprentice. for (i = firstMonId; i < firstMonId + 3; i++) CreateApprenticeMon(&gEnemyParty[i], &gSaveBlock2Ptr->apprentices[trainerId - BATTLE_TOWER_RECORD_APPRENTICE_BASE_ID], 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. 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->monData[i].species, level, fixedIV, 8); friendship = 0xFF; for (j = 0; j < 4; j++) { if (apprentice->monData[i].moves[j] == MOVE_FRUSTRATION) friendship = 0; } SetMonData(&gEnemyParty[firstMonId + i], MON_DATA_FRIENDSHIP, &friendship); SetMonData(&gEnemyParty[firstMonId + i], MON_DATA_HELD_ITEM, &apprentice->monData[i].item); } } u16 RandomizeFacilityTrainerMonId(u16 trainerId) { u32 monPoolId; u8 level = GetFacilityEnemyMonLevel(); const u16 *bfMonPool = gFacilityTrainers[trainerId].bfMonPool; u8 bfMonCount = 0; /* I had to use ugly C tricks to get this part to match. A cleaner version would look like this for (bfMonCount = 0; bfMonPool[bfMonCount] != 0xFFFF; bfMonCount++) ; */ monPoolId = bfMonPool[bfMonCount]; goto COMPARE; while (1) { bfMonCount++; monPoolId = bfMonPool[bfMonCount]; COMPARE: if (monPoolId == 0xFFFF) break; } do { monPoolId = bfMonPool[Random() % bfMonCount]; } while((level == 50 || level == 20) && monPoolId > 849); return monPoolId; } void sub_8163590(void) { ZeroEnemyPartyMons(); if (gSaveBlock2Ptr->frontier.lvlMode != FRONTIER_LVL_TENT) sub_81635D4(gTrainerBattleOpponent_A, 0); else sub_816379C(gTrainerBattleOpponent_A, 0); } extern u16 gUnknown_03006298[]; void sub_81635D4(u16 trainerId, u8 firstMonId) { u8 i, j; u8 friendship; u8 level; u8 fixedIV; u32 otID; if (trainerId < BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID) { u8 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; // Unused variable. u8 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); u8 r1 = gSaveBlock2Ptr->frontier.field_CE0[battleMode][0] / 7; if (gSaveBlock2Ptr->frontier.curChallengeBattleNum < 6) fixedIV = sub_81A6CA8(r1, 0); else fixedIV = sub_81A6CA8(r1, 1); } else if (trainerId == BATTLE_TOWER_EREADER_TRAINER_ID) { 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 = GetFacilityEnemyMonLevel(); 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]); } } void sub_816379C(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 ConvertBattleFrontierTrainerSpeechToString(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; } }