#include "global.h" #include "battle.h" #include "battle_setup.h" #include "battle_tower.h" #include "battle_transition.h" #include "data2.h" #include "easy_chat.h" #include "constants/easy_chat.h" #include "event_data.h" #include "item.h" #include "constants/items.h" #include "main.h" #include "constants/map_objects.h" #include "constants/moves.h" #include "new_game.h" #include "overworld.h" #include "pokedex.h" #include "random.h" #include "save.h" #include "script_pokemon_80C4.h" #include "constants/species.h" #include "string_util.h" #include "task.h" #include "text.h" #include "trainer.h" #include "tv.h" #include "constants/vars.h" #include "ewram.h" #if ENGLISH #include "data/battle_tower/trainers.h" #elif GERMAN #include "data/battle_tower/trainers_de.h" #endif static const u16 sBattleTowerHeldItems[] = { ITEM_NONE, ITEM_KINGS_ROCK, ITEM_SITRUS_BERRY, ITEM_ORAN_BERRY, ITEM_CHESTO_BERRY, ITEM_HARD_STONE, ITEM_FOCUS_BAND, ITEM_PERSIM_BERRY, ITEM_MIRACLE_SEED, ITEM_BERRY_JUICE, ITEM_MACHO_BRACE, ITEM_SILVER_POWDER, ITEM_CHERI_BERRY, ITEM_BLACK_GLASSES, ITEM_BLACK_BELT, ITEM_SOUL_DEW, ITEM_CHOICE_BAND, ITEM_MAGNET, ITEM_SILK_SCARF, ITEM_WHITE_HERB, ITEM_DEEP_SEA_SCALE, ITEM_DEEP_SEA_TOOTH, ITEM_MYSTIC_WATER, ITEM_SHARP_BEAK, ITEM_QUICK_CLAW, ITEM_LEFTOVERS, ITEM_RAWST_BERRY, ITEM_LIGHT_BALL, ITEM_POISON_BARB, ITEM_NEVER_MELT_ICE, ITEM_ASPEAR_BERRY, ITEM_SPELL_TAG, ITEM_BRIGHT_POWDER, ITEM_LEPPA_BERRY, ITEM_SCOPE_LENS, ITEM_TWISTED_SPOON, ITEM_METAL_COAT, ITEM_MENTAL_HERB, ITEM_CHARCOAL, ITEM_PECHA_BERRY, ITEM_SOFT_SAND, ITEM_LUM_BERRY, ITEM_DRAGON_SCALE, ITEM_DRAGON_FANG, ITEM_IAPAPA_BERRY, ITEM_WIKI_BERRY, ITEM_SEA_INCENSE, ITEM_SHELL_BELL, ITEM_SALAC_BERRY, ITEM_LANSAT_BERRY, ITEM_APICOT_BERRY, ITEM_STARF_BERRY, ITEM_LIECHI_BERRY, ITEM_STICK, ITEM_LAX_INCENSE, ITEM_AGUAV_BERRY, ITEM_FIGY_BERRY, ITEM_THICK_CLUB, ITEM_MAGO_BERRY, ITEM_METAL_POWDER, ITEM_PETAYA_BERRY, ITEM_LUCKY_PUNCH, ITEM_GANLON_BERRY, }; #include "data/battle_tower/level_50_mons.h" #include "data/battle_tower/level_100_mons.h" static const u8 sMaleTrainerClasses[] = { FACILITY_CLASS_RUIN_MANIAC, FACILITY_CLASS_TUBER_M, FACILITY_CLASS_COOL_TRAINER_M, FACILITY_CLASS_RICH_BOY, FACILITY_CLASS_POKEMANIAC, FACILITY_CLASS_SWIMMER_M, FACILITY_CLASS_BLACK_BELT, FACILITY_CLASS_GUITARIST, FACILITY_CLASS_KINDLER, FACILITY_CLASS_CAMPER, FACILITY_CLASS_BUG_MANIAC, FACILITY_CLASS_PSYCHIC_M, FACILITY_CLASS_GENTLEMAN, FACILITY_CLASS_SCHOOL_KID_M, FACILITY_CLASS_POKEFAN_M, FACILITY_CLASS_EXPERT_M, FACILITY_CLASS_YOUNGSTER, FACILITY_CLASS_FISHERMAN, FACILITY_CLASS_CYCLING_TRIATHLETE_M, FACILITY_CLASS_RUNNING_TRIATHLETE_M, FACILITY_CLASS_SWIMMING_TRIATHLETE_M, FACILITY_CLASS_DRAGON_TAMER, FACILITY_CLASS_BIRD_KEEPER, FACILITY_CLASS_NINJA_BOY, FACILITY_CLASS_SAILOR, FACILITY_CLASS_COLLECTOR, FACILITY_CLASS_POKEMON_BREEDER_M, FACILITY_CLASS_POKEMON_RANGER_M, FACILITY_CLASS_BUG_CATCHER, FACILITY_CLASS_HIKER, }; static const u8 sFemaleTrainerClasses[] = { FACILITY_CLASS_AROMA_LADY, FACILITY_CLASS_TUBER_F, FACILITY_CLASS_COOL_TRAINER_F, FACILITY_CLASS_HEX_MANIAC, FACILITY_CLASS_LADY, FACILITY_CLASS_BEAUTY, FACILITY_CLASS_PSYCHIC_F, FACILITY_CLASS_SCHOOL_KID_F, FACILITY_CLASS_POKEFAN_F, FACILITY_CLASS_EXPERT_F, FACILITY_CLASS_CYCLING_TRIATHLETE_F, FACILITY_CLASS_RUNNING_TRIATHLETE_F, FACILITY_CLASS_SWIMMING_TRIATHLETE_F, FACILITY_CLASS_BATTLE_GIRL, FACILITY_CLASS_PARASOL_LADY, FACILITY_CLASS_SWIMMER_F, FACILITY_CLASS_PICNICKER, FACILITY_CLASS_POKEMON_BREEDER_F, FACILITY_CLASS_POKEMON_RANGER_F, FACILITY_CLASS_LASS, }; static const u8 sMaleTrainerGfxIds[] = { MAP_OBJ_GFX_HIKER, MAP_OBJ_GFX_TUBER_M, MAP_OBJ_GFX_MAN_4, MAP_OBJ_GFX_BOY_4, MAP_OBJ_GFX_MANIAC, MAP_OBJ_GFX_RUNNING_TRIATHLETE_M, MAP_OBJ_GFX_BLACK_BELT, MAP_OBJ_GFX_MAN_6, MAP_OBJ_GFX_MAN_6, MAP_OBJ_GFX_CAMPER, MAP_OBJ_GFX_MANIAC, MAP_OBJ_GFX_PSYCHIC_M, MAP_OBJ_GFX_GENTLEMAN, MAP_OBJ_GFX_SCHOOL_KID_M, MAP_OBJ_GFX_MAN_3, MAP_OBJ_GFX_OLD_MAN_1, MAP_OBJ_GFX_YOUNGSTER, MAP_OBJ_GFX_FISHERMAN, MAP_OBJ_GFX_CYCLING_TRIATHLETE_M, MAP_OBJ_GFX_RUNNING_TRIATHLETE_M, MAP_OBJ_GFX_RUNNING_TRIATHLETE_M, MAP_OBJ_GFX_MAN_4, MAP_OBJ_GFX_MAN_6, MAP_OBJ_GFX_LITTLE_BOY_1, MAP_OBJ_GFX_SAILOR, MAP_OBJ_GFX_MANIAC, MAP_OBJ_GFX_MAN_5, MAP_OBJ_GFX_CAMPER, MAP_OBJ_GFX_BUG_CATCHER, MAP_OBJ_GFX_HIKER, }; static const u8 sFemaleTrainerGfxIds[] = { MAP_OBJ_GFX_WOMAN_3, MAP_OBJ_GFX_TUBER_F, MAP_OBJ_GFX_WOMAN_7, MAP_OBJ_GFX_WOMAN_1, MAP_OBJ_GFX_WOMAN_3, MAP_OBJ_GFX_BEAUTY, MAP_OBJ_GFX_LASS, MAP_OBJ_GFX_GIRL_3, MAP_OBJ_GFX_WOMAN_2, MAP_OBJ_GFX_OLD_WOMAN_1, MAP_OBJ_GFX_CYCLING_TRIATHLETE_F, MAP_OBJ_GFX_RUNNING_TRIATHLETE_F, MAP_OBJ_GFX_RUNNING_TRIATHLETE_F, MAP_OBJ_GFX_GIRL_3, MAP_OBJ_GFX_WOMAN_7, MAP_OBJ_GFX_RUNNING_TRIATHLETE_F, MAP_OBJ_GFX_PICNICKER, MAP_OBJ_GFX_WOMAN_3, MAP_OBJ_GFX_PICNICKER, MAP_OBJ_GFX_LASS, }; const u16 gBattleTowerBannedSpecies[] = { SPECIES_MEW, SPECIES_MEWTWO, SPECIES_HO_OH, SPECIES_LUGIA, SPECIES_CELEBI, SPECIES_KYOGRE, SPECIES_GROUDON, SPECIES_RAYQUAZA, SPECIES_JIRACHI, SPECIES_DEOXYS, 0xFFFF, }; // Item prizes for battle tower streaks of 5 or fewer sets. static const u16 sShortStreakPrizes[] = { ITEM_HP_UP, ITEM_PROTEIN, ITEM_IRON, ITEM_CALCIUM, ITEM_CARBOS, ITEM_ZINC, }; // Item prizes for battle tower streaks of greater than 5 sets. static const u16 sLongStreakPrizes[] = { ITEM_BRIGHT_POWDER, ITEM_WHITE_HERB, ITEM_QUICK_CLAW, ITEM_LEFTOVERS, ITEM_MENTAL_HERB, ITEM_KINGS_ROCK, ITEM_FOCUS_BAND, ITEM_SCOPE_LENS, ITEM_CHOICE_BAND, }; static void ResetBattleTowerStreak(u8 levelType); static void ValidateBattleTowerRecordChecksums(void); static void PrintEReaderTrainerFarewellMessage(void); extern void SetBattleTowerTrainerGfxId(u8); static void SaveCurrentWinStreak(void); static void sub_8135CFC(void); static void CheckMonBattleTowerBanlist(u16, u16, u16, u8, u8, u16 *, u16 *, u8 *); static void ClearEReaderTrainer(struct BattleTowerEReaderTrainer *); static void SetBattleTowerRecordChecksum(struct BattleTowerRecord *); static void ClearBattleTowerRecord(struct BattleTowerRecord *); extern const u8 BattleText_Format3[]; extern const u8 BattleText_Format4[]; extern const u8 BattleText_Format5[]; extern const u8 BattleText_Format6[]; extern const u8 BattleText_Format7[]; extern const u8 BattleText_Format8[]; extern const u8 BattleText_Format9[]; extern u16 gSpecialVar_0x8004; extern u8 gTrainerClassToPicIndex[]; extern u8 gTrainerClassToNameIndex[]; extern u16 gTrainerBattleOpponent; extern u16 gBattleTypeFlags; extern u8 gSelectedOrderFromParty[]; extern u8 gBattleOutcome; extern struct Pokemon gUnknown_030042FC[]; extern struct BattlePokemon gBattleMons[]; void sub_8134548(void) { u8 var1 = 0; s32 levelType; for (levelType = 0; levelType < 2; levelType++) { switch (gSaveBlock2.battleTower.var_4AE[levelType]) { case 0: default: ResetBattleTowerStreak(levelType); if (!var1) VarSet(VAR_TEMP_0, 5); break; case 1: ResetBattleTowerStreak(levelType); VarSet(VAR_TEMP_0, 1); var1++; break; case 4: VarSet(VAR_TEMP_0, 2); var1++; break; case 5: VarSet(VAR_TEMP_0, 3); var1++; break; case 2: VarSet(VAR_TEMP_0, 4); var1++; break; case 3: case 6: break; } } if ((gSaveBlock2.battleTower.var_4AE[0] == 3 || gSaveBlock2.battleTower.var_4AE[0] == 6) && (gSaveBlock2.battleTower.var_4AE[1] == 3 || gSaveBlock2.battleTower.var_4AE[1] == 6)) VarSet(VAR_TEMP_0, 5); ValidateBattleTowerRecordChecksums(); } void ResetBattleTowerStreak(u8 levelType) { gSaveBlock2.battleTower.var_4AE[levelType] = 0; gSaveBlock2.battleTower.curChallengeBattleNum[levelType] = 1; gSaveBlock2.battleTower.curStreakChallengesNum[levelType] = 1; } // Checks if the next trainer in Battle Tower should be the E-Reader trainer. bool8 ShouldBattleEReaderTrainer(u8 levelType, u16 winStreak) { u8 trainerTeamLevel; u8 monLevel; s32 i; u16 validPartySpecies[6]; u16 validPartyHeldItems[6]; u8 numValid; numValid = 0; ValidateEReaderTrainer(); if (gSpecialVar_Result != 0 || gSaveBlock2.battleTower.ereaderTrainer.winStreak != winStreak) return FALSE; if (levelType != 0) trainerTeamLevel = 100; else trainerTeamLevel = 50; for (i = 0; i < 3; i++) { monLevel = gSaveBlock2.battleTower.ereaderTrainer.party[i].level; if (gSaveBlock2.battleTower.ereaderTrainer.party[i].level != trainerTeamLevel) return FALSE; CheckMonBattleTowerBanlist( gSaveBlock2.battleTower.ereaderTrainer.party[i].species, gSaveBlock2.battleTower.ereaderTrainer.party[i].heldItem, 1, levelType, monLevel, validPartySpecies, validPartyHeldItems, &numValid); } return (numValid == 3); } bool8 sub_81346F4(void) { s32 recordIndex, i; u8 battleTowerLevelType; u16 winStreak; bool8 retVal; s32 numCandidates; u32 trainerIds[5]; numCandidates = 0; battleTowerLevelType = gSaveBlock2.battleTower.battleTowerLevelType; winStreak = GetCurrentBattleTowerWinStreak(battleTowerLevelType); if (ShouldBattleEReaderTrainer(battleTowerLevelType, winStreak)) { gSaveBlock2.battleTower.battleTowerTrainerId = BATTLE_TOWER_EREADER_TRAINER_ID; retVal = TRUE; } else { // Check if one of the battle tower trainers from record mixing should be the next trainer. for (recordIndex = 0; recordIndex < 5; recordIndex++) { struct BattleTowerRecord *record = &gSaveBlock2.battleTower.records[recordIndex]; u32 recordHasData = 0; u32 checksum = 0; for (i = 0; i < sizeof(struct BattleTowerRecord) / sizeof(u32) - 1; i++) { recordHasData |= ((u32 *)record)[i]; checksum += ((u32 *)record)[i]; } if (gSaveBlock2.battleTower.records[recordIndex].winStreak == winStreak && gSaveBlock2.battleTower.records[recordIndex].battleTowerLevelType == battleTowerLevelType && recordHasData && gSaveBlock2.battleTower.records[recordIndex].checksum == checksum) { trainerIds[numCandidates] = recordIndex; numCandidates++; } } if (numCandidates == 0) { retVal = FALSE; } else { gSaveBlock2.battleTower.battleTowerTrainerId = trainerIds[Random() % numCandidates] + BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID; retVal = TRUE; } } return retVal; } // void sub_81347F8(void) // { // bool8 levelType = gSaveBlock2.battleTower.battleTowerLevelType; // if (sub_81346F4()) // { // SetBattleTowerTrainerGfxId(gSaveBlock2.battleTower.battleTowerTrainerId); // gSaveBlock2.battleTower.var_4C1[gSaveBlock2.battleTower.curChallengeBattleNum[levelType]] = gSaveBlock2.battleTower.battleTowerTrainerId; // } // else // { // u16 var1; // if (gSaveBlock2.battleTower.curStreakChallengesNum[levelType] > 7) // { // while (1) // { // s32 i; // u32 temp = ((Random() & 0xFF) * 30) >> 8; // var1 = temp + 70; // for (i = 0; i < gSaveBlock2.battleTower.curChallengeBattleNum[levelType] - 1 && var1 != gSaveBlock2.battleTower.var_4C1[i]; i++) // TODO: [i + 1]??? // { } // if (i == gSaveBlock2.battleTower.curChallengeBattleNum[levelType] - 1) // { // break; // } // } // } // else // { // if (gSaveBlock2.battleTower.curChallengeBattleNum[levelType] == 7) // { // while (1) // { // s32 i; // u32 temp = ((Random() & 0xFF) * 5); // u32 temp2 = (((gSaveBlock2.battleTower.curStreakChallengesNum[levelType] - 1) * 10) + 20); // var1 = temp / 128 + temp2; // for (i = 0; i < gSaveBlock2.battleTower.curChallengeBattleNum[levelType] - 1 && gSaveBlock2.battleTower.var_4C1[i] != var1; i++) // TODO: [i + 1]???? // { } // if (i == gSaveBlock2.battleTower.curChallengeBattleNum[levelType] - 1) // { // break; // } // } // } // else // { // while (1) // { // s32 i; // u32 temp = ((Random() & 0xFF) * 320); // u32 temp2 = ((gSaveBlock2.battleTower.curStreakChallengesNum[levelType] - 1) * 10); // var1 = temp + temp2; // for (i = 0; i < gSaveBlock2.battleTower.curChallengeBattleNum[levelType] - 1 && gSaveBlock2.battleTower.var_4C1[i] != var1; i++) // { } // if (i == gSaveBlock2.battleTower.curChallengeBattleNum[levelType] - 1) // { // break; // } // } // } // } // gSaveBlock2.battleTower.battleTowerTrainerId = var1; // SetBattleTowerTrainerGfxId(gSaveBlock2.battleTower.battleTowerTrainerId); // if (gSaveBlock2.battleTower.curChallengeBattleNum[levelType] < 7) // { // gSaveBlock2.battleTower.var_4C1[gSaveBlock2.battleTower.curChallengeBattleNum[levelType]] = gSaveBlock2.battleTower.battleTowerTrainerId; // } // } // } NAKED void sub_81347F8(void) { asm(".syntax unified\n\ push {r4-r7,lr}\n\ mov r7, r10\n\ mov r6, r9\n\ mov r5, r8\n\ push {r5-r7}\n\ ldr r5, _08134838 @ =gSaveBlock2\n\ ldr r1, _0813483C @ =0x00000554\n\ adds r0, r5, r1\n\ ldrb r0, [r0]\n\ lsls r0, 31\n\ lsrs r6, r0, 31\n\ bl sub_81346F4\n\ lsls r0, 24\n\ cmp r0, 0\n\ beq _08134848\n\ ldr r2, _08134840 @ =0x00000564\n\ adds r4, r5, r2\n\ ldrb r0, [r4]\n\ bl SetBattleTowerTrainerGfxId\n\ lsls r0, r6, 1\n\ movs r3, 0xAB\n\ lsls r3, 3\n\ adds r1, r5, r3\n\ adds r0, r1\n\ ldrh r0, [r0]\n\ adds r0, r5, r0\n\ ldr r1, _08134844 @ =0x00000569\n\ adds r0, r1\n\ ldrb r1, [r4]\n\ b _081349DC\n\ .align 2, 0\n\ _08134838: .4byte gSaveBlock2\n\ _0813483C: .4byte 0x00000554\n\ _08134840: .4byte 0x00000564\n\ _08134844: .4byte 0x00000569\n\ _08134848:\n\ lsls r0, r6, 1\n\ ldr r2, _081348D8 @ =0x0000055c\n\ adds r1, r5, r2\n\ adds r1, r0, r1\n\ ldrh r1, [r1]\n\ adds r7, r0, 0\n\ cmp r1, 0x7\n\ bls _0813485A\n\ b _0813495C\n\ _0813485A:\n\ movs r3, 0xAB\n\ lsls r3, 3\n\ adds r0, r5, r3\n\ adds r1, r7, r0\n\ ldrh r0, [r1]\n\ cmp r0, 0x7\n\ bne _081348E4\n\ adds r6, r5, 0\n\ mov r9, r7\n\ adds r5, r1, 0\n\ ldr r0, _081348DC @ =0x0000056a\n\ adds r0, r6\n\ mov r10, r0\n\ mov r8, r5\n\ _08134876:\n\ bl Random\n\ movs r1, 0xFF\n\ ands r1, r0\n\ lsls r2, r1, 2\n\ adds r2, r1\n\ ldr r1, _081348D8 @ =0x0000055c\n\ adds r4, r6, r1\n\ mov r3, r9\n\ adds r0, r3, r4\n\ ldrh r1, [r0]\n\ subs r1, 0x1\n\ lsls r0, r1, 2\n\ adds r0, r1\n\ lsls r0, 1\n\ adds r0, 0x14\n\ lsrs r2, 7\n\ adds r2, r0\n\ lsls r2, 16\n\ lsrs r2, 16\n\ movs r1, 0\n\ ldrh r0, [r5]\n\ subs r0, 0x1\n\ cmp r1, r0\n\ bge _081348C8\n\ mov r3, r10\n\ ldrb r0, [r3]\n\ cmp r0, r2\n\ beq _081348C8\n\ subs r0, r4, 0x4\n\ adds r0, r7, r0\n\ ldrh r0, [r0]\n\ subs r3, r0, 0x1\n\ adds r4, 0xE\n\ _081348BA:\n\ adds r1, 0x1\n\ cmp r1, r3\n\ bge _081348C8\n\ adds r0, r1, r4\n\ ldrb r0, [r0]\n\ cmp r0, r2\n\ bne _081348BA\n\ _081348C8:\n\ mov r3, r8\n\ ldrh r0, [r3]\n\ subs r0, 0x1\n\ ldr r4, _081348E0 @ =gSaveBlock2\n\ cmp r1, r0\n\ bne _08134876\n\ b _081349B6\n\ .align 2, 0\n\ _081348D8: .4byte 0x0000055c\n\ _081348DC: .4byte 0x0000056a\n\ _081348E0: .4byte gSaveBlock2\n\ _081348E4:\n\ adds r6, r5, 0\n\ mov r9, r7\n\ adds r5, r1, 0\n\ ldr r2, _08134950 @ =0x0000056a\n\ adds r2, r6\n\ mov r10, r2\n\ mov r8, r5\n\ _081348F2:\n\ bl Random\n\ movs r1, 0xFF\n\ ands r1, r0\n\ lsls r0, r1, 2\n\ adds r0, r1\n\ lsrs r2, r0, 6\n\ ldr r3, _08134954 @ =0x0000055c\n\ adds r4, r6, r3\n\ mov r1, r9\n\ adds r0, r1, r4\n\ ldrh r1, [r0]\n\ subs r1, 0x1\n\ lsls r0, r1, 2\n\ adds r0, r1\n\ lsls r0, 1\n\ adds r0, r2, r0\n\ lsls r0, 16\n\ lsrs r2, r0, 16\n\ movs r1, 0\n\ ldrh r0, [r5]\n\ subs r0, 0x1\n\ cmp r1, r0\n\ bge _08134942\n\ mov r3, r10\n\ ldrb r0, [r3]\n\ cmp r0, r2\n\ beq _08134942\n\ subs r0, r4, 0x4\n\ adds r0, r7, r0\n\ ldrh r0, [r0]\n\ subs r3, r0, 0x1\n\ adds r4, 0xE\n\ _08134934:\n\ adds r1, 0x1\n\ cmp r1, r3\n\ bge _08134942\n\ adds r0, r1, r4\n\ ldrb r0, [r0]\n\ cmp r0, r2\n\ bne _08134934\n\ _08134942:\n\ mov r3, r8\n\ ldrh r0, [r3]\n\ subs r0, 0x1\n\ ldr r4, _08134958 @ =gSaveBlock2\n\ cmp r1, r0\n\ bne _081348F2\n\ b _081349B6\n\ .align 2, 0\n\ _08134950: .4byte 0x0000056a\n\ _08134954: .4byte 0x0000055c\n\ _08134958: .4byte gSaveBlock2\n\ _0813495C:\n\ movs r2, 0xAB\n\ lsls r2, 3\n\ adds r0, r5, r2\n\ adds r6, r7, r0\n\ ldr r3, _081349EC @ =0x0000056a\n\ adds r3, r5\n\ mov r8, r3\n\ adds r5, r6, 0\n\ _0813496C:\n\ bl Random\n\ movs r1, 0xFF\n\ ands r1, r0\n\ lsls r0, r1, 4\n\ subs r0, r1\n\ lsls r0, 1\n\ asrs r0, 8\n\ adds r0, 0x46\n\ lsls r0, 16\n\ lsrs r2, r0, 16\n\ movs r1, 0\n\ ldrh r0, [r6]\n\ subs r0, 0x1\n\ cmp r1, r0\n\ bge _081349AC\n\ mov r3, r8\n\ ldrb r0, [r3]\n\ ldr r4, _081349F0 @ =gSaveBlock2\n\ cmp r0, r2\n\ beq _081349AC\n\ ldrh r0, [r5]\n\ subs r3, r0, 0x1\n\ ldr r0, _081349EC @ =0x0000056a\n\ adds r4, r0\n\ _0813499E:\n\ adds r1, 0x1\n\ cmp r1, r3\n\ bge _081349AC\n\ adds r0, r1, r4\n\ ldrb r0, [r0]\n\ cmp r0, r2\n\ bne _0813499E\n\ _081349AC:\n\ ldrh r0, [r6]\n\ subs r0, 0x1\n\ ldr r4, _081349F0 @ =gSaveBlock2\n\ cmp r1, r0\n\ bne _0813496C\n\ _081349B6:\n\ ldr r1, _081349F4 @ =0x00000564\n\ adds r0, r4, r1\n\ strb r2, [r0]\n\ ldr r2, _081349F4 @ =0x00000564\n\ adds r5, r4, r2\n\ ldrb r0, [r5]\n\ bl SetBattleTowerTrainerGfxId\n\ movs r3, 0xAB\n\ lsls r3, 3\n\ adds r0, r4, r3\n\ adds r1, r7, r0\n\ ldrh r0, [r1]\n\ cmp r0, 0x6\n\ bhi _081349DE\n\ adds r0, r4, r0\n\ ldr r1, _081349F8 @ =0x00000569\n\ adds r0, r1\n\ ldrb r1, [r5]\n\ _081349DC:\n\ strb r1, [r0]\n\ _081349DE:\n\ pop {r3-r5}\n\ mov r8, r3\n\ mov r9, r4\n\ mov r10, r5\n\ pop {r4-r7}\n\ pop {r0}\n\ bx r0\n\ .align 2, 0\n\ _081349EC: .4byte 0x0000056a\n\ _081349F0: .4byte gSaveBlock2\n\ _081349F4: .4byte 0x00000564\n\ _081349F8: .4byte 0x00000569\n\ .syntax divided\n"); } void SetBattleTowerTrainerGfxId(u8 trainerIndex) { u32 i; u8 trainerClass; if (trainerIndex < BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID) trainerClass = gBattleTowerTrainers[trainerIndex].trainerClass; else if (trainerIndex < BATTLE_TOWER_EREADER_TRAINER_ID) trainerClass = gSaveBlock2.battleTower.records[trainerIndex - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID].trainerClass; else trainerClass = gSaveBlock2.battleTower.ereaderTrainer.trainerClass; for (i = 0; i < 30; i++) { if (sMaleTrainerClasses[i] == trainerClass) break; } if (i != 30) { u8 trainerObjectGfxId = sMaleTrainerGfxIds[i]; VarSet(VAR_OBJ_GFX_ID_0, trainerObjectGfxId); return; } for (i = 0; i < 20; i++) { if (sFemaleTrainerClasses[i] == trainerClass) break; } if (i != 20) { u8 trainerObjectGfxId = sFemaleTrainerGfxIds[i]; VarSet(VAR_OBJ_GFX_ID_0, trainerObjectGfxId); return; } VarSet(VAR_OBJ_GFX_ID_0, MAP_OBJ_GFX_BOY_1); } void SetEReaderTrainerGfxId(void) { SetBattleTowerTrainerGfxId(BATTLE_TOWER_EREADER_TRAINER_ID); } // void sub_8134AC0(struct BattleTowerRecord *record) // { // u16 var1[6]; // u16 var2[6]; // s32 i, j, k; // s16 l = 0; // for (i = 0; i < 5; i++) // { // k = 0; // for (j = 0; j < 4 && gSaveBlock2.battleTower.records[i].trainerId[j] == record->trainerId[j]; j++); // if (j == 4) // { // for (; k < 7 && gSaveBlock2.battleTower.records[i].name[4] == record->name[4]; k++) // { // if (record->name[4] == 0xFF) // { // k = 7; // break; // } // } // } // if (k == 7) // { // break; // } // } // if (i < 5) // { // gSaveBlock2.battleTower.records[i] = *record; // return; // } // i = 0; // while (i < 5) // { // if (gSaveBlock2.battleTower.records[i].winStreak == 0) // { // if (i > 4) // { // break; // } // gSaveBlock2.battleTower.records[i] = *record; // return; // } // i++; // } // var1[0] = gSaveBlock2.battleTower.records[0].winStreak; // var2[0] = 0; // l++; // for (i = 1; i < 5; i++) // { // j = 0; // if (j < l) // { // for (; gSaveBlock2.battleTower.records[i].winStreak <= var1[j]; j++) // { // if (gSaveBlock2.battleTower.records[i].winStreak < var1[j]) // { // j = 0; // l = 1; // var1[0] = gSaveBlock2.battleTower.records[i].winStreak; // var2[0] = i; // break; // } // } // } // if (j == l) // { // var1[l] = gSaveBlock2.battleTower.records[i].winStreak; // var2[l] = i; // l++; // } // } // gSaveBlock2.battleTower.records[var2[(Random() % l)]] = *record; // } NAKED void sub_8134AC0(struct BattleTowerRecord *record) { asm(".syntax unified\n\ push {r4-r7,lr}\n\ mov r7, r10\n\ mov r6, r9\n\ mov r5, r8\n\ push {r5-r7}\n\ sub sp, 0x1C\n\ movs r7, 0\n\ mov r8, r0\n\ movs r5, 0\n\ ldr r0, _08134B3C @ =gSaveBlock2\n\ mov r12, r0\n\ mov r1, r8\n\ ldrb r1, [r1, 0xC]\n\ str r1, [sp, 0x18]\n\ movs r2, 0xAA\n\ lsls r2, 1\n\ add r2, r12\n\ mov r10, r2\n\ mov r9, r5\n\ _08134AE6:\n\ movs r6, 0\n\ movs r3, 0\n\ ldr r0, _08134B40 @ =gSaveBlock2 + 0x158\n\ add r0, r9\n\ ldrb r0, [r0]\n\ ldr r1, [sp, 0x18]\n\ cmp r0, r1\n\ bne _08134B14\n\ movs r0, 0xA4\n\ muls r0, r5\n\ ldr r1, _08134B40 @ =gSaveBlock2 + 0x158\n\ adds r2, r0, r1\n\ mov r4, r8\n\ adds r4, 0xC\n\ _08134B02:\n\ adds r2, 0x1\n\ adds r3, 0x1\n\ cmp r3, 0x3\n\ bgt _08134B14\n\ adds r1, r4, r3\n\ ldrb r0, [r2]\n\ ldrb r1, [r1]\n\ cmp r0, r1\n\ beq _08134B02\n\ _08134B14:\n\ cmp r3, 0x4\n\ bne _08134B54\n\ movs r6, 0\n\ mov r2, r10\n\ ldrb r0, [r2]\n\ mov r3, r8\n\ ldrb r3, [r3, 0x8]\n\ cmp r0, r3\n\ bne _08134B54\n\ adds r1, r0, 0\n\ movs r0, 0xA4\n\ muls r0, r5\n\ adds r0, 0x4\n\ ldr r3, _08134B44 @ =gSaveBlock2 + 0x150\n\ adds r2, r0, r3\n\ _08134B32:\n\ cmp r1, 0xFF\n\ bne _08134B48\n\ movs r6, 0x7\n\ b _08134B54\n\ .align 2, 0\n\ _08134B3C: .4byte gSaveBlock2\n\ _08134B40: .4byte gSaveBlock2 + 0x158\n\ _08134B44: .4byte gSaveBlock2 + 0x150\n\ _08134B48:\n\ adds r6, 0x1\n\ cmp r6, 0x6\n\ bgt _08134B54\n\ ldrb r0, [r2]\n\ cmp r0, r1\n\ beq _08134B32\n\ _08134B54:\n\ cmp r6, 0x7\n\ beq _08134B64\n\ movs r0, 0xA4\n\ add r10, r0\n\ add r9, r0\n\ adds r5, 0x1\n\ cmp r5, 0x4\n\ ble _08134AE6\n\ \n\ _08134B64:\n\ cmp r5, 0x4\n\ bgt _08134B76\n\ movs r0, 0xA4\n\ muls r0, r5\n\ add r0, r12\n\ movs r1, 0xA6\n\ lsls r1, 1\n\ adds r0, r1\n\ b _08134B9E\n\ _08134B76:\n\ movs r5, 0\n\ movs r1, 0xA7\n\ lsls r1, 1\n\ add r1, r12\n\ b _08134B88\n\ _08134B80:\n\ adds r1, 0xA4\n\ adds r5, 0x1\n\ cmp r5, 0x4\n\ bgt _08134BA8\n\ _08134B88:\n\ ldrh r0, [r1]\n\ cmp r0, 0\n\ bne _08134B80\n\ cmp r5, 0x4\n\ bgt _08134BA8\n\ movs r0, 0xA4\n\ muls r0, r5\n\ add r0, r12\n\ movs r2, 0xA6\n\ lsls r2, 1\n\ adds r0, r2\n\ _08134B9E:\n\ mov r1, r8\n\ movs r2, 0xA4\n\ bl memcpy\n\ b _08134C5E\n\ \n\ _08134BA8:\n\ mov r2, sp\n\ movs r3, 0xA7\n\ lsls r3, 1\n\ mov r1, r12\n\ adds r0, r1, r3\n\ ldrh r0, [r0]\n\ movs r1, 0\n\ strh r0, [r2]\n\ add r0, sp, 0xC\n\ strh r1, [r0]\n\ adds r7, 0x1\n\ movs r5, 0x1\n\ add r2, sp, 0xC @var2 = r2\n\ mov r9, r2\n\ mov r10, r3\n\ _08134BC6:\n\ movs r3, 0\n\ adds r0, r5, 0x1\n\ mov r12, r0\n\ cmp r3, r7\n\ bge _08134C0A @ j < l\n\ movs r1, 0xA4\n\ adds r0, r5, 0\n\ muls r0, r1\n\ ldr r2, _08134BFC @ =gSaveBlock2\n\ adds r0, r2\n\ mov r1, r10\n\ adds r4, r0, r1\n\ mov r6, sp\n\ _08134BE0:\n\ lsls r0, r3, 1\n\ add r0, sp\n\ ldrh r2, [r4]\n\ adds r1, r2, 0\n\ ldrh r0, [r0]\n\ cmp r1, r0\n\ bcs _08134C00\n\ movs r3, 0\n\ movs r7, 0x1\n\ strh r2, [r6]\n\ mov r2, r9\n\ strh r5, [r2]\n\ b _08134C0A\n\ .align 2, 0\n\ _08134BFC: .4byte gSaveBlock2\n\ _08134C00:\n\ cmp r1, r0\n\ bhi _08134C0A\n\ adds r3, 0x1\n\ cmp r3, r7\n\ blt _08134BE0\n\ _08134C0A:\n\ cmp r3, r7\n\ bne _08134C2A\n\ lsls r1, r7, 1\n\ mov r3, sp\n\ adds r2, r3, r1\n\ movs r3, 0xA4\n\ adds r0, r5, 0\n\ muls r0, r3\n\ ldr r3, _08134C70 @ =gSaveBlock2\n\ adds r0, r3\n\ add r0, r10\n\ ldrh r0, [r0]\n\ strh r0, [r2]\n\ add r1, r9\n\ strh r5, [r1]\n\ adds r7, 0x1\n\ _08134C2A:\n\ mov r5, r12\n\ cmp r5, 0x4\n\ ble _08134BC6\n\ bl Random\n\ lsls r0, 16\n\ lsrs r0, 16\n\ adds r1, r7, 0\n\ bl __modsi3\n\ adds r5, r0, 0\n\ ldr r2, _08134C70 @ =gSaveBlock2\n\ lsls r0, r5, 1\n\ add r0, sp\n\ adds r0, 0xC\n\ ldrh r1, [r0]\n\ movs r0, 0xA4\n\ muls r0, r1\n\ adds r0, r2\n\ movs r1, 0xA6\n\ lsls r1, 1\n\ adds r0, r1\n\ mov r1, r8\n\ movs r2, 0xA4\n\ bl memcpy\n\ _08134C5E:\n\ add sp, 0x1C\n\ pop {r3-r5}\n\ mov r8, r3\n\ mov r9, r4\n\ mov r10, r5\n\ pop {r4-r7}\n\ pop {r0}\n\ bx r0\n\ .align 2, 0\n\ _08134C70: .4byte gSaveBlock2\n\ .syntax divided\n"); } u8 get_trainer_class_pic_index(void) { if (gSaveBlock2.battleTower.battleTowerTrainerId == BATTLE_TOWER_EREADER_TRAINER_ID) return gTrainerClassToPicIndex[gSaveBlock2.battleTower.ereaderTrainer.trainerClass]; else if (gSaveBlock2.battleTower.battleTowerTrainerId < BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID) return gTrainerClassToPicIndex[gBattleTowerTrainers[gSaveBlock2.battleTower.battleTowerTrainerId].trainerClass]; else return gTrainerClassToPicIndex[gSaveBlock2.battleTower.records[gSaveBlock2.battleTower.battleTowerTrainerId - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID].trainerClass]; } u8 get_trainer_class_name_index(void) { if (gSaveBlock2.battleTower.battleTowerTrainerId == BATTLE_TOWER_EREADER_TRAINER_ID) return gTrainerClassToNameIndex[gSaveBlock2.battleTower.ereaderTrainer.trainerClass]; else if (gSaveBlock2.battleTower.battleTowerTrainerId >= BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID) return gTrainerClassToNameIndex[gSaveBlock2.battleTower.records[gSaveBlock2.battleTower.battleTowerTrainerId - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID].trainerClass]; else return gTrainerClassToNameIndex[gBattleTowerTrainers[gSaveBlock2.battleTower.battleTowerTrainerId].trainerClass]; } void get_trainer_name(u8* dest) { s32 i; if (gSaveBlock2.battleTower.battleTowerTrainerId == BATTLE_TOWER_EREADER_TRAINER_ID) { for (i = 0; i < 7; i++) dest[i] = gSaveBlock2.battleTower.ereaderTrainer.name[i]; } else if (gSaveBlock2.battleTower.battleTowerTrainerId < BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID) { for (i = 0; i < 7; i++) dest[i] = gBattleTowerTrainers[gSaveBlock2.battleTower.battleTowerTrainerId].name[i]; } else { for (i = 0; i < 7; i++) dest[i] = gSaveBlock2.battleTower.records[gSaveBlock2.battleTower.battleTowerTrainerId - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID].name[i]; } dest[i] = EOS; } void FillBattleTowerTrainerParty(void) { s32 partyIndex, i; u16 chosenMonIndices[3]; u8 friendship; u8 level; u8 fixedIV; u8 battleMonsOffset; u8 monPoolSize; u8 teamFlags; const struct BattleTowerPokemon *battleTowerMons; battleMonsOffset = 0; monPoolSize = 60; friendship = 255; ZeroEnemyPartyMons(); // Different trainers have access to different sets of pokemon to use in battle. // The pokemon later in gBattleTowerLevel100Mons or gBattleTowerLevel50Mons are // stronger. Additionally, the later trainers' pokemon are granted higher IVs. if (gSaveBlock2.battleTower.battleTowerTrainerId < 20) { fixedIV = 6; } else if (gSaveBlock2.battleTower.battleTowerTrainerId < 30) { fixedIV = 9; battleMonsOffset = 30; } else if (gSaveBlock2.battleTower.battleTowerTrainerId < 40) { fixedIV = 12; battleMonsOffset = 60; } else if (gSaveBlock2.battleTower.battleTowerTrainerId < 50) { fixedIV = 15; battleMonsOffset = 90; } else if (gSaveBlock2.battleTower.battleTowerTrainerId < 60) { fixedIV = 18; battleMonsOffset = 120; } else if (gSaveBlock2.battleTower.battleTowerTrainerId < 70) { fixedIV = 21; battleMonsOffset = 150; } else if (gSaveBlock2.battleTower.battleTowerTrainerId < 80) { fixedIV = 31; battleMonsOffset = 180; } else if (gSaveBlock2.battleTower.battleTowerTrainerId < BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID) { fixedIV = 31; battleMonsOffset = 200; monPoolSize = 100; } else if (gSaveBlock2.battleTower.battleTowerTrainerId == BATTLE_TOWER_EREADER_TRAINER_ID) { // Load E-Reader trainer's party. for (partyIndex = 0; partyIndex < 3; partyIndex++) sub_803ADE8(&gEnemyParty[partyIndex], &gSaveBlock2.battleTower.ereaderTrainer.party[partyIndex]); return; } else { // Load a battle tower record's party. (From record mixing) for (partyIndex = 0; partyIndex < 3; partyIndex++) { sub_803ADE8( &gEnemyParty[partyIndex], &gSaveBlock2.battleTower.records[gSaveBlock2.battleTower.battleTowerTrainerId - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID].party[partyIndex]); } return; } // Use the appropriate list of pokemon and level depending on the // current challenge type. (level 50 or level 100 challenge) if (gSaveBlock2.battleTower.battleTowerLevelType != 0) { battleTowerMons = gBattleTowerLevel100Mons; level = 100; } else { battleTowerMons = gBattleTowerLevel50Mons; level = 50; } teamFlags = gBattleTowerTrainers[gSaveBlock2.battleTower.battleTowerTrainerId].teamFlags; // 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. Each pokemon must have all of the trainer's team flags // set, as well. If any of those conditions are not met, then the loop starts over // and another pokemon is chosen at random. partyIndex = 0; while (partyIndex != 3) { // Pick a random pokemon index based on the number of pokemon available to choose from // and the starting offset in the battle tower pokemon array. s32 battleMonIndex = ((Random() & 0xFF) * monPoolSize) / 256 + battleMonsOffset; // Ensure the chosen pokemon has compatible team flags with the trainer. if (teamFlags == 0 || (battleTowerMons[battleMonIndex].teamFlags & teamFlags) == teamFlags) { // Ensure this pokemon species isn't a duplicate. for (i = 0; i < partyIndex; i++) { if (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES, NULL) == battleTowerMons[battleMonIndex].species) break; } if (i != partyIndex) continue; // Ensure this pokemon's held item isn't a duplicate. for (i = 0; i < partyIndex; i++) { if (GetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, NULL) != 0 && GetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, NULL) == sBattleTowerHeldItems[battleTowerMons[battleMonIndex].heldItem]) break; } if (i != partyIndex) 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. Perhaps this // is leftover code before the logic for duplicate species and held items was added. //for (i = 0; i < partyIndex && chosenMonIndices[i] != battleMonIndex; i++); for (i = 0; i < partyIndex; i++) { if (chosenMonIndices[i] == battleMonIndex) break; } if (i != partyIndex) continue; chosenMonIndices[partyIndex] = battleMonIndex; // Place the chosen pokemon into the trainer's party. CreateMonWithEVSpread( &gEnemyParty[partyIndex], battleTowerMons[battleMonIndex].species, level, fixedIV, battleTowerMons[battleMonIndex].evSpread); // Give the chosen pokemon its specified moves. for (i = 0; i < 4; i++) { SetMonMoveSlot(&gEnemyParty[partyIndex], battleTowerMons[battleMonIndex].moves[i], i); if (battleTowerMons[battleMonIndex].moves[i] == MOVE_FRUSTRATION) friendship = 0; // MOVE_FRUSTRATION is more powerful the lower the pokemon's friendship is. } SetMonData(&gEnemyParty[partyIndex], MON_DATA_FRIENDSHIP, &friendship); SetMonData(&gEnemyParty[partyIndex], MON_DATA_HELD_ITEM, &sBattleTowerHeldItems[battleTowerMons[battleMonIndex].heldItem]); // The pokemon was successfully added to the trainer's party, so it's safe to move on to // the next party slot. partyIndex++; } } } u32 CountBattleTowerBanlistCaught(void) { s32 i; u32 numCaught = 0; for (i = 0; gBattleTowerBannedSpecies[i] != 0xFFFF; i++) { if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleTowerBannedSpecies[i]), FLAG_GET_CAUGHT)) numCaught++; } return numCaught; } u8 AppendBattleTowerBannedSpeciesName(u16 species, u8 curIndexToAppend, s32 numToAppend) { if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_CAUGHT)) { curIndexToAppend++; switch (curIndexToAppend - 1) { case 0: case 2: case 4: case 6: case 8: case 10: if (numToAppend == curIndexToAppend) StringAppend(gStringVar1, BattleText_Format3); else if (numToAppend > curIndexToAppend) StringAppend(gStringVar1, BattleText_Format4); break; case 1: if (curIndexToAppend == numToAppend) StringAppend(gStringVar1, BattleText_Format3); else StringAppend(gStringVar1, BattleText_Format4); StringAppend(gStringVar1, BattleText_Format7); break; case 3: case 5: case 7: case 9: default: if (curIndexToAppend == numToAppend) StringAppend(gStringVar1, BattleText_Format3); else StringAppend(gStringVar1, BattleText_Format4); StringAppend(gStringVar1, BattleText_Format6); break; } StringAppend(gStringVar1, gSpeciesNames[species]); } return curIndexToAppend; } void CheckMonBattleTowerBanlist(u16 species, u16 heldItem, u16 hp, u8 battleTowerLevelType, u8 monLevel, u16 *validPartySpecies, u16 *validPartyHeldItems, u8 *numValid) { s32 i; u32 counter = 0; if (species == SPECIES_EGG || species == SPECIES_NONE) return; while (1) { if (gBattleTowerBannedSpecies[counter] == 0xFFFF) break; if (gBattleTowerBannedSpecies[counter] == species) break; counter++; } if (gBattleTowerBannedSpecies[counter] != 0xFFFF) return; if (battleTowerLevelType == 0 && monLevel > 50) return; for (i = 0; i < *numValid && validPartySpecies[i] != species ; i++); if (i != *numValid) return; if (heldItem != 0) { for (i = 0; i < *numValid && validPartyHeldItems[i] != heldItem ; i++); if (i != *numValid) return; } validPartySpecies[*numValid] = species; validPartyHeldItems[*numValid] = heldItem; *numValid = *numValid + 1; } void CheckPartyBattleTowerBanlist(void) { s32 i; u16 species2; u16 heldItem; u8 level; u16 hp; u32 numBanlistCaught; u16 validPartySpecies[6]; u16 validPartyHeldItems[6]; u8 counter; counter = 0; for (i = 0; i < PARTY_SIZE; i++) { species2 = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2); heldItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM); level = GetMonData(&gPlayerParty[i], MON_DATA_LEVEL); hp = GetMonData(&gPlayerParty[i], MON_DATA_HP); CheckMonBattleTowerBanlist(species2, heldItem, hp, gSpecialVar_Result, level, validPartySpecies, validPartyHeldItems, &counter); } if (counter < 3) { gStringVar1[0] = 0xFF; gSpecialVar_0x8004 = 1; counter = 0; numBanlistCaught = CountBattleTowerBanlistCaught(); for (i = 0; gBattleTowerBannedSpecies[i] != 0xFFFF; i++) counter = AppendBattleTowerBannedSpeciesName(gBattleTowerBannedSpecies[i], counter, numBanlistCaught); if (counter == 0) { StringAppend(gStringVar1, BattleText_Format5); StringAppend(gStringVar1, BattleText_Format8); return; } if (1 & counter) StringAppend(gStringVar1, BattleText_Format6); else StringAppend(gStringVar1, BattleText_Format5); StringAppend(gStringVar1, BattleText_Format9); } else { gSpecialVar_0x8004 = 0; gSaveBlock2.battleTower.battleTowerLevelType = gSpecialVar_Result; } } void PrintBattleTowerTrainerMessage(u16 *easyChat) { sub_80EB544(gStringVar4, easyChat, 2, 3); } void PrintBattleTowerTrainerGreeting(void) { if (gSaveBlock2.battleTower.battleTowerTrainerId == BATTLE_TOWER_EREADER_TRAINER_ID) PrintBattleTowerTrainerMessage(gSaveBlock2.battleTower.ereaderTrainer.greeting); else if (gSaveBlock2.battleTower.battleTowerTrainerId < BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID) PrintBattleTowerTrainerMessage((u16 *)gBattleTowerTrainers[gSaveBlock2.battleTower.battleTowerTrainerId].greeting); else PrintBattleTowerTrainerMessage(gSaveBlock2.battleTower.records[gSaveBlock2.battleTower.battleTowerTrainerId - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID].greeting); } void sub_81354CC(void) { s32 i; u16 heldItem; switch (gSpecialVar_0x8004) { case 0: break; case 1: for (i = 0; i < PARTY_SIZE; i++) { heldItem = GetMonData(&gSaveBlock1.playerParty[i], MON_DATA_HELD_ITEM); SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &heldItem); } break; case 2: PrintEReaderTrainerFarewellMessage(); break; } SetMainCallback2(c2_exit_to_overworld_1_continue_scripts_restart_music); } void sub_8135534(u8 taskId) { if (IsBattleTransitionDone() == TRUE) { gMain.savedCallback = sub_81354CC; SetMainCallback2(sub_800E7C4); DestroyTask(taskId); } } void StartSpecialBattle(void) { s32 i; u16 heldItem; u8 transition; switch (gSpecialVar_0x8004) { case 0: // battle tower battle gBattleTypeFlags = (BATTLE_TYPE_BATTLE_TOWER | BATTLE_TYPE_TRAINER); gTrainerBattleOpponent = 0; FillBattleTowerTrainerParty(); CreateTask(sub_8135534, 1); current_map_music_set__default_for_battle(0); transition = BattleSetup_GetBattleTowerBattleTransition(); BattleTransition_StartOnField(transition); break; case 1: // secret base battle for (i = 0; i < PARTY_SIZE; i++) { heldItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM); SetMonData(&gSaveBlock1.playerParty[i], MON_DATA_HELD_ITEM, &heldItem); } CreateTask(sub_8135534, 1); current_map_music_set__default_for_battle(0); transition = BattleSetup_GetBattleTowerBattleTransition(); BattleTransition_StartOnField(transition); break; case 2: // e-reader trainer battle ZeroEnemyPartyMons(); for (i = 0; i < 3; i++) sub_803ADE8(&gEnemyParty[i], &gSaveBlock2.battleTower.ereaderTrainer.party[i]); gBattleTypeFlags = (BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_TRAINER); gTrainerBattleOpponent = 0; CreateTask(sub_8135534, 1); current_map_music_set__default_for_battle(0); transition = BattleSetup_GetBattleTowerBattleTransition(); BattleTransition_StartOnField(transition); break; } } void SetBattleTowerProperty(void) { s32 i; u8 battleTowerLevelType = gSaveBlock2.battleTower.battleTowerLevelType; switch (gSpecialVar_0x8004) { case 0: ewram160FB = gSaveBlock2.battleTower.var_4AE[battleTowerLevelType]; gSaveBlock2.battleTower.var_4AE[battleTowerLevelType] = gSpecialVar_0x8005; break; case 1: gSaveBlock2.battleTower.battleTowerLevelType = gSpecialVar_0x8005; break; case 2: gSaveBlock2.battleTower.curChallengeBattleNum[battleTowerLevelType] = gSpecialVar_0x8005; break; case 3: gSaveBlock2.battleTower.curStreakChallengesNum[battleTowerLevelType] = gSpecialVar_0x8005; break; case 4: gSaveBlock2.battleTower.battleTowerTrainerId = gSpecialVar_0x8005; break; case 5: for (i = 0; i < 3; i++) gSaveBlock2.battleTower.selectedPartyMons[i] = gSelectedOrderFromParty[i]; break; case 6: if (gSaveBlock2.battleTower.battleTowerTrainerId == BATTLE_TOWER_EREADER_TRAINER_ID) ClearEReaderTrainer(&gSaveBlock2.battleTower.ereaderTrainer); if (gSaveBlock2.battleTower.totalBattleTowerWins < 9999) gSaveBlock2.battleTower.totalBattleTowerWins++; gSaveBlock2.battleTower.curChallengeBattleNum[battleTowerLevelType]++; SaveCurrentWinStreak(); gSpecialVar_Result = gSaveBlock2.battleTower.curChallengeBattleNum[battleTowerLevelType]; gStringVar1[0] = gSaveBlock2.battleTower.curChallengeBattleNum[battleTowerLevelType] + 0xA1; gStringVar1[1] = 0xFF; break; case 7: if (gSaveBlock2.battleTower.curStreakChallengesNum[battleTowerLevelType] < 1430) gSaveBlock2.battleTower.curStreakChallengesNum[battleTowerLevelType]++; SaveCurrentWinStreak(); gSpecialVar_Result = gSaveBlock2.battleTower.curStreakChallengesNum[battleTowerLevelType]; break; case 8: gSaveBlock2.battleTower.unk_554 = gSpecialVar_0x8005; break; case 9: break; case 10: SetGameStat(GAME_STAT_BATTLE_TOWER_BEST_STREAK, gSaveBlock2.battleTower.bestBattleTowerWinStreak); break; case 11: if (gSaveBlock2.battleTower.var_4AE[battleTowerLevelType] != 3) ResetBattleTowerStreak(battleTowerLevelType); break; case 12: gSaveBlock2.battleTower.var_4AE[battleTowerLevelType] = ewram160FB; break; case 13: gSaveBlock2.battleTower.currentWinStreaks[battleTowerLevelType] = GetCurrentBattleTowerWinStreak(battleTowerLevelType); break; case 14: gSaveBlock2.battleTower.lastStreakLevelType = gSaveBlock2.battleTower.battleTowerLevelType; break; } } void BattleTowerUtil(void) { u8 battleTowerLevelType = gSaveBlock2.battleTower.battleTowerLevelType; switch (gSpecialVar_0x8004) { case 0: gSpecialVar_Result = gSaveBlock2.battleTower.var_4AE[battleTowerLevelType]; break; case 1: gSpecialVar_Result = gSaveBlock2.battleTower.battleTowerLevelType; break; case 2: gSpecialVar_Result = gSaveBlock2.battleTower.curChallengeBattleNum[battleTowerLevelType]; break; case 3: gSpecialVar_Result = gSaveBlock2.battleTower.curStreakChallengesNum[battleTowerLevelType]; break; case 4: gSpecialVar_Result = gSaveBlock2.battleTower.battleTowerTrainerId; break; case 5: case 6: case 7: break; case 8: gSpecialVar_Result = gSaveBlock2.battleTower.unk_554; break; case 9: gSpecialVar_Result = GetCurrentBattleTowerWinStreak(battleTowerLevelType); break; case 10: SetGameStat(GAME_STAT_BATTLE_TOWER_BEST_STREAK, gSaveBlock2.battleTower.bestBattleTowerWinStreak); break; case 11: ResetBattleTowerStreak(battleTowerLevelType); break; case 12: gSaveBlock2.battleTower.var_4AE[battleTowerLevelType] = ewram160FB; break; case 13: gSaveBlock2.battleTower.currentWinStreaks[battleTowerLevelType] = GetCurrentBattleTowerWinStreak(battleTowerLevelType); break; case 14: gSaveBlock2.battleTower.lastStreakLevelType = gSaveBlock2.battleTower.battleTowerLevelType; break; } } void SetBattleTowerParty(void) { s32 i; for (i = 0; i < 3; i++) gSelectedOrderFromParty[i] = gSaveBlock2.battleTower.selectedPartyMons[i]; ReducePlayerPartyToThree(); } static void SaveCurrentWinStreak(void) { u8 levelType = gSaveBlock2.battleTower.battleTowerLevelType; u16 streak = GetCurrentBattleTowerWinStreak(levelType); if (gSaveBlock2.battleTower.recordWinStreaks[levelType] < streak) gSaveBlock2.battleTower.recordWinStreaks[levelType] = streak; if (gSaveBlock2.battleTower.recordWinStreaks[0] > gSaveBlock2.battleTower.recordWinStreaks[1]) { streak = gSaveBlock2.battleTower.recordWinStreaks[0]; SetGameStat(GAME_STAT_BATTLE_TOWER_BEST_STREAK, streak); if (streak > 9999) gSaveBlock2.battleTower.bestBattleTowerWinStreak = 9999; else gSaveBlock2.battleTower.bestBattleTowerWinStreak = streak; } else { streak = gSaveBlock2.battleTower.recordWinStreaks[1]; SetGameStat(GAME_STAT_BATTLE_TOWER_BEST_STREAK, streak); if (streak > 9999) gSaveBlock2.battleTower.bestBattleTowerWinStreak = 9999; else gSaveBlock2.battleTower.bestBattleTowerWinStreak = streak; } } void sub_8135AC4(void) { s32 i; u8 trainerClass; struct BattleTowerRecord *playerRecord = &gSaveBlock2.battleTower.playerRecord; u8 battleTowerLevelType = gSaveBlock2.battleTower.battleTowerLevelType; if (gSaveBlock2.playerGender != MALE) { trainerClass = sFemaleTrainerClasses[(gSaveBlock2.playerTrainerId[0] + gSaveBlock2.playerTrainerId[1] + gSaveBlock2.playerTrainerId[2] + gSaveBlock2.playerTrainerId[3]) % 20u]; } else { trainerClass = sMaleTrainerClasses[(gSaveBlock2.playerTrainerId[0] + gSaveBlock2.playerTrainerId[1] + gSaveBlock2.playerTrainerId[2] + gSaveBlock2.playerTrainerId[3]) % 30u]; } playerRecord->battleTowerLevelType = battleTowerLevelType; playerRecord->trainerClass = trainerClass; copy_word_to_mem(playerRecord->trainerId, gSaveBlock2.playerTrainerId); StringCopy8(playerRecord->name, gSaveBlock2.playerName); playerRecord->winStreak = GetCurrentBattleTowerWinStreak(battleTowerLevelType); for (i = 0; i < 6; i++) playerRecord->greeting[i] = gSaveBlock1.easyChats.unk2B28[i]; for (i = 0; i < 3; i++) sub_803AF78(&gUnknown_030042FC[gSaveBlock2.battleTower.selectedPartyMons[i]], &playerRecord->party[i]); SetBattleTowerRecordChecksum(&gSaveBlock2.battleTower.playerRecord); SaveCurrentWinStreak(); } void SaveBattleTowerProgress(void) { u8 battleTowerLevelType = gSaveBlock2.battleTower.battleTowerLevelType; if (gSpecialVar_0x8004 == 3 || gSpecialVar_0x8004 == 0) { if (gSaveBlock2.battleTower.curStreakChallengesNum[battleTowerLevelType] > 1 || gSaveBlock2.battleTower.curChallengeBattleNum[battleTowerLevelType] > 1) sub_8135AC4(); } sub_8135CFC(); gSaveBlock2.battleTower.battleOutcome = gBattleOutcome; if (gSpecialVar_0x8004 != 3) gSaveBlock2.battleTower.var_4AE[battleTowerLevelType] = gSpecialVar_0x8004; VarSet(VAR_TEMP_0, 0); gSaveBlock2.battleTower.unk_554 = 1; Save_WriteData(SAVE_EREADER); } void BattleTower_SoftReset(void) { DoSoftReset(); } void ValidateBattleTowerRecordChecksums(void) { u32 i; s32 recordIndex; struct BattleTowerRecord *record; u32 checksum; checksum = 0; for (i = 0; i < (sizeof(struct BattleTowerRecord) / sizeof(u32)) - 1; i++) checksum += ((u32 *)&gSaveBlock2.battleTower.playerRecord)[i]; if (gSaveBlock2.battleTower.playerRecord.checksum != checksum) ClearBattleTowerRecord(&gSaveBlock2.battleTower.playerRecord); for (recordIndex = 0; recordIndex < 5; recordIndex++) { record = &gSaveBlock2.battleTower.records[recordIndex]; checksum = 0; for (i = 0; i < (sizeof(struct BattleTowerRecord) / sizeof(u32)) - 1; i++) checksum += ((u32 *)record)[i]; if (gSaveBlock2.battleTower.records[recordIndex].checksum != checksum) ClearBattleTowerRecord(&gSaveBlock2.battleTower.records[recordIndex]); } } void SetBattleTowerRecordChecksum(struct BattleTowerRecord *record) { u32 i; record->checksum = 0; for (i = 0; i < (sizeof(struct BattleTowerRecord) / sizeof(u32)) - 1; i++) record->checksum += ((u32 *)record)[i]; } void ClearBattleTowerRecord(struct BattleTowerRecord *record) { u32 i; for (i = 0; i < sizeof(struct BattleTowerRecord) / sizeof(u32); i++) ((u32 *)record)[i] = 0; } void sub_8135CFC(void) { s32 i; get_trainer_name(gSaveBlock2.battleTower.defeatedByTrainerName); gSaveBlock2.battleTower.defeatedBySpecies = gBattleMons[1].species; gSaveBlock2.battleTower.firstMonSpecies = gBattleMons[0].species; for (i = 0; i < POKEMON_NAME_LENGTH; i++) gSaveBlock2.battleTower.firstMonNickname[i] = gBattleMons[0].nickname[i]; } u16 GetCurrentBattleTowerWinStreak(u8 battleTowerLevelType) { u16 winStreak = ((gSaveBlock2.battleTower.curStreakChallengesNum[battleTowerLevelType] - 1) * 7 - 1) + gSaveBlock2.battleTower.curChallengeBattleNum[battleTowerLevelType]; if (winStreak > 9999) return 9999; else return winStreak; } void DetermineBattleTowerPrize(void) { u8 levelType = gSaveBlock2.battleTower.battleTowerLevelType; if (gSaveBlock2.battleTower.curStreakChallengesNum[levelType] - 1 > 5) gSaveBlock2.battleTower.prizeItem = sLongStreakPrizes[Random() % ARRAY_COUNT(sLongStreakPrizes)]; else gSaveBlock2.battleTower.prizeItem = sShortStreakPrizes[Random() % ARRAY_COUNT(sShortStreakPrizes)]; } void GiveBattleTowerPrize(void) { u8 battleTowerLevelType = gSaveBlock2.battleTower.battleTowerLevelType; if (AddBagItem(gSaveBlock2.battleTower.prizeItem, 1) == TRUE) { CopyItemName(gSaveBlock2.battleTower.prizeItem, gStringVar1); gSpecialVar_Result = 1; } else { gSpecialVar_Result = 0; gSaveBlock2.battleTower.var_4AE[battleTowerLevelType] = 6; } } void AwardBattleTowerRibbons(void) { s32 i; u32 partyIndex; struct Pokemon *pokemon; u8 ribbonType; u8 battleTowerLevelType = gSaveBlock2.battleTower.battleTowerLevelType; if (battleTowerLevelType != 0) ribbonType = MON_DATA_VICTORY_RIBBON; else ribbonType = MON_DATA_WINNING_RIBBON; gSpecialVar_Result = 0; if (GetCurrentBattleTowerWinStreak(battleTowerLevelType) > 55) { for (i = 0; i < 3; i++) { partyIndex = gSaveBlock2.battleTower.selectedPartyMons[i] - 1; pokemon = &gPlayerParty[partyIndex]; if (!GetMonData(pokemon, ribbonType)) { gSpecialVar_Result = 1; SetMonData(pokemon, ribbonType, &gSpecialVar_Result); } } } if (gSpecialVar_Result != 0) IncrementGameStat(GAME_STAT_RECEIVED_RIBBONS); } // This is a leftover debugging function that is used to populate the E-Reader // trainer with the player's current data. void Debug_FillEReaderTrainerWithPlayerData(void) { struct BattleTowerEReaderTrainer *ereaderTrainer; s32 i; s32 j; ereaderTrainer = &gSaveBlock2.battleTower.ereaderTrainer; if (gSaveBlock2.playerGender != MALE) { ereaderTrainer->trainerClass = sFemaleTrainerClasses[(gSaveBlock2.playerTrainerId[0] + gSaveBlock2.playerTrainerId[1] + gSaveBlock2.playerTrainerId[2] + gSaveBlock2.playerTrainerId[3]) % 20u]; } else { ereaderTrainer->trainerClass = sMaleTrainerClasses[(gSaveBlock2.playerTrainerId[0] + gSaveBlock2.playerTrainerId[1] + gSaveBlock2.playerTrainerId[2] + gSaveBlock2.playerTrainerId[3]) % 30u]; } copy_word_to_mem(ereaderTrainer->trainerId, gSaveBlock2.playerTrainerId); StringCopy8(ereaderTrainer->name, gSaveBlock2.playerName); ereaderTrainer->winStreak = 1; j = 7; for (i = 0; i < 6; i++) { ereaderTrainer->greeting[i] = gSaveBlock1.easyChats.unk2B28[i]; ereaderTrainer->farewellPlayerLost[i] = j; ereaderTrainer->farewellPlayerWon[i] = j + 6; j++; } for (i = 0; i < 3; i++) sub_803AF78(&gPlayerParty[i], &ereaderTrainer->party[i]); SetEReaderTrainerChecksum(ereaderTrainer); } u8 GetEReaderTrainerPicIndex(void) { return gTrainerClassToPicIndex[gSaveBlock2.battleTower.ereaderTrainer.trainerClass]; } u8 GetEReaderTrainerClassNameIndex(void) { return gTrainerClassToNameIndex[gSaveBlock2.battleTower.ereaderTrainer.trainerClass]; } void SetEReaderTrainerName(u8 *trainerName) { s32 i; for (i = 0; i < 7; i++) trainerName[i] = gSaveBlock2.battleTower.ereaderTrainer.name[i]; trainerName[i] = 0xFF; } // Checks if the saved E-Reader trainer is valid. void ValidateEReaderTrainer(void) { u32 i; u32 checksum; struct BattleTowerEReaderTrainer *ereaderTrainer; gSpecialVar_Result = 0; ereaderTrainer = &gSaveBlock2.battleTower.ereaderTrainer; checksum = 0; for (i = 0; i < sizeof(struct BattleTowerEReaderTrainer) / sizeof(u32) - 1; i++) checksum |= ((u32 *)ereaderTrainer)[i]; if (checksum == 0) { gSpecialVar_Result = 1; return; } checksum = 0; for (i = 0; i < sizeof(struct BattleTowerEReaderTrainer) / sizeof(u32) - 1; i++) checksum += ((u32 *)ereaderTrainer)[i]; if (gSaveBlock2.battleTower.ereaderTrainer.checksum != checksum) { ClearEReaderTrainer(&gSaveBlock2.battleTower.ereaderTrainer); gSpecialVar_Result = 1; } } void SetEReaderTrainerChecksum(struct BattleTowerEReaderTrainer *ereaderTrainer) { s32 i; ereaderTrainer->checksum = 0; for (i = 0; i < sizeof(struct BattleTowerEReaderTrainer) / sizeof(u32) - 1; i++) ereaderTrainer->checksum += ((u32 *)ereaderTrainer)[i]; } void ClearEReaderTrainer(struct BattleTowerEReaderTrainer *ereaderTrainer) { u32 i; for (i = 0; i < sizeof(struct BattleTowerEReaderTrainer) / sizeof(u32); i++) ((u32 *)ereaderTrainer)[i] = 0; } void PrintEReaderTrainerGreeting(void) { PrintBattleTowerTrainerMessage(gSaveBlock2.battleTower.ereaderTrainer.greeting); } void PrintEReaderTrainerFarewellMessage(void) { if (gBattleOutcome == BATTLE_DREW) gStringVar4[0] = EOS; else if (gBattleOutcome == BATTLE_WON) PrintBattleTowerTrainerMessage(gSaveBlock2.battleTower.ereaderTrainer.farewellPlayerWon); else PrintBattleTowerTrainerMessage(gSaveBlock2.battleTower.ereaderTrainer.farewellPlayerLost); } void TryEnableBravoTrainerBattleTower(void) { s32 i; for (i = 0; i < 2; i++) { if (gSaveBlock2.battleTower.var_4AE[i] == 1) sub_80BFD20(); } } #if GERMAN u8 de_sub_81364AC(void) { if (gSaveBlock2.battleTower.battleTowerTrainerId == BATTLE_TOWER_EREADER_TRAINER_ID) return gSaveBlock2.battleTower.ereaderTrainer.trainerClass; else if (gSaveBlock2.battleTower.battleTowerTrainerId >= BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID) return gSaveBlock2.battleTower.records[gSaveBlock2.battleTower.battleTowerTrainerId - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID].trainerClass; else return gBattleTowerTrainers[gSaveBlock2.battleTower.battleTowerTrainerId].trainerClass; } u8 de_sub_81364F8(void) { return gSaveBlock2.battleTower.ereaderTrainer.trainerClass; } #endif