#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 "strings.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 "battle_factory.h"
#include "constants/battle_frontier.h"
#include "constants/items.h"
#include "constants/trainers.h"
#include "constants/event_objects.h"
#include "constants/moves.h"
#include "constants/species.h"
#include "constants/easy_chat.h"

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;

// IWRAM common
u16 gUnknown_03006298[4];

// 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.
const u16 gBattleFrontierHeldItems[] =
{
    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_frontier/battle_frontier_trainer_mons.h"
#include "data/battle_frontier/battle_frontier_trainers.h"
#include "data/battle_frontier/battle_frontier_mons.h"

const u8 gTowerMaleFacilityClasses[30] =
{
    FACILITY_CLASS_RUIN_MANIAC,
    FACILITY_CLASS_TUBER_M,
    FACILITY_CLASS_COOLTRAINER_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_PKMN_BREEDER_M,
    FACILITY_CLASS_PKMN_RANGER_M,
    FACILITY_CLASS_BUG_CATCHER,
    FACILITY_CLASS_HIKER
};

const u8 gTowerFemaleFacilityClasses[20] =
{
    FACILITY_CLASS_AROMA_LADY,
    FACILITY_CLASS_TUBER_F,
    FACILITY_CLASS_COOLTRAINER_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_PKMN_RANGER_F,
    FACILITY_CLASS_LASS
};

const u8 gTowerMaleTrainerGfxIds[30] =
{
    EVENT_OBJ_GFX_HIKER,
    EVENT_OBJ_GFX_TUBER_M,
    EVENT_OBJ_GFX_MAN_3,
    EVENT_OBJ_GFX_RICH_BOY,
    EVENT_OBJ_GFX_MANIAC,
    EVENT_OBJ_GFX_RUNNING_TRIATHLETE_M,
    EVENT_OBJ_GFX_BLACK_BELT,
    EVENT_OBJ_GFX_MAN_5,
    EVENT_OBJ_GFX_MAN_5,
    EVENT_OBJ_GFX_CAMPER,
    EVENT_OBJ_GFX_MANIAC,
    EVENT_OBJ_GFX_PSYCHIC_M,
    EVENT_OBJ_GFX_GENTLEMAN,
    EVENT_OBJ_GFX_SCHOOL_KID_M,
    EVENT_OBJ_GFX_POKEFAN_M,
    EVENT_OBJ_GFX_EXPERT_M,
    EVENT_OBJ_GFX_YOUNGSTER,
    EVENT_OBJ_GFX_FISHERMAN,
    EVENT_OBJ_GFX_CYCLING_TRIATHLETE_M,
    EVENT_OBJ_GFX_RUNNING_TRIATHLETE_M,
    EVENT_OBJ_GFX_RUNNING_TRIATHLETE_M,
    EVENT_OBJ_GFX_MAN_3,
    EVENT_OBJ_GFX_MAN_5,
    EVENT_OBJ_GFX_NINJA_BOY,
    EVENT_OBJ_GFX_SAILOR,
    EVENT_OBJ_GFX_MANIAC,
    EVENT_OBJ_GFX_MAN_4,
    EVENT_OBJ_GFX_CAMPER,
    EVENT_OBJ_GFX_BUG_CATCHER,
    EVENT_OBJ_GFX_HIKER
};

const u8 gTowerFemaleTrainerGfxIds[20] =
{
    EVENT_OBJ_GFX_WOMAN_2,
    EVENT_OBJ_GFX_TUBER_F,
    EVENT_OBJ_GFX_WOMAN_5,
    EVENT_OBJ_GFX_HEX_MANIAC,
    EVENT_OBJ_GFX_WOMAN_2,
    EVENT_OBJ_GFX_BEAUTY,
    EVENT_OBJ_GFX_LASS,
    EVENT_OBJ_GFX_GIRL_3,
    EVENT_OBJ_GFX_POKEFAN_F,
    EVENT_OBJ_GFX_EXPERT_F,
    EVENT_OBJ_GFX_CYCLING_TRIATHLETE_F,
    EVENT_OBJ_GFX_RUNNING_TRIATHLETE_F,
    EVENT_OBJ_GFX_RUNNING_TRIATHLETE_F,
    EVENT_OBJ_GFX_GIRL_3,
    EVENT_OBJ_GFX_WOMAN_5,
    EVENT_OBJ_GFX_RUNNING_TRIATHLETE_F,
    EVENT_OBJ_GFX_PICNICKER,
    EVENT_OBJ_GFX_WOMAN_2,
    EVENT_OBJ_GFX_PICNICKER,
    EVENT_OBJ_GFX_LASS
};

static const u8 sRubyFacilityClassToEmerald[][2] =
{
    {0x00, FACILITY_CLASS_AQUA_LEADER_ARCHIE},
    {0x01, FACILITY_CLASS_AQUA_GRUNT_M},
    {0x02, FACILITY_CLASS_AQUA_GRUNT_F},
    {0x03, FACILITY_CLASS_AROMA_LADY},
    {0x04, FACILITY_CLASS_RUIN_MANIAC},
    {0x05, FACILITY_CLASS_INTERVIEWER},
    {0x06, FACILITY_CLASS_TUBER_F},
    {0x07, FACILITY_CLASS_TUBER_M},
    {0x08, FACILITY_CLASS_COOLTRAINER_M},
    {0x09, FACILITY_CLASS_COOLTRAINER_F},
    {0x0a, FACILITY_CLASS_HEX_MANIAC},
    {0x0b, FACILITY_CLASS_LADY},
    {0x0c, FACILITY_CLASS_BEAUTY},
    {0x0d, FACILITY_CLASS_RICH_BOY},
    {0x0e, FACILITY_CLASS_POKEMANIAC},
    {0x0f, FACILITY_CLASS_SWIMMER_M},
    {0x10, FACILITY_CLASS_BLACK_BELT},
    {0x11, FACILITY_CLASS_GUITARIST},
    {0x12, FACILITY_CLASS_KINDLER},
    {0x13, FACILITY_CLASS_CAMPER},
    {0x14, FACILITY_CLASS_BUG_MANIAC},
    {0x15, FACILITY_CLASS_PSYCHIC_M},
    {0x16, FACILITY_CLASS_PSYCHIC_F},
    {0x17, FACILITY_CLASS_GENTLEMAN},
    {0x18, FACILITY_CLASS_ELITE_FOUR_SIDNEY},
    {0x19, FACILITY_CLASS_ELITE_FOUR_PHOEBE},
    {0x1a, FACILITY_CLASS_LEADER_ROXANNE},
    {0x1b, FACILITY_CLASS_LEADER_BRAWLY},
    {0x1c, FACILITY_CLASS_LEADER_TATE_AND_LIZA},
    {0x1d, FACILITY_CLASS_SCHOOL_KID_M},
    {0x1e, FACILITY_CLASS_SCHOOL_KID_F},
    {0x1f, FACILITY_CLASS_SR_AND_JR},
    {0x20, FACILITY_CLASS_POKEFAN_M},
    {0x21, FACILITY_CLASS_POKEFAN_F},
    {0x22, FACILITY_CLASS_EXPERT_M},
    {0x23, FACILITY_CLASS_EXPERT_F},
    {0x24, FACILITY_CLASS_YOUNGSTER},
    {0x25, FACILITY_CLASS_CHAMPION_WALLACE},
    {0x26, FACILITY_CLASS_FISHERMAN},
    {0x27, FACILITY_CLASS_CYCLING_TRIATHLETE_M},
    {0x28, FACILITY_CLASS_CYCLING_TRIATHLETE_F},
    {0x29, FACILITY_CLASS_RUNNING_TRIATHLETE_M},
    {0x2a, FACILITY_CLASS_RUNNING_TRIATHLETE_F},
    {0x2b, FACILITY_CLASS_SWIMMING_TRIATHLETE_M},
    {0x2c, FACILITY_CLASS_SWIMMING_TRIATHLETE_F},
    {0x2d, FACILITY_CLASS_DRAGON_TAMER},
    {0x2e, FACILITY_CLASS_BIRD_KEEPER},
    {0x2f, FACILITY_CLASS_NINJA_BOY},
    {0x30, FACILITY_CLASS_BATTLE_GIRL},
    {0x31, FACILITY_CLASS_PARASOL_LADY},
    {0x32, FACILITY_CLASS_SWIMMER_F},
    {0x33, FACILITY_CLASS_PICNICKER},
    {0x34, FACILITY_CLASS_TWINS},
    {0x35, FACILITY_CLASS_SAILOR},
    {0x38, FACILITY_CLASS_COLLECTOR},
    {0x39, FACILITY_CLASS_WALLY},
    {0x3a, FACILITY_CLASS_BRENDAN},
    {0x3b, FACILITY_CLASS_BRENDAN_2},
    {0x3c, FACILITY_CLASS_BRENDAN_3},
    {0x3d, FACILITY_CLASS_MAY},
    {0x3e, FACILITY_CLASS_MAY_2},
    {0x3f, FACILITY_CLASS_MAY_3},
    {0x40, FACILITY_CLASS_PKMN_BREEDER_M},
    {0x41, FACILITY_CLASS_POKEMON_BREEDER_F},
    {0x42, FACILITY_CLASS_PKMN_RANGER_M},
    {0x43, FACILITY_CLASS_PKMN_RANGER_F},
    {0x44, FACILITY_CLASS_MAGMA_LEADER_MAXIE},
    {0x45, FACILITY_CLASS_MAGMA_GRUNT_M},
    {0x46, FACILITY_CLASS_MAGMA_GRUNT_F},
    {0x47, FACILITY_CLASS_LASS},
    {0x48, FACILITY_CLASS_BUG_CATCHER},
    {0x49, FACILITY_CLASS_HIKER},
    {0x4a, FACILITY_CLASS_YOUNG_COUPLE},
    {0x4b, FACILITY_CLASS_OLD_COUPLE},
    {0x4c, FACILITY_CLASS_SIS_AND_BRO},
};

static const u8 *const gUnknown_085DCFD8[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244383,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2443E7,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244413,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24444B,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2444D6
};

static const u8 *const gUnknown_085DCFEC[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2444EF,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24451E,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244549,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24459B,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2445C5
};

static const u8 *const gUnknown_085DD000[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2445E8,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244618,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244643,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24468F,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2446B4
};

static const u8 *const gUnknown_085DD014[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2446E5,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24474D,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24479E,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24480C,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2448A7
};

static const u8 *const gUnknown_085DD028[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2448CD,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2448F5,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244939,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244989,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2449E6
};

static const u8 *const gUnknown_085DD03C[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244A23,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244AA9,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244AD6,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244B52,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244B93
};

static const u8 *const gUnknown_085DD050[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244BD2,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244C18,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244C6E,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244CD6,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244D07
};

static const u8 *const gUnknown_085DD064[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244D36,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244D82,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244DC6,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244DFE,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244E5B
};

static const u8 *const gUnknown_085DD078[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244E7E,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244ECA,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244EF4,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244F4F,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244F98
};

static const u8 *const gUnknown_085DD08C[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_244FD3,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245013,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24503D,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245087,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2450C0
};

static const u8 *const gUnknown_085DD0A0[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2450E6,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245196,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2451BD,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24522F,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24529A
};

static const u8 *const gUnknown_085DD0B4[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2452EF,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2453B4,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245406,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245464,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2454A3
};

static const u8 *const gUnknown_085DD0C8[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2454D6,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245535,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24555F,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2455EC,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245656
};

static const u8 *const gUnknown_085DD0DC[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24568A,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2456F5,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245740,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2457D9,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24581F
};

static const u8 *const gUnknown_085DD0F0[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245851,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24589C,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2458CE,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245923,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24597F
};

static const u8 *const gUnknown_085DD104[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2459BE,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2459F7,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245A5F,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245AB4,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245B3E
};

static const u8 *const gUnknown_085DD118[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245B79,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245B91,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245BBD,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245C05,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245C2D
};

static const u8 *const gUnknown_085DD12C[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245C66,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245C7F,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245CAC,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245CE6,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245D07
};

static const u8 *const gUnknown_085DD140[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245D4B,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245D60,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245D8D,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245DC7,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245DF0
};

static const u8 *const gUnknown_085DD154[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245E20,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245E41,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245E78,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245EBD,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245EEC
};

static const u8 *const gUnknown_085DD168[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245F2B,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245F58,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245F87,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_245FC6,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246002
};

static const u8 *const gUnknown_085DD17C[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246020,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246051,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246082,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2460D5,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24610D
};

static const u8 *const gUnknown_085DD190[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24614A,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24615D,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24617D,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2461C7,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2461F5
};

static const u8 *const gUnknown_085DD1A4[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24622A,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246244,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24626E,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2462AA,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2462E4
};

static const u8 *const gUnknown_085DD1B8[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24631F,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24633C,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24636B,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2463C5,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2463FF
};

static const u8 *const gUnknown_085DD1CC[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246449,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246478,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24649D,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2464EE,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246529
};

static const u8 *const gUnknown_085DD1E0[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246571,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24658C,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2465B5,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246605,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24662A
};

static const u8 *const gUnknown_085DD1F4[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246662,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246686,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2466B4,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2466E6,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24670A
};

static const u8 *const gUnknown_085DD208[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246741,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246760,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246791,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2467C4,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2467E6
};

static const u8 *const gUnknown_085DD21C[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2467FF,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246831,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246864,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2468A7,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2468FE
};

static const u8 *const gUnknown_085DD230[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24692B,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24694B,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24697C,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2469B4,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2469F1
};

static const u8 *const gUnknown_085DD244[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246A22,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246A4E,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246A85,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246ACF,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246AF7
};

static const u8 *const gUnknown_085DD258[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246B2B,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246B3F,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246B6B,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246BB2,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246BE9
};

static const u8 *const gUnknown_085DD26C[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246C22,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246C35,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246C6C,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246CB3,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246CE9
};

static const u8 *const gUnknown_085DD280[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246D2D,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246D3F,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246D6A,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246D9E,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246DD3
};

static const u8 *const gUnknown_085DD294[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246E1A,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246E39,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246E64,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246EB6,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246EEE
};

static const u8 *const gUnknown_085DD2A8[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246F21,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246F4C,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246F7B,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_246FCC,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247013
};

static const u8 *const gUnknown_085DD2BC[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24705B,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24707D,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2470B2,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2470FD,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24712A
};

static const u8 *const gUnknown_085DD2D0[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24716C,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24718D,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2471C7,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2471FB,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247238
};

static const u8 *const gUnknown_085DD2E4[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247268,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247291,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2472C7,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247313,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247348
};

static const u8 *const gUnknown_085DD2F8[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24737A,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24738F,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2473C6,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2473FB,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247438
};

static const u8 *const gUnknown_085DD30C[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247470,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247484,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2474AF,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2474EC,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24751D
};

static const u8 *const gUnknown_085DD320[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24754A,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24755C,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24758B,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2475BC,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2475D5
};

static const u8 *const gUnknown_085DD334[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24760D,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247629,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247655,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24769B,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2476ED
};

static const u8 *const gUnknown_085DD348[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247724,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247749,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24777A,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2477CE,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247807
};

static const u8 *const gUnknown_085DD35C[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247838,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247854,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247889,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2478DD,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24790D
};

static const u8 *const gUnknown_085DD370[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247942,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24795F,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247991,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2479CE,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247A18
};

static const u8 *const gUnknown_085DD384[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247A51,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247A76,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247AA1,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247AE3,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247B16
};

static const u8 *const gUnknown_085DD398[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247B3E,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247B62,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247B9B,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247BE7,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247C23
};

static const u8 *const gUnknown_085DD3AC[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247C4E,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247C8C,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247CB2,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247D07,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247D4E
};

static const u8 *const gUnknown_085DD3C0[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247D97,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247DC7,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247DFE,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247E3E,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247E7E
};

static const u8 *const gUnknown_085DD3D4[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247EA1,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247EB5,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247EE9,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247F3F,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247F6D
};

static const u8 *const gUnknown_085DD3E8[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247FA3,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247FB7,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_247FE9,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248031,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24805E
};

static const u8 *const gUnknown_085DD3FC[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24808D,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2480AB,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2480DC,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248128,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248151
};

static const u8 *const gUnknown_085DD410[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24818E,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2481A9,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2481E1,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248221,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24824F
};

static const u8 *const gUnknown_085DD424[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248297,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2482B1,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2482DC,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248321,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24833D
};

static const u8 *const gUnknown_085DD438[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248369,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24838D,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2483C1,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248401,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248431
};

static const u8 *const gUnknown_085DD44C[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248467,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248477,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2484AC,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2484E7,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248517
};

static const u8 *const gUnknown_085DD460[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248553,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24856A,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24857D,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2485E5,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248614
};

static const u8 *const gUnknown_085DD474[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24864E,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248661,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248671,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2486C6,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2486FF
};

static const u8 *const gUnknown_085DD488[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248725,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248751,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248781,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2487C9,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2487F7
};

static const u8 *const gUnknown_085DD49C[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248829,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24885B,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248892,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2488D8,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248908
};

static const u8 *const gUnknown_085DD4B0[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24894B,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24895F,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_24896F,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2489BD,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_2489E7
};

static const u8 *const gUnknown_085DD4C4[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248A10,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248A40,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248A72,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248AC0,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248AEB
};

static const u8 *const gUnknown_085DD4D8[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248B22,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248B39,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248B4C,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248BB4,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248BEE
};

static const u8 *const gUnknown_085DD4EC[] =
{
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248C2E,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248C5B,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248C90,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248CCF,
    BattleFrontier_BattleTowerMultiBattleRoom_Text_248D04
};

struct
{
    u32 facilityClass;
    const u8 *const *strings;
} const gUnknown_085DD500[] =
{
    {FACILITY_CLASS_LASS,                  gUnknown_085DD118},
    {FACILITY_CLASS_YOUNGSTER,             gUnknown_085DD12C},
    {FACILITY_CLASS_HIKER,                 gUnknown_085DD140},
    {FACILITY_CLASS_BEAUTY,                gUnknown_085DD154},
    {FACILITY_CLASS_FISHERMAN,             gUnknown_085DD168},
    {FACILITY_CLASS_LADY,                  gUnknown_085DD17C},
    {FACILITY_CLASS_CYCLING_TRIATHLETE_F,  gUnknown_085DD190},
    {FACILITY_CLASS_BUG_CATCHER,           gUnknown_085DD1A4},
    {FACILITY_CLASS_SCHOOL_KID_M,          gUnknown_085DD1B8},
    {FACILITY_CLASS_RICH_BOY,              gUnknown_085DD1CC},
    {FACILITY_CLASS_BLACK_BELT,            gUnknown_085DD1E0},
    {FACILITY_CLASS_TUBER_F,               gUnknown_085DD1F4},
    {FACILITY_CLASS_HEX_MANIAC,            gUnknown_085DD208},
    {FACILITY_CLASS_PKMN_BREEDER_M,        gUnknown_085DD21C},
    {FACILITY_CLASS_RUNNING_TRIATHLETE_F,  gUnknown_085DD230},
    {FACILITY_CLASS_RUNNING_TRIATHLETE_M,  gUnknown_085DD244},
    {FACILITY_CLASS_BATTLE_GIRL,           gUnknown_085DD258},
    {FACILITY_CLASS_CYCLING_TRIATHLETE_M,  gUnknown_085DD26C},
    {FACILITY_CLASS_TUBER_M,               gUnknown_085DD280},
    {FACILITY_CLASS_GUITARIST,             gUnknown_085DD294},
    {FACILITY_CLASS_GENTLEMAN,             gUnknown_085DD2A8},
    {FACILITY_CLASS_POKEFAN_M,             gUnknown_085DD2BC},
    {FACILITY_CLASS_EXPERT_M,              gUnknown_085DD2D0},
    {FACILITY_CLASS_EXPERT_F,              gUnknown_085DD2E4},
    {FACILITY_CLASS_DRAGON_TAMER,          gUnknown_085DD2F8},
    {FACILITY_CLASS_BIRD_KEEPER,           gUnknown_085DD30C},
    {FACILITY_CLASS_NINJA_BOY,             gUnknown_085DD320},
    {FACILITY_CLASS_PARASOL_LADY,          gUnknown_085DD334},
    {FACILITY_CLASS_BUG_MANIAC,            gUnknown_085DD348},
    {FACILITY_CLASS_SAILOR,                gUnknown_085DD35C},
    {FACILITY_CLASS_COLLECTOR,             gUnknown_085DD370},
    {FACILITY_CLASS_PKMN_RANGER_M,         gUnknown_085DD384},
    {FACILITY_CLASS_PKMN_RANGER_F,         gUnknown_085DD398},
    {FACILITY_CLASS_AROMA_LADY,            gUnknown_085DD3AC},
    {FACILITY_CLASS_RUIN_MANIAC,           gUnknown_085DD3C0},
    {FACILITY_CLASS_COOLTRAINER_M,         gUnknown_085DD3D4},
    {FACILITY_CLASS_COOLTRAINER_F,         gUnknown_085DD3E8},
    {FACILITY_CLASS_POKEMANIAC,            gUnknown_085DD3FC},
    {FACILITY_CLASS_KINDLER,               gUnknown_085DD410},
    {FACILITY_CLASS_CAMPER,                gUnknown_085DD424},
    {FACILITY_CLASS_PICNICKER,             gUnknown_085DD438},
    {FACILITY_CLASS_PSYCHIC_M,             gUnknown_085DD44C},
    {FACILITY_CLASS_PSYCHIC_F,             gUnknown_085DD460},
    {FACILITY_CLASS_SCHOOL_KID_F,          gUnknown_085DD474},
    {FACILITY_CLASS_POKEMON_BREEDER_F,     gUnknown_085DD488},
    {FACILITY_CLASS_POKEFAN_F,             gUnknown_085DD49C},
    {FACILITY_CLASS_SWIMMER_F,             gUnknown_085DD4B0},
    {FACILITY_CLASS_SWIMMING_TRIATHLETE_M, gUnknown_085DD4C4},
    {FACILITY_CLASS_SWIMMING_TRIATHLETE_F, gUnknown_085DD4D8},
    {FACILITY_CLASS_SWIMMER_M,             gUnknown_085DD4EC}
};

static const u8 *const *const gUnknown_085DD690[] =
{
    gUnknown_085DCFD8,
    gUnknown_085DCFEC,
    gUnknown_085DD000,
    gUnknown_085DD014,
    gUnknown_085DD028,
    gUnknown_085DD03C,
    gUnknown_085DD050,
    gUnknown_085DD064,
    gUnknown_085DD078,
    gUnknown_085DD08C,
    gUnknown_085DD0A0,
    gUnknown_085DD0B4,
    gUnknown_085DD0C8,
    gUnknown_085DD0DC,
    gUnknown_085DD0F0,
    gUnknown_085DD104
};

struct
{
    u16 species;
    u8 fixedIV;
    u8 level;
    u8 nature;
    u8 evs[6];
    u16 moves[4];
} const sStevenMons[3] =
{
    {
        .species = SPECIES_METANG,
        .fixedIV = 31,
        .level = 42,
        .nature = NATURE_BRAVE,
        .evs = {0, 252, 252, 0, 6, 0},
        .moves = {MOVE_LIGHT_SCREEN, MOVE_PSYCHIC, MOVE_REFLECT, MOVE_METAL_CLAW}
    },
    {
        .species = SPECIES_SKARMORY,
        .fixedIV = 31,
        .level = 43,
        .nature = NATURE_IMPISH,
        .evs = {252, 0, 0, 0, 6, 252},
        .moves = {MOVE_TOXIC, MOVE_AERIAL_ACE, MOVE_PROTECT, MOVE_STEEL_WING}
    },
    {
        .species = SPECIES_AGGRON,
        .fixedIV = 31,
        .level = 44,
        .nature = NATURE_ADAMANT,
        .evs = {0, 252, 0, 0, 252, 6},
        .moves = {MOVE_THUNDER, MOVE_PROTECT, MOVE_SOLAR_BEAM, MOVE_DRAGON_CLAW}
    }
};

#include "data/battle_frontier/battle_tent.h"

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();
    SetDynamicWarp(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.trainerIds[id * 2];
            gTrainerBattleOpponent_B = gSaveBlock2Ptr->frontier.trainerIds[id * 2 + 1];
            SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_A, 0);
            SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_B, 1);
        }
        else if (ChooseSpecialBattleTowerTrainer())
        {
            SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_A, 0);
            gSaveBlock2Ptr->frontier.trainerIds[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.trainerIds[i] == id)
                        break;
                }
                if (i == gSaveBlock2Ptr->frontier.curChallengeBattleNum)
                    break;
            }

            gTrainerBattleOpponent_A = id;
            SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_A, 0);
            if (gSaveBlock2Ptr->frontier.curChallengeBattleNum + 1 < 7)
                gSaveBlock2Ptr->frontier.trainerIds[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;
}

static void sub_81625B4(u8 challengeNum, u8 battleNum, u16 *trainerIdPtr, u8 *arg3) // Unused
{
    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;
}

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 *monSets = NULL;
    u32 otID = 0;

    if (trainerId < TRAINER_RECORD_MIXING_FRIEND)
    {
        // Normal battle frontier trainer.
        fixedIV = GetFrontierTrainerFixedIvs(trainerId);
        monSets = gFacilityTrainers[gTrainerBattleOpponent_A].monSets;
    }
    else if (trainerId == TRAINER_EREADER)
    {
        for (i = firstMonId; i < firstMonId + 3; i++)
            CreateBattleTowerMon(&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)
            {
                CreateBattleTowerMon2(&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; monSets[bfMonCount] != 0xFFFF; bfMonCount++)
        ;
    i = 0;
    otID = Random32();
    while (i != monCount)
    {
        u16 monSetId = monSets[Random() % bfMonCount];
        if ((level == 50 || level == 20) && monSetId > 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[monSetId].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[monSetId].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] == monSetId)
                break;
        }
        if (j != i)
            continue;

        chosenMonIndices[i] = monSetId;

        // Place the chosen pokemon into the trainer's party.
        CreateMonWithEVSpreadNatureOTID(&gEnemyParty[i + firstMonId],
                                             gFacilityTrainerMons[monSetId].species,
                                             level,
                                             gFacilityTrainerMons[monSetId].nature,
                                             fixedIV,
                                             gFacilityTrainerMons[monSetId].evSpread,
                                             otID);

        friendship = 255;
        // Give the chosen pokemon its specified moves.
        for (j = 0; j < MAX_MON_MOVES; j++)
        {
            SetMonMoveSlot(&gEnemyParty[i + firstMonId], gFacilityTrainerMons[monSetId].moves[j], j);
            if (gFacilityTrainerMons[monSetId].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[monSetId].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 < MAX_MON_MOVES; 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 RandomizeFacilityTrainerMonSet(u16 trainerId)
{
    u8 level = SetFacilityPtrsGetLevel();
    const u16 *monSets = gFacilityTrainers[trainerId].monSets;
    u8 bfMonCount = 0;
    u32 monSetId = monSets[bfMonCount];

    while (monSetId != 0xFFFF)
    {
        bfMonCount++;
        monSetId = monSets[bfMonCount];
        if (monSetId == 0xFFFF)
            break;
    }

    do
    {
        monSetId = monSets[Random() % bfMonCount];
    } while((level == 50 || level == 20) && monSetId > 849);

    return monSetId;
}

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 = GetFactoryMonFixedIV(challengeNum, 0);
        else
            fixedIV = GetFactoryMonFixedIV(challengeNum, 1);
    }
    else if (trainerId == TRAINER_EREADER)
    {
        for (i = firstMonId; i < firstMonId + 3; i++)
            CreateBattleTowerMon(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.ereaderTrainer.party[i - firstMonId]);
        return;
    }
    else if (trainerId == TRAINER_FRONTIER_BRAIN)
    {
        FillFactoryBrainParty();
        return;
    }
    else
    {
        fixedIV = 31;
    }

    level = SetFacilityPtrsGetLevel();
    otID = T1_READ_32(gSaveBlock2Ptr->playerTrainerId);
    for (i = 0; i < 3; i++)
    {
        u16 monSetId = gUnknown_03006298[i];
        CreateMonWithEVSpreadNatureOTID(&gEnemyParty[firstMonId + i],
                                             gFacilityTrainerMons[monSetId].species,
                                             level,
                                             gFacilityTrainerMons[monSetId].nature,
                                             fixedIV,
                                             gFacilityTrainerMons[monSetId].evSpread,
                                             otID);

        friendship = 0;
        for (j = 0; j < MAX_MON_MOVES; j++)
            SetMonMoveAvoidReturn(&gEnemyParty[firstMonId + i], gFacilityTrainerMons[monSetId].moves[j], j);

        SetMonData(&gEnemyParty[firstMonId + i], MON_DATA_FRIENDSHIP, &friendship);
        SetMonData(&gEnemyParty[firstMonId + i], MON_DATA_HELD_ITEM, &gBattleFrontierHeldItems[gFacilityTrainerMons[monSetId].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 monSetId = gUnknown_03006298[i];
        CreateMonWithEVSpreadNatureOTID(&gEnemyParty[firstMonId + i],
                                             gFacilityTrainerMons[monSetId].species,
                                             level,
                                             gFacilityTrainerMons[monSetId].nature,
                                             fixedIV,
                                             gFacilityTrainerMons[monSetId].evSpread,
                                             otID);

        friendship = 0;
        for (j = 0; j < MAX_MON_MOVES; j++)
        {
            SetMonMoveAvoidReturn(&gEnemyParty[firstMonId + i], gFacilityTrainerMons[monSetId].moves[j], j);
            if (gFacilityTrainerMons[monSetId].moves[j] == MOVE_FRUSTRATION)
                friendship = 0;
        }

        SetMonData(&gEnemyParty[firstMonId + i], MON_DATA_FRIENDSHIP, &friendship);
        SetMonData(&gEnemyParty[firstMonId + i], MON_DATA_HELD_ITEM, &gBattleFrontierHeldItems[gFacilityTrainerMons[monSetId].itemTableId]);
    }
}

void FrontierSpeechToString(const u16 *words)
{
    ConvertEasyChatWordsToString(gStringVar4, words, 3, 2);
    if (GetStringWidth(1, gStringVar4, -1) > 204u)
    {
        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.trainerIds[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++)
            CreateBattleTowerMon(&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 monSetId;
    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.trainerIds[i] == trainerId)
                    break;
                if (gFacilityTrainers[gSaveBlock2Ptr->frontier.trainerIds[i]].facilityClass == gFacilityTrainers[trainerId].facilityClass)
                    break;
            }
        } while (i != j);
        gSaveBlock2Ptr->frontier.trainerIds[j] = trainerId;
        j++;
    } while (j < 6);

    r10 = 8;
    for (i = 0; i < 6; i++)
    {
        trainerId = gSaveBlock2Ptr->frontier.trainerIds[i];
        eventObjTemplates[i + 1].graphicsId = GetBattleFacilityTrainerGfxId(trainerId);
        for (j = 0; j < 2; j++)
        {
            while (1)
            {
                monSetId = RandomizeFacilityTrainerMonSet(trainerId);
                if (j % 2 != 0 && gFacilityTrainerMons[gSaveBlock2Ptr->frontier.trainerIds[r10 - 1]].itemTableId == gFacilityTrainerMons[monSetId].itemTableId)
                    continue;

                for (k = 8; k < r10; k++)
                {
                    if (gFacilityTrainerMons[gSaveBlock2Ptr->frontier.trainerIds[k]].species == gFacilityTrainerMons[monSetId].species)
                        break;
                    if (species1 == gFacilityTrainerMons[monSetId].species)
                        break;
                    if (species2 == gFacilityTrainerMons[monSetId].species)
                        break;
                }
                if (k == r10)
                    break;
            }

            gSaveBlock2Ptr->frontier.trainerIds[r10] = monSetId;
            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.trainerIds[6] = spArray[Random() % r10];
        eventObjTemplates[7].graphicsId = GetBattleFacilityTrainerGfxId(gSaveBlock2Ptr->frontier.trainerIds[6]);
        FlagClear(FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_ALT_1);
        sub_81640E0(gSaveBlock2Ptr->frontier.trainerIds[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.trainerIds[7] = spArray[Random() % r10];
        eventObjTemplates[8].graphicsId = GetBattleFacilityTrainerGfxId(gSaveBlock2Ptr->frontier.trainerIds[7]);
        FlagClear(FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_ALT_2);
        sub_8164188(gSaveBlock2Ptr->frontier.trainerIds[7]);
    }
}

static void sub_81646BC(u16 trainerId, u16 monSetId)
{
    u16 move = 0;
    u16 species = 0;
    SetFacilityPtrsGetLevel();

    if (trainerId != TRAINER_EREADER)
    {
        if (trainerId < TRAINER_RECORD_MIXING_FRIEND)
        {
            move = gFacilityTrainerMons[monSetId].moves[0];
            species = gFacilityTrainerMons[monSetId].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 monSetId;
    s32 level = SetFacilityPtrsGetLevel();
    u16 winStreak = GetCurrentFacilityWinStreak();
    s32 challengeNum = winStreak / 7;
    s32 k = gSpecialVar_LastTalked - 2;
    s32 trainerId = gSaveBlock2Ptr->frontier.trainerIds[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:
        monSetId = gSaveBlock2Ptr->frontier.trainerIds[8 + k * 2];
        sub_81646BC(trainerId, monSetId);
        break;
    case 2:
        monSetId = gSaveBlock2Ptr->frontier.trainerIds[9 + k * 2];
        sub_81646BC(trainerId, monSetId);
        break;
    case 3:
        gPartnerTrainerId = trainerId;
        if (trainerId < TRAINER_RECORD_MIXING_FRIEND)
        {
            gSaveBlock2Ptr->frontier.trainerIds[18] = gSaveBlock2Ptr->frontier.trainerIds[8 + k * 2];
            gSaveBlock2Ptr->frontier.trainerIds[19] = gSaveBlock2Ptr->frontier.trainerIds[9 + k * 2];
        }
        else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE)
        {
            gSaveBlock2Ptr->frontier.trainerIds[18] = gUnknown_03006298[2];
            gSaveBlock2Ptr->frontier.trainerIds[19] = gUnknown_03006298[3];
        }
        else
        {
            gSaveBlock2Ptr->frontier.trainerIds[18] = gUnknown_03006298[0];
            gSaveBlock2Ptr->frontier.trainerIds[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.trainerIds[j] == i)
                        break;
                }
                if (j == k)
                    break;
            }
            gSaveBlock2Ptr->frontier.trainerIds[k] = i;
        }
        gSaveBlock2Ptr->frontier.trainerIds[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 (IsLinkTaskFinished())
            {
                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.trainerIds[j] == trainerId)
                            break;
                    }
                } while (i != j);
                if (i == j) // This condition is always true, because of the loop above.
                    gSaveBlock2Ptr->frontier.trainerIds[i] = trainerId;
            }
            gSpecialVar_Result = 2;
        }
        break;
    case 2:
        if (IsLinkTaskFinished())
        {
            SendBlock(bitmask_all_link_players_but_self(), &gSaveBlock2Ptr->frontier.trainerIds, sizeof(gSaveBlock2Ptr->frontier.trainerIds));
            gSpecialVar_Result = 3;
        }
        break;
    case 3:
        if ((GetBlockReceivedStatus() & 3) == 3)
        {
            ResetBlockReceivedFlags();
            memcpy(&gSaveBlock2Ptr->frontier.trainerIds, gBlockRecvBuffer, sizeof(gSaveBlock2Ptr->frontier.trainerIds));
            gTrainerBattleOpponent_A = gSaveBlock2Ptr->frontier.trainerIds[battleNum * 2];
            gTrainerBattleOpponent_B = gSaveBlock2Ptr->frontier.trainerIds[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.trainerIds[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_CANCEL_BATTLE_ROOM_CHALLENGE) == TRUE)
    {
        sub_80F01B8();
        FlagClear(FLAG_CANCEL_BATTLE_ROOM_CHALLENGE);
    }
}

#define STEVEN_OTID 61226

static void FillPartnerParty(u16 trainerId)
{
    s32 i, j;
    u32 ivs, level;
    u32 friendship;
    u16 monSetId;
    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 < MAX_MON_MOVES; 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++)
        {
            monSetId = gSaveBlock2Ptr->frontier.trainerIds[i + 18];
            CreateMonWithEVSpreadNatureOTID(&gPlayerParty[3 + i],
                                                 gFacilityTrainerMons[monSetId].species,
                                                 level,
                                                 gFacilityTrainerMons[monSetId].nature,
                                                 ivs,
                                                 gFacilityTrainerMons[monSetId].evSpread,
                                                 otID);
            friendship = 0xFF;
            for (j = 0; j < MAX_MON_MOVES; j++)
            {
                SetMonMoveSlot(&gPlayerParty[3 + i], gFacilityTrainerMons[monSetId].moves[j], j);
                if (gFacilityTrainerMons[monSetId].moves[j] == MOVE_FRUSTRATION)
                    friendship = 0;
            }
            SetMonData(&gPlayerParty[3 + i], MON_DATA_FRIENDSHIP, &friendship);
            SetMonData(&gPlayerParty[3 + i], MON_DATA_HELD_ITEM, &gBattleFrontierHeldItems[gFacilityTrainerMons[monSetId].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 BattleTowerPokemon monData = record->party[gSaveBlock2Ptr->frontier.trainerIds[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;
            }
            CreateBattleTowerMon2(&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.trainerIds[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;
        // BUG: Reading outside the array. sRubyFacilityClassToEmerald has less than FACILITY_CLASSES_COUNT entries.
        for (i = 0; i < FACILITY_CLASSES_COUNT; i++)
        {
            if (sRubyFacilityClassToEmerald[i][0] == src->facilityClass)
                break;
        }
        if (i != FACILITY_CLASSES_COUNT)
            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;
        // BUG: Reading outside the array. sRubyFacilityClassToEmerald has less than FACILITY_CLASSES_COUNT entries.
        for (i = 0; i < FACILITY_CLASSES_COUNT; i++)
        {
            if (sRubyFacilityClassToEmerald[i][1] == src->facilityClass)
                break;
        }
        if (i != FACILITY_CLASSES_COUNT)
            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.trainerIds[i] == trainerId)
                break;
        }
    } while (i != gSaveBlock2Ptr->frontier.curChallengeBattleNum);

    gTrainerBattleOpponent_A = trainerId;
    SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_A, 0);
    if (gSaveBlock2Ptr->frontier.curChallengeBattleNum + 1 < 3)
       gSaveBlock2Ptr->frontier.trainerIds[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 *monSets = NULL;
    u32 otID = 0;
    u16 monSetId;

    monSets = gFacilityTrainers[gTrainerBattleOpponent_A].monSets;

    bfMonCount = 0;
    monSetId = monSets[bfMonCount];
    while (monSetId != 0xFFFF)
    {
        bfMonCount++;
        monSetId = monSets[bfMonCount];
        if (monSetId == 0xFFFF)
            break;
    }

    i = 0;
    otID = Random32();
    while (i != monCount)
    {
        u16 monSetId = monSets[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[monSetId].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[monSetId].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] == monSetId)
                break;
        }
        if (j != i)
            continue;

        chosenMonIndices[i] = monSetId;

        // Place the chosen pokemon into the trainer's party.
        CreateMonWithEVSpreadNatureOTID(&gEnemyParty[i + firstMonId],
                                             gFacilityTrainerMons[monSetId].species,
                                             level,
                                             gFacilityTrainerMons[monSetId].nature,
                                             fixedIV,
                                             gFacilityTrainerMons[monSetId].evSpread,
                                             otID);

        friendship = 255;
        // Give the chosen pokemon its specified moves.
        for (j = 0; j < MAX_MON_MOVES; j++)
        {
            SetMonMoveSlot(&gEnemyParty[i + firstMonId], gFacilityTrainerMons[monSetId].moves[j], j);
            if (gFacilityTrainerMons[monSetId].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[monSetId].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 FacilityClassToGraphicsId(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]);
            }
        }
    }
}
