summaryrefslogtreecommitdiff
path: root/src/battle_setup.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/battle_setup.c')
-rw-r--r--src/battle_setup.c1868
1 files changed, 1868 insertions, 0 deletions
diff --git a/src/battle_setup.c b/src/battle_setup.c
new file mode 100644
index 000000000..e478af936
--- /dev/null
+++ b/src/battle_setup.c
@@ -0,0 +1,1868 @@
+#include "global.h"
+#include "constants/trainers.h"
+#include "battle.h"
+#include "battle_setup.h"
+#include "battle_transition.h"
+#include "main.h"
+#include "task.h"
+#include "safari_zone.h"
+#include "script.h"
+#include "constants/game_stat.h"
+#include "event_data.h"
+#include "constants/species.h"
+#include "constants/songs.h"
+#include "metatile_behavior.h"
+#include "constants/maps.h"
+#include "field_player_avatar.h"
+#include "fieldmap.h"
+#include "random.h"
+#include "starter_choose.h"
+#include "script_pokemon_80F8.h"
+#include "constants/items.h"
+#include "palette.h"
+#include "window.h"
+#include "event_object_movement.h"
+#include "event_scripts.h"
+#include "trainer_see.h"
+#include "field_message_box.h"
+#include "sound.h"
+#include "strings.h"
+#include "secret_base.h"
+#include "string_util.h"
+#include "overworld.h"
+#include "field_weather.h"
+#include "gym_leader_rematch.h"
+#include "constants/map_types.h"
+
+enum
+{
+ TRAINER_PARAM_LOAD_VAL_8BIT,
+ TRAINER_PARAM_LOAD_VAL_16BIT,
+ TRAINER_PARAM_LOAD_VAL_32BIT,
+ TRAINER_PARAM_CLEAR_VAL_8BIT,
+ TRAINER_PARAM_CLEAR_VAL_16BIT,
+ TRAINER_PARAM_CLEAR_VAL_32BIT,
+ TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR,
+};
+
+struct TrainerBattleParameter
+{
+ void *varPtr;
+ u8 ptrType;
+};
+
+extern bool8 InBattlePyramid(void);
+extern bool8 InBattlePike(void);
+extern bool32 InTrainerHill(void);
+extern bool32 FieldPoisonEffectIsRunning(void);
+extern void prev_quest_postbuffer_cursor_backup_reset(void);
+extern void ResetPoisonStepCounter(void);
+extern void sub_81BE72C(void);
+extern void sub_808BCF4(void);
+extern void sub_80EECC8(void);
+extern void sub_80AF6F0(void);
+extern u16 sub_81A9AA8(u8 localId);
+extern u16 sub_81D6180(u8 localId);
+extern bool8 GetBattlePyramidTrainerFlag(u8 eventObjId);
+extern bool8 GetTrainerHillTrainerFlag(u8 eventObjId);
+extern bool8 sub_81D5C18(void);
+extern void sub_816306C(u8 a0);
+extern void sub_8163048(u8 a0);
+extern void sub_81A9B04(void);
+extern void sub_81D639C(void);
+extern void sub_81D6384(void);
+extern void sub_81D61E8(void);
+extern void sub_80982B8(void);
+extern void sub_81A9EDC(u16 a0);
+extern void sub_81D572C(u8 a0, u16 arg1);
+
+// this file's functions
+static void DoBattlePikeWildBattle(void);
+static void DoSafariBattle(void);
+static void DoStandardWildBattle(void);
+static void CB2_EndWildBattle(void);
+static void CB2_EndScriptedWildBattle(void);
+static u8 GetWildBattleTransition(void);
+static u8 GetTrainerBattleTransition(void);
+static void sub_80B1218(void);
+static void sub_80B1234(void);
+static void CB2_GiveStarter(void);
+static void CB2_StartFirstBattle(void);
+static void CB2_EndFirstBattle(void);
+static void CB2_EndTrainerBattle(void);
+static bool32 IsPlayerDefeated(u32 battleOutcome);
+static u16 GetRematchTrainerId(u16 trainerId);
+static void RegisterTrainerInMatchCall(void);
+static void HandleRematchVarsOnBattleEnd(void);
+static const u8 *GetIntroSpeechOfApproachingTrainer(void);
+static const u8 *GetTrainerCantBattleSpeech(void);
+
+// ewram vars
+EWRAM_DATA static u16 sTrainerBattleMode = 0;
+EWRAM_DATA u16 gTrainerBattleOpponent_A = 0;
+EWRAM_DATA u16 gTrainerBattleOpponent_B = 0;
+EWRAM_DATA u16 gPartnerTrainerId = 0;
+EWRAM_DATA static u16 sTrainerEventObjectLocalId = 0;
+EWRAM_DATA static u8 *sTrainerAIntroSpeech = NULL;
+EWRAM_DATA static u8 *sTrainerBIntroSpeech = NULL;
+EWRAM_DATA static u8 *sTrainerADefeatSpeech = NULL;
+EWRAM_DATA static u8 *sTrainerBDefeatSpeech = NULL;
+EWRAM_DATA static u8 *sTrainerVictorySpeech = NULL;
+EWRAM_DATA static u8 *sTrainerCannotBattleSpeech = NULL;
+EWRAM_DATA static u8 *sTrainerBattleEndScript = NULL;
+EWRAM_DATA static u8 *sTrainerABattleScriptRetAddr = NULL;
+EWRAM_DATA static u8 *sTrainerBBattleScriptRetAddr = NULL;
+EWRAM_DATA static bool8 sShouldCheckTrainerBScript = FALSE;
+EWRAM_DATA static u8 sNoOfPossibleTrainerRetScripts = 0;
+
+// const rom data
+
+// The first transition is used if the enemy pokemon are lower level than our pokemon.
+// Otherwise, the second transition is used.
+static const u8 sBattleTransitionTable_Wild[][2] =
+{
+ {B_TRANSITION_SLICE, B_TRANSITION_WHITEFADE}, // Normal
+ {B_TRANSITION_CLOCKWISE_BLACKFADE, B_TRANSITION_GRID_SQUARES}, // Cave
+ {B_TRANSITION_BLUR, B_TRANSITION_GRID_SQUARES}, // Cave with flash used
+ {B_TRANSITION_WAVE, B_TRANSITION_RIPPLE}, // Water
+};
+
+static const u8 sBattleTransitionTable_Trainer[][2] =
+{
+ {B_TRANSITION_POKEBALLS_TRAIL, B_TRANSITION_SHARDS}, // Normal
+ {B_TRANSITION_SHUFFLE, B_TRANSITION_BIG_POKEBALL}, // Cave
+ {B_TRANSITION_BLUR, B_TRANSITION_GRID_SQUARES}, // Cave with flash used
+ {B_TRANSITION_SWIRL, B_TRANSITION_RIPPLE}, // Water
+};
+
+static const u8 sUnknown_0854FE98[] =
+{
+ B_TRANSITION_29, B_TRANSITION_30, B_TRANSITION_31, B_TRANSITION_32,
+ B_TRANSITION_34, B_TRANSITION_35, B_TRANSITION_36, B_TRANSITION_37,
+ B_TRANSITION_38, B_TRANSITION_39, B_TRANSITION_40, B_TRANSITION_41
+};
+
+static const u8 sUnknown_0854FEA4[] =
+{
+ B_TRANSITION_31, B_TRANSITION_32, B_TRANSITION_33
+};
+
+static const u8 sUnknown_0854FEA7[] =
+{
+ B_TRANSITION_29, B_TRANSITION_31, B_TRANSITION_32, B_TRANSITION_33
+};
+
+static const struct TrainerBattleParameter sOrdinaryBattleParams[] =
+{
+ {&sTrainerBattleMode, TRAINER_PARAM_LOAD_VAL_8BIT},
+ {&gTrainerBattleOpponent_A, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerEventObjectLocalId, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerAIntroSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerADefeatSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerVictorySpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerCannotBattleSpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerABattleScriptRetAddr, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerBattleEndScript, TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR},
+};
+
+static const struct TrainerBattleParameter sContinueScriptBattleParams[] =
+{
+ {&sTrainerBattleMode, TRAINER_PARAM_LOAD_VAL_8BIT},
+ {&gTrainerBattleOpponent_A, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerEventObjectLocalId, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerAIntroSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerADefeatSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerVictorySpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerCannotBattleSpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerABattleScriptRetAddr, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerBattleEndScript, TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR},
+};
+
+static const struct TrainerBattleParameter sDoubleBattleParams[] =
+{
+ {&sTrainerBattleMode, TRAINER_PARAM_LOAD_VAL_8BIT},
+ {&gTrainerBattleOpponent_A, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerEventObjectLocalId, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerAIntroSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerADefeatSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerVictorySpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerCannotBattleSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerABattleScriptRetAddr, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerBattleEndScript, TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR},
+};
+
+static const struct TrainerBattleParameter sOrdinaryNoIntroBattleParams[] =
+{
+ {&sTrainerBattleMode, TRAINER_PARAM_LOAD_VAL_8BIT},
+ {&gTrainerBattleOpponent_A, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerEventObjectLocalId, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerAIntroSpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerADefeatSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerVictorySpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerCannotBattleSpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerABattleScriptRetAddr, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerBattleEndScript, TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR},
+};
+
+static const struct TrainerBattleParameter sContinueScriptDoubleBattleParams[] =
+{
+ {&sTrainerBattleMode, TRAINER_PARAM_LOAD_VAL_8BIT},
+ {&gTrainerBattleOpponent_A, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerEventObjectLocalId, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerAIntroSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerADefeatSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerVictorySpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerCannotBattleSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerABattleScriptRetAddr, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerBattleEndScript, TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR},
+};
+
+static const struct TrainerBattleParameter sTrainerBOrdinaryBattleParams[] =
+{
+ {&sTrainerBattleMode, TRAINER_PARAM_LOAD_VAL_8BIT},
+ {&gTrainerBattleOpponent_B, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerEventObjectLocalId, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerBIntroSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerBDefeatSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerVictorySpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerCannotBattleSpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerBBattleScriptRetAddr, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerBattleEndScript, TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR},
+};
+
+static const struct TrainerBattleParameter sTrainerBContinueScriptBattleParams[] =
+{
+ {&sTrainerBattleMode, TRAINER_PARAM_LOAD_VAL_8BIT},
+ {&gTrainerBattleOpponent_B, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerEventObjectLocalId, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerBIntroSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerBDefeatSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerVictorySpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerCannotBattleSpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerBBattleScriptRetAddr, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerBattleEndScript, TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR},
+};
+
+const struct RematchTrainer gRematchTable[REMATCH_TABLE_ENTRIES] =
+{
+ {{TRAINER_ROSE_1, TRAINER_ROSE_2, TRAINER_ROSE_3, TRAINER_ROSE_4, TRAINER_ROSE_5}, 0x0, 0x21},
+ {{TRAINER_ANDRES_1, TRAINER_ANDRES_2, TRAINER_ANDRES_3, TRAINER_ANDRES_4, TRAINER_ANDRES_5}, 0x0, 0x14},
+ {{TRAINER_DUSTY_1, TRAINER_DUSTY_2, TRAINER_DUSTY_3, TRAINER_DUSTY_4, TRAINER_DUSTY_5}, 0x0, 0x1a},
+ {{TRAINER_LOLA_1, TRAINER_LOLA_2, TRAINER_LOLA_3, TRAINER_LOLA_4, TRAINER_LOLA_5}, 0x0, 0x18},
+ {{TRAINER_RICKY_1, TRAINER_RICKY_2, TRAINER_RICKY_3, TRAINER_RICKY_4, TRAINER_RICKY_5}, 0x0, 0x18},
+ {{TRAINER_LILA_AND_ROY_1, TRAINER_LILA_AND_ROY_2, TRAINER_LILA_AND_ROY_3, TRAINER_LILA_AND_ROY_4, TRAINER_LILA_AND_ROY_5}, 0x0, 0x27},
+ {{TRAINER_CRISTIN_1, TRAINER_CRISTIN_2, TRAINER_CRISTIN_3, TRAINER_CRISTIN_4, TRAINER_CRISTIN_5}, 0x0, 0x24},
+ {{TRAINER_BROOKE_1, TRAINER_BROOKE_2, TRAINER_BROOKE_3, TRAINER_BROOKE_4, TRAINER_BROOKE_5}, 0x0, 0x1a},
+ {{TRAINER_WILTON_1, TRAINER_WILTON_2, TRAINER_WILTON_3, TRAINER_WILTON_4, TRAINER_WILTON_5}, 0x0, 0x1a},
+ {{TRAINER_VALERIE_1, TRAINER_VALERIE_2, TRAINER_VALERIE_3, TRAINER_VALERIE_4, TRAINER_VALERIE_5}, 0x18, 0x14},
+ {{TRAINER_CINDY_1, TRAINER_CINDY_3, TRAINER_CINDY_4, TRAINER_CINDY_5, TRAINER_CINDY_6}, 0x0, 0x13},
+ {{TRAINER_THALIA_1, TRAINER_THALIA_2, TRAINER_THALIA_3, TRAINER_THALIA_4, TRAINER_THALIA_5}, 0x18, 0x38},
+ {{TRAINER_JESSICA_1, TRAINER_JESSICA_2, TRAINER_JESSICA_3, TRAINER_JESSICA_4, TRAINER_JESSICA_5}, 0x0, 0x24},
+ {{TRAINER_WINSTON_1, TRAINER_WINSTON_2, TRAINER_WINSTON_3, TRAINER_WINSTON_4, TRAINER_WINSTON_5}, 0x0, 0x13},
+ {{TRAINER_STEVE_1, TRAINER_STEVE_2, TRAINER_STEVE_3, TRAINER_STEVE_4, TRAINER_STEVE_5}, 0x0, 0x1d},
+ {{TRAINER_TONY_1, TRAINER_TONY_2, TRAINER_TONY_3, TRAINER_TONY_4, TRAINER_TONY_5}, 0x0, 0x16},
+ {{TRAINER_NOB_1, TRAINER_NOB_2, TRAINER_NOB_3, TRAINER_NOB_4, TRAINER_NOB_5}, 0x0, 0x1e},
+ {{TRAINER_KOJI_1, TRAINER_KOJI_2, TRAINER_KOJI_3, TRAINER_KOJI_4, TRAINER_KOJI_5}, 0x0, 0x2a},
+ {{TRAINER_FERNANDO_1, TRAINER_FERNANDO_2, TRAINER_FERNANDO_3, TRAINER_FERNANDO_4, TRAINER_FERNANDO_5}, 0x0, 0x26},
+ {{TRAINER_DALTON_1, TRAINER_DALTON_2, TRAINER_DALTON_3, TRAINER_DALTON_4, TRAINER_DALTON_5}, 0x0, 0x21},
+ {{TRAINER_BERNIE_1, TRAINER_BERNIE_2, TRAINER_BERNIE_3, TRAINER_BERNIE_4, TRAINER_BERNIE_5}, 0x0, 0x1d},
+ {{TRAINER_ETHAN_1, TRAINER_ETHAN_2, TRAINER_ETHAN_3, TRAINER_ETHAN_4, TRAINER_ETHAN_5}, 0x18, 0xd},
+ {{TRAINER_JOHN_AND_JAY_1, TRAINER_JOHN_AND_JAY_2, TRAINER_JOHN_AND_JAY_3, TRAINER_JOHN_AND_JAY_4, TRAINER_JOHN_AND_JAY_5}, 0x18, 0x1},
+ {{TRAINER_JEFFREY_1, TRAINER_JEFFREY_2, TRAINER_JEFFREY_3, TRAINER_JEFFREY_4, TRAINER_JEFFREY_5}, 0x0, 0x23},
+ {{TRAINER_CAMERON_1, TRAINER_CAMERON_2, TRAINER_CAMERON_3, TRAINER_CAMERON_4, TRAINER_CAMERON_5}, 0x0, 0x26},
+ {{TRAINER_JACKI_1, TRAINER_JACKI_2, TRAINER_JACKI_3, TRAINER_JACKI_4, TRAINER_JACKI_5}, 0x0, 0x26},
+ {{TRAINER_WALTER_1, TRAINER_WALTER_2, TRAINER_WALTER_3, TRAINER_WALTER_4, TRAINER_WALTER_5}, 0x0, 0x24},
+ {{TRAINER_KAREN_1, TRAINER_KAREN_2, TRAINER_KAREN_3, TRAINER_KAREN_4, TRAINER_KAREN_5}, 0x0, 0x1f},
+ {{TRAINER_JERRY_1, TRAINER_JERRY_2, TRAINER_JERRY_3, TRAINER_JERRY_4, TRAINER_JERRY_5}, 0x0, 0x1f},
+ {{TRAINER_ANNA_AND_MEG_1, TRAINER_ANNA_AND_MEG_2, TRAINER_ANNA_AND_MEG_3, TRAINER_ANNA_AND_MEG_4, TRAINER_ANNA_AND_MEG_5}, 0x0, 0x20},
+ {{TRAINER_ISABEL_1, TRAINER_ISABEL_2, TRAINER_ISABEL_3, TRAINER_ISABEL_4, TRAINER_ISABEL_5}, 0x0, 0x19},
+ {{TRAINER_MIGUEL_1, TRAINER_MIGUEL_2, TRAINER_MIGUEL_3, TRAINER_MIGUEL_4, TRAINER_MIGUEL_5}, 0x0, 0x12},
+ {{TRAINER_TIMOTHY_1, TRAINER_TIMOTHY_2, TRAINER_TIMOTHY_3, TRAINER_TIMOTHY_4, TRAINER_TIMOTHY_5}, 0x0, 0x1e},
+ {{TRAINER_SHELBY_1, TRAINER_SHELBY_2, TRAINER_SHELBY_3, TRAINER_SHELBY_4, TRAINER_SHELBY_5}, 0x18, 0xc},
+ {{TRAINER_CALVIN_1, TRAINER_CALVIN_2, TRAINER_CALVIN_3, TRAINER_CALVIN_4, TRAINER_CALVIN_5}, 0x0, 0x11},
+ {{TRAINER_ELLIOT_1, TRAINER_ELLIOT_2, TRAINER_ELLIOT_3, TRAINER_ELLIOT_4, TRAINER_ELLIOT_5}, 0x0, 0x15},
+ {{TRAINER_ISAIAH_1, TRAINER_ISAIAH_2, TRAINER_ISAIAH_3, TRAINER_ISAIAH_4, TRAINER_ISAIAH_5}, 0x0, 0x2b},
+ {{TRAINER_MARIA_1, TRAINER_MARIA_2, TRAINER_MARIA_3, TRAINER_MARIA_4, TRAINER_MARIA_5}, 0x0, 0x20},
+ {{TRAINER_ABIGAIL_1, TRAINER_ABIGAIL_2, TRAINER_ABIGAIL_3, TRAINER_ABIGAIL_4, TRAINER_ABIGAIL_5}, 0x0, 0x19},
+ {{TRAINER_DYLAN_1, TRAINER_DYLAN_2, TRAINER_DYLAN_3, TRAINER_DYLAN_4, TRAINER_DYLAN_5}, 0x0, 0x20},
+ {{TRAINER_KATELYN_1, TRAINER_KATELYN_2, TRAINER_KATELYN_3, TRAINER_KATELYN_4, TRAINER_KATELYN_5}, 0x0, 0x2b},
+ {{TRAINER_BENJAMIN_1, TRAINER_BENJAMIN_2, TRAINER_BENJAMIN_3, TRAINER_BENJAMIN_4, TRAINER_BENJAMIN_5}, 0x0, 0x19},
+ {{TRAINER_PABLO_1, TRAINER_PABLO_2, TRAINER_PABLO_3, TRAINER_PABLO_4, TRAINER_PABLO_5}, 0x0, 0x29},
+ {{TRAINER_NICOLAS_1, TRAINER_NICOLAS_2, TRAINER_NICOLAS_3, TRAINER_NICOLAS_4, TRAINER_NICOLAS_5}, 0x18, 0x1},
+ {{TRAINER_ROBERT_1, TRAINER_ROBERT_2, TRAINER_ROBERT_3, TRAINER_ROBERT_4, TRAINER_ROBERT_5}, 0x0, 0x23},
+ {{TRAINER_LAO_1, TRAINER_LAO_2, TRAINER_LAO_3, TRAINER_LAO_4, TRAINER_LAO_5}, 0x0, 0x1c},
+ {{TRAINER_CYNDY_1, TRAINER_CYNDY_2, TRAINER_CYNDY_3, TRAINER_CYNDY_4, TRAINER_CYNDY_5}, 0x0, 0x1e},
+ {{TRAINER_MADELINE_1, TRAINER_MADELINE_2, TRAINER_MADELINE_3, TRAINER_MADELINE_4, TRAINER_MADELINE_5}, 0x0, 0x1c},
+ {{TRAINER_JENNY_1, TRAINER_JENNY_2, TRAINER_JENNY_3, TRAINER_JENNY_4, TRAINER_JENNY_5}, 0x0, 0x27},
+ {{TRAINER_DIANA_1, TRAINER_DIANA_2, TRAINER_DIANA_3, TRAINER_DIANA_4, TRAINER_DIANA_5}, 0x18, 0xd},
+ {{TRAINER_AMY_AND_LIV_1, TRAINER_AMY_AND_LIV_2, TRAINER_AMY_AND_LIV_4, TRAINER_AMY_AND_LIV_5, TRAINER_AMY_AND_LIV_6}, 0x0, 0x12},
+ {{TRAINER_ERNEST_1, TRAINER_ERNEST_2, TRAINER_ERNEST_3, TRAINER_ERNEST_4, TRAINER_ERNEST_5}, 0x0, 0x28},
+ {{TRAINER_CORY_1, TRAINER_CORY_2, TRAINER_CORY_3, TRAINER_CORY_4, TRAINER_CORY_5}, 0x0, 0x17},
+ {{TRAINER_EDWIN_1, TRAINER_EDWIN_2, TRAINER_EDWIN_3, TRAINER_EDWIN_4, TRAINER_EDWIN_5}, 0x0, 0x19},
+ {{TRAINER_LYDIA_1, TRAINER_LYDIA_2, TRAINER_LYDIA_3, TRAINER_LYDIA_4, TRAINER_LYDIA_5}, 0x0, 0x20},
+ {{TRAINER_ISAAC_1, TRAINER_ISAAC_2, TRAINER_ISAAC_3, TRAINER_ISAAC_4, TRAINER_ISAAC_5}, 0x0, 0x20},
+ {{TRAINER_GABRIELLE_1, TRAINER_GABRIELLE_2, TRAINER_GABRIELLE_3, TRAINER_GABRIELLE_4, TRAINER_GABRIELLE_5}, 0x18, 0x11},
+ {{TRAINER_CATHERINE_1, TRAINER_CATHERINE_2, TRAINER_CATHERINE_3, TRAINER_CATHERINE_4, TRAINER_CATHERINE_5}, 0x0, 0x22},
+ {{TRAINER_JACKSON_1, TRAINER_JACKSON_2, TRAINER_JACKSON_3, TRAINER_JACKSON_4, TRAINER_JACKSON_5}, 0x0, 0x22},
+ {{TRAINER_HALEY_1, TRAINER_HALEY_2, TRAINER_HALEY_3, TRAINER_HALEY_4, TRAINER_HALEY_5}, 0x0, 0x13},
+ {{TRAINER_JAMES_1, TRAINER_JAMES_2, TRAINER_JAMES_3, TRAINER_JAMES_4, TRAINER_JAMES_5}, 0x18, 0xb},
+ {{TRAINER_TRENT_1, TRAINER_TRENT_2, TRAINER_TRENT_3, TRAINER_TRENT_4, TRAINER_TRENT_5}, 0x0, 0x1b},
+ {{TRAINER_SAWYER_1, TRAINER_SAWYER_2, TRAINER_SAWYER_3, TRAINER_SAWYER_4, TRAINER_SAWYER_5}, 0x18, 0xc},
+ {{TRAINER_KIRA_AND_DAN_1, TRAINER_KIRA_AND_DAN_2, TRAINER_KIRA_AND_DAN_3, TRAINER_KIRA_AND_DAN_4, TRAINER_KIRA_AND_DAN_5}, 0x18, 0x3e},
+ {{TRAINER_WALLY_3, 0x292, 0x293, 0x294, 0x294}, 0x18, 0x2b},
+ {{TRAINER_ROXANNE_1, TRAINER_ROXANNE_2, TRAINER_ROXANNE_3, TRAINER_ROXANNE_4, TRAINER_ROXANNE_5}, 0x0, 0x3},
+ {{TRAINER_BRAWLY_1, TRAINER_BRAWLY_2, TRAINER_BRAWLY_3, TRAINER_BRAWLY_4, TRAINER_BRAWLY_5}, 0x0, 0xb},
+ {{TRAINER_WATTSON_1, TRAINER_WATTSON_2, TRAINER_WATTSON_3, TRAINER_WATTSON_4, TRAINER_WATTSON_5}, 0x0, 0x2},
+ {{TRAINER_FLANNERY_1, TRAINER_FLANNERY_2, TRAINER_FLANNERY_3, TRAINER_FLANNERY_4, TRAINER_FLANNERY_5}, 0x0, 0xc},
+ {{TRAINER_NORMAN_1, TRAINER_NORMAN_2, TRAINER_NORMAN_3, TRAINER_NORMAN_4, TRAINER_NORMAN_5}, 0x0, 0x0},
+ {{TRAINER_WINONA_1, TRAINER_WINONA_2, TRAINER_WINONA_3, TRAINER_WINONA_4, TRAINER_WINONA_5}, 0x0, 0x4},
+ {{TRAINER_TATE_AND_LIZA_1, TRAINER_TATE_AND_LIZA_2, TRAINER_TATE_AND_LIZA_3, TRAINER_TATE_AND_LIZA_4, TRAINER_TATE_AND_LIZA_5}, 0x0, 0x6},
+ {{TRAINER_JUAN_1, TRAINER_JUAN_2, TRAINER_JUAN_3, TRAINER_JUAN_4, TRAINER_JUAN_5}, 0x0, 0x7},
+ {{TRAINER_SIDNEY, TRAINER_SIDNEY, TRAINER_SIDNEY, TRAINER_SIDNEY, TRAINER_SIDNEY}, 0x0, 0x8},
+ {{TRAINER_PHOEBE, TRAINER_PHOEBE, TRAINER_PHOEBE, TRAINER_PHOEBE, TRAINER_PHOEBE}, 0x0, 0x8},
+ {{TRAINER_GLACIA, TRAINER_GLACIA, TRAINER_GLACIA, TRAINER_GLACIA, TRAINER_GLACIA}, 0x0, 0x8},
+ {{TRAINER_DRAKE, TRAINER_DRAKE, TRAINER_DRAKE, TRAINER_DRAKE, TRAINER_DRAKE}, 0x0, 0x8},
+ {{TRAINER_WALLACE, TRAINER_WALLACE, TRAINER_WALLACE, TRAINER_WALLACE, TRAINER_WALLACE}, 0x0, 0x8},
+};
+
+static const u16 sBadgeFlags[8] =
+{
+ FLAG_BADGE01_GET, FLAG_BADGE02_GET, FLAG_BADGE03_GET, FLAG_BADGE04_GET,
+ FLAG_BADGE05_GET, FLAG_BADGE06_GET, FLAG_BADGE07_GET, FLAG_BADGE08_GET,
+};
+
+#define tState data[0]
+#define tTransition data[1]
+
+static void Task_BattleStart(u8 taskId)
+{
+ s16 *data = gTasks[taskId].data;
+
+ switch (tState)
+ {
+ case 0:
+ if (!FieldPoisonEffectIsRunning()) // is poison not active?
+ {
+ BattleTransition_StartOnField(tTransition);
+ sub_81BE72C();
+ tState++; // go to case 1.
+ }
+ break;
+ case 1:
+ if (IsBattleTransitionDone() == TRUE)
+ {
+ overworld_free_bg_tilemaps();
+ SetMainCallback2(CB2_InitBattle);
+ prev_quest_postbuffer_cursor_backup_reset();
+ ResetPoisonStepCounter();
+ DestroyTask(taskId);
+ }
+ break;
+ }
+}
+
+static void CreateBattleStartTask(u8 transition, u16 song)
+{
+ u8 taskId = CreateTask(Task_BattleStart, 1);
+
+ gTasks[taskId].tTransition = transition;
+ PlayMapChosenOrBattleBGM(song);
+}
+
+#undef tState
+#undef tTransition
+
+void BattleSetup_StartWildBattle(void)
+{
+ if (GetSafariZoneFlag())
+ DoSafariBattle();
+ else
+ DoStandardWildBattle();
+}
+
+void BattleSetup_StartBattlePikeWildBattle(void)
+{
+ DoBattlePikeWildBattle();
+}
+
+static void DoStandardWildBattle(void)
+{
+ ScriptContext2_Enable();
+ FreezeEventObjects();
+ sub_808BCF4();
+ gMain.savedCallback = CB2_EndWildBattle;
+ gBattleTypeFlags = 0;
+ if (InBattlePyramid())
+ {
+ VarSet(VAR_TEMP_E, 0);
+ gBattleTypeFlags |= BATTLE_TYPE_PYRAMID;
+ }
+ CreateBattleStartTask(GetWildBattleTransition(), 0);
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_WILD_BATTLES);
+ sub_80EECC8();
+ sub_80B1218();
+}
+
+void BattleSetup_StartRoamerBattle(void)
+{
+ ScriptContext2_Enable();
+ FreezeEventObjects();
+ sub_808BCF4();
+ gMain.savedCallback = CB2_EndWildBattle;
+ gBattleTypeFlags = BATTLE_TYPE_ROAMER;
+ CreateBattleStartTask(GetWildBattleTransition(), 0);
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_WILD_BATTLES);
+ sub_80EECC8();
+ sub_80B1218();
+}
+
+static void DoSafariBattle(void)
+{
+ ScriptContext2_Enable();
+ FreezeEventObjects();
+ sub_808BCF4();
+ gMain.savedCallback = CB2_EndSafariBattle;
+ gBattleTypeFlags = BATTLE_TYPE_SAFARI;
+ CreateBattleStartTask(GetWildBattleTransition(), 0);
+}
+
+static void DoBattlePikeWildBattle(void)
+{
+ ScriptContext2_Enable();
+ FreezeEventObjects();
+ sub_808BCF4();
+ gMain.savedCallback = CB2_EndWildBattle;
+ gBattleTypeFlags = BATTLE_TYPE_PIKE;
+ CreateBattleStartTask(GetWildBattleTransition(), 0);
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_WILD_BATTLES);
+ sub_80EECC8();
+ sub_80B1218();
+}
+
+static void DoTrainerBattle(void)
+{
+ CreateBattleStartTask(GetTrainerBattleTransition(), 0);
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_TRAINER_BATTLES);
+ sub_80B1234();
+}
+
+static void sub_80B0828(void)
+{
+ if (InBattlePyramid())
+ CreateBattleStartTask(sub_80B100C(10), 0);
+ else
+ CreateBattleStartTask(sub_80B100C(11), 0);
+
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_TRAINER_BATTLES);
+ sub_80B1234();
+}
+
+// Initiates battle where Wally catches Ralts
+void StartWallyTutorialBattle(void)
+{
+ CreateMaleMon(&gEnemyParty[0], SPECIES_RALTS, 5);
+ ScriptContext2_Enable();
+ gMain.savedCallback = CB2_ReturnToFieldContinueScriptPlayMapMusic;
+ gBattleTypeFlags = BATTLE_TYPE_WALLY_TUTORIAL;
+ CreateBattleStartTask(B_TRANSITION_SLICE, 0);
+}
+
+void BattleSetup_StartScriptedWildBattle(void)
+{
+ ScriptContext2_Enable();
+ gMain.savedCallback = CB2_EndScriptedWildBattle;
+ gBattleTypeFlags = 0;
+ CreateBattleStartTask(GetWildBattleTransition(), 0);
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_WILD_BATTLES);
+ sub_80EECC8();
+ sub_80B1218();
+}
+
+void BattleSetup_StartLatiBattle(void)
+{
+ ScriptContext2_Enable();
+ gMain.savedCallback = CB2_EndScriptedWildBattle;
+ gBattleTypeFlags = BATTLE_TYPE_LEGENDARY;
+ CreateBattleStartTask(GetWildBattleTransition(), 0);
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_WILD_BATTLES);
+ sub_80EECC8();
+ sub_80B1218();
+}
+
+void BattleSetup_StartLegendaryBattle(void)
+{
+ ScriptContext2_Enable();
+ gMain.savedCallback = CB2_EndScriptedWildBattle;
+ gBattleTypeFlags = BATTLE_TYPE_LEGENDARY;
+
+ switch (GetMonData(&gEnemyParty[0], MON_DATA_SPECIES, NULL))
+ {
+ default:
+ case SPECIES_GROUDON:
+ gBattleTypeFlags |= BATTLE_TYPE_GROUDON;
+ CreateBattleStartTask(B_TRANSITION_GROUDON, MUS_BATTLE34);
+ break;
+ case SPECIES_KYOGRE:
+ gBattleTypeFlags |= BATTLE_TYPE_KYOGRE;
+ CreateBattleStartTask(B_TRANSITION_KYOGRE, MUS_BATTLE34);
+ break;
+ case SPECIES_RAYQUAZA:
+ gBattleTypeFlags |= BATTLE_TYPE_RAYQUAZA;
+ CreateBattleStartTask(B_TRANSITION_RAYQUAZA, MUS_VS_REKKU);
+ break;
+ case SPECIES_DEOXYS:
+ CreateBattleStartTask(B_TRANSITION_BLUR, MUS_RG_VS_DEO);
+ break;
+ case SPECIES_LUGIA:
+ case SPECIES_HO_OH:
+ CreateBattleStartTask(B_TRANSITION_BLUR, MUS_RG_VS_DEN);
+ break;
+ case SPECIES_MEW:
+ CreateBattleStartTask(B_TRANSITION_GRID_SQUARES, MUS_VS_MEW);
+ break;
+ }
+
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_WILD_BATTLES);
+ sub_80EECC8();
+ sub_80B1218();
+}
+
+void StartGroudonKyogreBattle(void)
+{
+ ScriptContext2_Enable();
+ gMain.savedCallback = CB2_EndScriptedWildBattle;
+ gBattleTypeFlags = BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_KYOGRE_GROUDON;
+
+ if (gGameVersion == VERSION_RUBY)
+ CreateBattleStartTask(B_TRANSITION_SHARDS, MUS_BATTLE34); // GROUDON
+ else
+ CreateBattleStartTask(B_TRANSITION_RIPPLE, MUS_BATTLE34); // KYOGRE
+
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_WILD_BATTLES);
+ sub_80EECC8();
+ sub_80B1218();
+}
+
+void StartRegiBattle(void)
+{
+ u8 transitionId;
+ u16 species;
+
+ ScriptContext2_Enable();
+ gMain.savedCallback = CB2_EndScriptedWildBattle;
+ gBattleTypeFlags = BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_REGI;
+
+ species = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES);
+ switch (species)
+ {
+ case SPECIES_REGIROCK:
+ transitionId = B_TRANSITION_REGIROCK;
+ break;
+ case SPECIES_REGICE:
+ transitionId = B_TRANSITION_REGICE;
+ break;
+ case SPECIES_REGISTEEL:
+ transitionId = B_TRANSITION_REGISTEEL;
+ break;
+ default:
+ transitionId = B_TRANSITION_GRID_SQUARES;
+ break;
+ }
+ CreateBattleStartTask(transitionId, MUS_BATTLE36);
+
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_WILD_BATTLES);
+ sub_80EECC8();
+ sub_80B1218();
+}
+
+static void CB2_EndWildBattle(void)
+{
+ CpuFill16(0, (void*)(BG_PLTT), BG_PLTT_SIZE);
+ ResetOamRange(0, 128);
+
+ if (IsPlayerDefeated(gBattleOutcome) == TRUE && !InBattlePyramid() && !InBattlePike())
+ {
+ SetMainCallback2(CB2_WhiteOut);
+ }
+ else
+ {
+ SetMainCallback2(CB2_ReturnToField);
+ gFieldCallback = sub_80AF6F0;
+ }
+}
+
+static void CB2_EndScriptedWildBattle(void)
+{
+ CpuFill16(0, (void*)(BG_PLTT), BG_PLTT_SIZE);
+ ResetOamRange(0, 128);
+
+ if (IsPlayerDefeated(gBattleOutcome) == TRUE)
+ {
+ if (InBattlePyramid())
+ SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
+ else
+ SetMainCallback2(CB2_WhiteOut);
+ }
+ else
+ {
+ SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
+ }
+}
+
+u8 BattleSetup_GetTerrainId(void)
+{
+ u16 tileBehavior;
+ s16 x, y;
+
+ PlayerGetDestCoords(&x, &y);
+ tileBehavior = MapGridGetMetatileBehaviorAt(x, y);
+
+ if (MetatileBehavior_IsTallGrass(tileBehavior))
+ return BATTLE_TERRAIN_GRASS;
+ if (MetatileBehavior_IsLongGrass(tileBehavior))
+ return BATTLE_TERRAIN_LONG_GRASS;
+ if (MetatileBehavior_IsSandOrDeepSand(tileBehavior))
+ return BATTLE_TERRAIN_SAND;
+
+ switch (gMapHeader.mapType)
+ {
+ case MAP_TYPE_TOWN:
+ case MAP_TYPE_CITY:
+ case MAP_TYPE_ROUTE:
+ break;
+ case MAP_TYPE_UNDERGROUND:
+ if (MetatileBehavior_IsMB_0B(tileBehavior))
+ return BATTLE_TERRAIN_BUILDING;
+ if (MetatileBehavior_IsSurfableWaterOrUnderwater(tileBehavior))
+ return BATTLE_TERRAIN_POND;
+ return BATTLE_TERRAIN_CAVE;
+ case MAP_TYPE_INDOOR:
+ case MAP_TYPE_SECRET_BASE:
+ return BATTLE_TERRAIN_BUILDING;
+ case MAP_TYPE_UNDERWATER:
+ return BATTLE_TERRAIN_UNDERWATER;
+ case MAP_TYPE_6:
+ if (MetatileBehavior_IsSurfableWaterOrUnderwater(tileBehavior))
+ return BATTLE_TERRAIN_WATER;
+ return BATTLE_TERRAIN_PLAIN;
+ }
+ if (MetatileBehavior_IsDeepOrOceanWater(tileBehavior))
+ return BATTLE_TERRAIN_WATER;
+ if (MetatileBehavior_IsSurfableWaterOrUnderwater(tileBehavior))
+ return BATTLE_TERRAIN_POND;
+ if (MetatileBehavior_IsMountain(tileBehavior))
+ return BATTLE_TERRAIN_MOUNTAIN;
+ if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING))
+ {
+ if (MetatileBehavior_GetBridgeSth(tileBehavior))
+ return BATTLE_TERRAIN_POND;
+ if (MetatileBehavior_IsBridge(tileBehavior) == TRUE)
+ return BATTLE_TERRAIN_WATER;
+ }
+ if (gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(ROUTE113) && gSaveBlock1Ptr->location.mapNum == MAP_NUM(ROUTE113))
+ return BATTLE_TERRAIN_SAND;
+ if (GetSav1Weather() == 8)
+ return BATTLE_TERRAIN_SAND;
+
+ return BATTLE_TERRAIN_PLAIN;
+}
+
+static u8 GetBattleTransitionTypeByMap(void)
+{
+ u16 tileBehavior;
+ s16 x, y;
+
+ PlayerGetDestCoords(&x, &y);
+ tileBehavior = MapGridGetMetatileBehaviorAt(x, y);
+ if (Overworld_GetFlashLevel())
+ return B_TRANSITION_SHUFFLE;
+ if (!MetatileBehavior_IsSurfableWaterOrUnderwater(tileBehavior))
+ {
+ switch (gMapHeader.mapType)
+ {
+ case MAP_TYPE_UNDERGROUND:
+ return B_TRANSITION_SWIRL;
+ case MAP_TYPE_UNDERWATER:
+ return B_TRANSITION_BIG_POKEBALL;
+ default:
+ return B_TRANSITION_BLUR;
+ }
+ }
+ return B_TRANSITION_BIG_POKEBALL;
+}
+
+static u16 GetSumOfPlayerPartyLevel(u8 numMons)
+{
+ u8 sum = 0;
+ int i;
+
+ for (i = 0; i < PARTY_SIZE; i++)
+ {
+ u32 species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2);
+
+ if (species != SPECIES_EGG && species != SPECIES_NONE && GetMonData(&gPlayerParty[i], MON_DATA_HP) != 0)
+ {
+ sum += GetMonData(&gPlayerParty[i], MON_DATA_LEVEL);
+ if (--numMons == 0)
+ break;
+ }
+ }
+ return sum;
+}
+
+static u8 GetSumOfEnemyPartyLevel(u16 opponentId, u8 numMons)
+{
+ u8 i;
+ u8 sum;
+ u32 count = numMons;
+
+ if (gTrainers[opponentId].partySize < count)
+ count = gTrainers[opponentId].partySize;
+
+ sum = 0;
+
+ switch (gTrainers[opponentId].partyFlags)
+ {
+ case 0:
+ {
+ const struct TrainerMonNoItemDefaultMoves *party;
+ party = gTrainers[opponentId].party.NoItemDefaultMoves;
+ for (i = 0; i < count; i++)
+ sum += party[i].lvl;
+ }
+ break;
+ case F_TRAINER_PARTY_CUSTOM_MOVESET:
+ {
+ const struct TrainerMonNoItemCustomMoves *party;
+ party = gTrainers[opponentId].party.NoItemCustomMoves;
+ for (i = 0; i < count; i++)
+ sum += party[i].lvl;
+ }
+ break;
+ case F_TRAINER_PARTY_HELD_ITEM:
+ {
+ const struct TrainerMonItemDefaultMoves *party;
+ party = gTrainers[opponentId].party.ItemDefaultMoves;
+ for (i = 0; i < count; i++)
+ sum += party[i].lvl;
+ }
+ break;
+ case F_TRAINER_PARTY_CUSTOM_MOVESET | F_TRAINER_PARTY_HELD_ITEM:
+ {
+ const struct TrainerMonItemCustomMoves *party;
+ party = gTrainers[opponentId].party.ItemCustomMoves;
+ for (i = 0; i < count; i++)
+ sum += party[i].lvl;
+ }
+ break;
+ }
+
+ return sum;
+}
+
+static u8 GetWildBattleTransition(void)
+{
+ u8 transitionType = GetBattleTransitionTypeByMap();
+ u8 enemyLevel = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL);
+ u8 playerLevel = GetSumOfPlayerPartyLevel(1);
+
+ if (enemyLevel < playerLevel)
+ {
+ if (InBattlePyramid())
+ return B_TRANSITION_BLUR;
+ else
+ return sBattleTransitionTable_Wild[transitionType][0];
+ }
+ else
+ {
+ if (InBattlePyramid())
+ return B_TRANSITION_GRID_SQUARES;
+ else
+ return sBattleTransitionTable_Wild[transitionType][1];
+ }
+}
+
+static u8 GetTrainerBattleTransition(void)
+{
+ u8 minPartyCount;
+ u8 transitionType;
+ u8 enemyLevel;
+ u8 playerLevel;
+
+ if (gTrainerBattleOpponent_A == TRAINER_SECRET_BASE)
+ return B_TRANSITION_CHAMPION;
+
+ if (gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_ELITE_FOUR)
+ {
+ if (gTrainerBattleOpponent_A == TRAINER_SIDNEY)
+ return B_TRANSITION_SYDNEY;
+ if (gTrainerBattleOpponent_A == TRAINER_PHOEBE)
+ return B_TRANSITION_PHOEBE;
+ if (gTrainerBattleOpponent_A == TRAINER_GLACIA)
+ return B_TRANSITION_GLACIA;
+ if (gTrainerBattleOpponent_A == TRAINER_DRAKE)
+ return B_TRANSITION_DRAKE;
+ return B_TRANSITION_CHAMPION;
+ }
+
+ if (gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_CHAMPION)
+ return B_TRANSITION_CHAMPION;
+
+ if (gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_TEAM_MAGMA
+ || gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_MAGMA_LEADER
+ || gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_MAGMA_ADMIN)
+ return B_TRANSITION_MAGMA;
+
+ if (gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_TEAM_AQUA
+ || gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_AQUA_LEADER
+ || gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_AQUA_ADMIN)
+ return B_TRANSITION_AQUA;
+
+ if (gTrainers[gTrainerBattleOpponent_A].doubleBattle == TRUE)
+ minPartyCount = 2; // double battles always at least have 2 pokemon.
+ else
+ minPartyCount = 1;
+
+ transitionType = GetBattleTransitionTypeByMap();
+ enemyLevel = GetSumOfEnemyPartyLevel(gTrainerBattleOpponent_A, minPartyCount);
+ playerLevel = GetSumOfPlayerPartyLevel(minPartyCount);
+
+ if (enemyLevel < playerLevel)
+ return sBattleTransitionTable_Trainer[transitionType][0];
+ else
+ return sBattleTransitionTable_Trainer[transitionType][1];
+}
+
+u8 sub_80B100C(s32 arg0)
+{
+ u16 var;
+ u8 enemyLevel = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL);
+ u8 playerLevel = GetSumOfPlayerPartyLevel(1);
+
+ if (enemyLevel < playerLevel)
+ {
+ switch (arg0)
+ {
+ case 11:
+ case 12:
+ case 13:
+ return B_TRANSITION_POKEBALLS_TRAIL;
+ case 10:
+ return sUnknown_0854FEA4[Random() % ARRAY_COUNT(sUnknown_0854FEA4)];
+ case 3:
+ return sUnknown_0854FEA7[Random() % ARRAY_COUNT(sUnknown_0854FEA7)];
+ }
+
+ if (VarGet(VAR_0x40CE) != 3)
+ return sUnknown_0854FE98[Random() % ARRAY_COUNT(sUnknown_0854FE98)];
+ }
+ else
+ {
+ switch (arg0)
+ {
+ case 11:
+ case 12:
+ case 13:
+ return B_TRANSITION_BIG_POKEBALL;
+ case 10:
+ return sUnknown_0854FEA4[Random() % ARRAY_COUNT(sUnknown_0854FEA4)];
+ case 3:
+ return sUnknown_0854FEA7[Random() % ARRAY_COUNT(sUnknown_0854FEA7)];
+ }
+
+ if (VarGet(VAR_0x40CE) != 3)
+ return sUnknown_0854FE98[Random() % ARRAY_COUNT(sUnknown_0854FE98)];
+ }
+
+ var = gSaveBlock2Ptr->frontier.field_CB4[gSaveBlock2Ptr->frontier.field_CB2 * 2 + 0]
+ + gSaveBlock2Ptr->frontier.field_CB4[gSaveBlock2Ptr->frontier.field_CB2 * 2 + 1];
+
+ return sUnknown_0854FE98[var % ARRAY_COUNT(sUnknown_0854FE98)];
+}
+
+void ChooseStarter(void)
+{
+ SetMainCallback2(CB2_ChooseStarter);
+ gMain.savedCallback = CB2_GiveStarter;
+}
+
+static void CB2_GiveStarter(void)
+{
+ u16 starterMon;
+
+ *GetVarPointer(VAR_FIRST_POKE) = gSpecialVar_Result;
+ starterMon = GetStarterPokemon(gSpecialVar_Result);
+ ScriptGiveMon(starterMon, 5, 0, 0, 0, 0);
+ ResetTasks();
+ PlayBattleBGM();
+ SetMainCallback2(CB2_StartFirstBattle);
+ BattleTransition_Start(B_TRANSITION_BLUR);
+}
+
+static void CB2_StartFirstBattle(void)
+{
+ UpdatePaletteFade();
+ RunTasks();
+
+ if (IsBattleTransitionDone() == TRUE)
+ {
+ gBattleTypeFlags = BATTLE_TYPE_FIRST_BATTLE;
+ gMain.savedCallback = CB2_EndFirstBattle;
+ FreeAllWindowBuffers();
+ SetMainCallback2(CB2_InitBattle);
+ prev_quest_postbuffer_cursor_backup_reset();
+ ResetPoisonStepCounter();
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_WILD_BATTLES);
+ sub_80EECC8();
+ sub_80B1218();
+ }
+}
+
+static void CB2_EndFirstBattle(void)
+{
+ Overworld_ClearSavedMusic();
+ SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
+}
+
+static void sub_80B1218(void)
+{
+ if (GetGameStat(GAME_STAT_WILD_BATTLES) % 60 == 0)
+ UpdateGymLeaderRematch();
+}
+
+static void sub_80B1234(void)
+{
+ if (GetGameStat(GAME_STAT_TRAINER_BATTLES) % 20 == 0)
+ UpdateGymLeaderRematch();
+}
+
+// why not just use the macros? maybe its because they didnt want to uncast const every time?
+static u32 TrainerBattleLoadArg32(const u8 *ptr)
+{
+ return T1_READ_32(ptr);
+}
+
+static u16 TrainerBattleLoadArg16(const u8 *ptr)
+{
+ return T1_READ_16(ptr);
+}
+
+static u8 TrainerBattleLoadArg8(const u8 *ptr)
+{
+ return T1_READ_8(ptr);
+}
+
+static u16 GetTrainerAFlag(void)
+{
+ return FLAG_TRAINER_FLAG_START + gTrainerBattleOpponent_A;
+}
+
+static u16 GetTrainerBFlag(void)
+{
+ return FLAG_TRAINER_FLAG_START + gTrainerBattleOpponent_B;
+}
+
+static bool32 IsPlayerDefeated(u32 battleOutcome)
+{
+ switch (battleOutcome)
+ {
+ case B_OUTCOME_LOST:
+ case B_OUTCOME_DREW:
+ return TRUE;
+ case B_OUTCOME_WON:
+ case B_OUTCOME_RAN:
+ case B_OUTCOME_PLAYER_TELEPORTED:
+ case B_OUTCOME_MON_FLED:
+ case B_OUTCOME_CAUGHT:
+ return FALSE;
+ default:
+ return FALSE;
+ }
+}
+
+void ResetTrainerOpponentIds(void)
+{
+ gTrainerBattleOpponent_A = 0;
+ gTrainerBattleOpponent_B = 0;
+}
+
+static void InitTrainerBattleVariables(void)
+{
+ sTrainerBattleMode = 0;
+ if (gApproachingTrainerId == 0)
+ {
+ sTrainerAIntroSpeech = NULL;
+ sTrainerADefeatSpeech = NULL;
+ sTrainerABattleScriptRetAddr = NULL;
+ }
+ else
+ {
+ sTrainerBIntroSpeech = NULL;
+ sTrainerBDefeatSpeech = NULL;
+ sTrainerBBattleScriptRetAddr = NULL;
+ }
+ sTrainerEventObjectLocalId = 0;
+ sTrainerVictorySpeech = NULL;
+ sTrainerCannotBattleSpeech = NULL;
+ sTrainerBattleEndScript = NULL;
+}
+
+static inline void SetU8(void *ptr, u8 value)
+{
+ *(u8*)(ptr) = value;
+}
+
+static inline void SetU16(void *ptr, u16 value)
+{
+ *(u16*)(ptr) = value;
+}
+
+static inline void SetU32(void *ptr, u32 value)
+{
+ *(u32*)(ptr) = value;
+}
+
+static inline void SetPtr(const void *ptr, const void* value)
+{
+ *(const void**)(ptr) = value;
+}
+
+static void TrainerBattleLoadArgs(const struct TrainerBattleParameter *specs, const u8 *data)
+{
+ while (1)
+ {
+ switch (specs->ptrType)
+ {
+ case TRAINER_PARAM_LOAD_VAL_8BIT:
+ SetU8(specs->varPtr, TrainerBattleLoadArg8(data));
+ data += 1;
+ break;
+ case TRAINER_PARAM_LOAD_VAL_16BIT:
+ SetU16(specs->varPtr, TrainerBattleLoadArg16(data));
+ data += 2;
+ break;
+ case TRAINER_PARAM_LOAD_VAL_32BIT:
+ SetU32(specs->varPtr, TrainerBattleLoadArg32(data));
+ data += 4;
+ break;
+ case TRAINER_PARAM_CLEAR_VAL_8BIT:
+ SetU8(specs->varPtr, 0);
+ break;
+ case TRAINER_PARAM_CLEAR_VAL_16BIT:
+ SetU16(specs->varPtr, 0);
+ break;
+ case TRAINER_PARAM_CLEAR_VAL_32BIT:
+ SetU32(specs->varPtr, 0);
+ break;
+ case TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR:
+ SetPtr(specs->varPtr, data);
+ return;
+ }
+ specs++;
+ }
+}
+
+void SetMapVarsToTrainer(void)
+{
+ if (sTrainerEventObjectLocalId != 0)
+ {
+ gSpecialVar_LastTalked = sTrainerEventObjectLocalId;
+ gSelectedEventObject = GetEventObjectIdByLocalIdAndMap(sTrainerEventObjectLocalId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup);
+ }
+}
+
+const u8 *BattleSetup_ConfigureTrainerBattle(const u8 *data)
+{
+ InitTrainerBattleVariables();
+ sTrainerBattleMode = TrainerBattleLoadArg8(data);
+
+ switch (sTrainerBattleMode)
+ {
+ case TRAINER_BATTLE_NORMAL_NO_INTRO_TEXT:
+ TrainerBattleLoadArgs(sOrdinaryNoIntroBattleParams, data);
+ return EventScript_2713C2;
+ case TRAINER_BATTLE_DOUBLE:
+ TrainerBattleLoadArgs(sDoubleBattleParams, data);
+ SetMapVarsToTrainer();
+ return EventScript_TryDoDoubleTrainerBattle;
+ case TRAINER_BATTLE_CONTINUE_SCRIPT:
+ if (gApproachingTrainerId == 0)
+ {
+ TrainerBattleLoadArgs(sContinueScriptBattleParams, data);
+ SetMapVarsToTrainer();
+ }
+ else
+ {
+ TrainerBattleLoadArgs(sTrainerBContinueScriptBattleParams, data);
+ }
+ return EventScript_271362;
+ case TRAINER_BATTLE_CONTINUE_SCRIPT_NO_MUSIC:
+ TrainerBattleLoadArgs(sContinueScriptBattleParams, data);
+ SetMapVarsToTrainer();
+ return EventScript_271362;
+ case TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE:
+ case TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE_NO_MUSIC:
+ TrainerBattleLoadArgs(sContinueScriptDoubleBattleParams, data);
+ SetMapVarsToTrainer();
+ return EventScript_TryDoDoubleTrainerBattle;
+ case TRAINER_BATTLE_REMATCH_DOUBLE:
+ TrainerBattleLoadArgs(sDoubleBattleParams, data);
+ SetMapVarsToTrainer();
+ gTrainerBattleOpponent_A = GetRematchTrainerId(gTrainerBattleOpponent_A);
+ return EventScript_TryDoDoubleRematchBattle;
+ case TRAINER_BATTLE_REMATCH:
+ TrainerBattleLoadArgs(sOrdinaryBattleParams, data);
+ SetMapVarsToTrainer();
+ gTrainerBattleOpponent_A = GetRematchTrainerId(gTrainerBattleOpponent_A);
+ return EventScript_2713D1;
+ case TRAINER_BATTLE_9:
+ if (gApproachingTrainerId == 0)
+ {
+ TrainerBattleLoadArgs(sOrdinaryBattleParams, data);
+ SetMapVarsToTrainer();
+ gTrainerBattleOpponent_A = sub_81A9AA8(gSpecialVar_LastTalked);
+ }
+ else
+ {
+ TrainerBattleLoadArgs(sTrainerBOrdinaryBattleParams, data);
+ gTrainerBattleOpponent_B = sub_81A9AA8(gSpecialVar_LastTalked);
+ }
+ return EventScript_271362;
+ case TRAINER_BATTLE_SET_TRAINER_A:
+ TrainerBattleLoadArgs(sOrdinaryBattleParams, data);
+ return NULL;
+ case TRAINER_BATTLE_SET_TRAINER_B:
+ TrainerBattleLoadArgs(sTrainerBOrdinaryBattleParams, data);
+ return NULL;
+ case TRAINER_BATTLE_12:
+ if (gApproachingTrainerId == 0)
+ {
+ TrainerBattleLoadArgs(sOrdinaryBattleParams, data);
+ SetMapVarsToTrainer();
+ gTrainerBattleOpponent_A = sub_81D6180(gSpecialVar_LastTalked);
+ }
+ else
+ {
+ TrainerBattleLoadArgs(sTrainerBOrdinaryBattleParams, data);
+ gTrainerBattleOpponent_B = sub_81D6180(gSpecialVar_LastTalked);
+ }
+ return EventScript_271362;
+ default:
+ if (gApproachingTrainerId == 0)
+ {
+ TrainerBattleLoadArgs(sOrdinaryBattleParams, data);
+ SetMapVarsToTrainer();
+ }
+ else
+ {
+ TrainerBattleLoadArgs(sTrainerBOrdinaryBattleParams, data);
+ }
+ return EventScript_271362;
+ }
+}
+
+void ConfigureAndSetUpOneTrainerBattle(u8 trainerEventObjId, const u8 *trainerScript)
+{
+ gSelectedEventObject = trainerEventObjId;
+ gSpecialVar_LastTalked = gEventObjects[trainerEventObjId].localId;
+ BattleSetup_ConfigureTrainerBattle(trainerScript + 1);
+ ScriptContext1_SetupScript(EventScript_271354);
+ ScriptContext2_Enable();
+}
+
+void ConfigureTwoTrainersBattle(u8 trainerEventObjId, const u8 *trainerScript)
+{
+ gSelectedEventObject = trainerEventObjId;
+ gSpecialVar_LastTalked = gEventObjects[trainerEventObjId].localId;
+ BattleSetup_ConfigureTrainerBattle(trainerScript + 1);
+}
+
+void SetUpTwoTrainersBattle(void)
+{
+ ScriptContext1_SetupScript(EventScript_271354);
+ ScriptContext2_Enable();
+}
+
+bool32 GetTrainerFlagFromScriptPointer(const u8 *data)
+{
+ u32 flag = TrainerBattleLoadArg16(data + 2);
+ return FlagGet(FLAG_TRAINER_FLAG_START + flag);
+}
+
+void sub_80B16D8(void)
+{
+ struct EventObject *eventObject = &gEventObjects[gSelectedEventObject];
+
+ SetTrainerMovementType(eventObject, GetTrainerFacingDirectionMovementType(eventObject->facingDirection));
+}
+
+u8 GetTrainerBattleMode(void)
+{
+ return sTrainerBattleMode;
+}
+
+bool8 GetTrainerFlag(void)
+{
+ if (InBattlePyramid())
+ return GetBattlePyramidTrainerFlag(gSelectedEventObject);
+ else if (InTrainerHill())
+ return GetTrainerHillTrainerFlag(gSelectedEventObject);
+ else
+ return FlagGet(GetTrainerAFlag());
+}
+
+static void SetBattledTrainersFlags(void)
+{
+ if (gTrainerBattleOpponent_B != 0)
+ FlagSet(GetTrainerBFlag());
+ FlagSet(GetTrainerAFlag());
+}
+
+static void SetBattledTrainerFlag(void)
+{
+ FlagSet(GetTrainerAFlag());
+}
+
+bool8 HasTrainerBeenFought(u16 trainerId)
+{
+ return FlagGet(FLAG_TRAINER_FLAG_START + trainerId);
+}
+
+void SetTrainerFlag(u16 trainerId)
+{
+ FlagSet(FLAG_TRAINER_FLAG_START + trainerId);
+}
+
+void ClearTrainerFlag(u16 trainerId)
+{
+ FlagClear(FLAG_TRAINER_FLAG_START + trainerId);
+}
+
+void BattleSetup_StartTrainerBattle(void)
+{
+ if (gNoOfApproachingTrainers == 2)
+ gBattleTypeFlags = (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_TRAINER);
+ else
+ gBattleTypeFlags = (BATTLE_TYPE_TRAINER);
+
+ if (InBattlePyramid())
+ {
+ VarSet(VAR_TEMP_E, 0);
+ gBattleTypeFlags |= BATTLE_TYPE_PYRAMID;
+
+ if (gNoOfApproachingTrainers == 2)
+ {
+ sub_816306C(1);
+ ZeroMonData(&gEnemyParty[1]);
+ ZeroMonData(&gEnemyParty[2]);
+ ZeroMonData(&gEnemyParty[4]);
+ ZeroMonData(&gEnemyParty[5]);
+ }
+ else
+ {
+ sub_8163048(1);
+ ZeroMonData(&gEnemyParty[1]);
+ ZeroMonData(&gEnemyParty[2]);
+ }
+
+ sub_81A9B04();
+ }
+ else if (sub_81D5C18())
+ {
+ gBattleTypeFlags |= BATTLE_TYPE_x4000000;
+
+ if (gNoOfApproachingTrainers == 2)
+ sub_81D639C();
+ else
+ sub_81D6384();
+
+ sub_81D61E8();
+ }
+
+ sNoOfPossibleTrainerRetScripts = gNoOfApproachingTrainers;
+ gNoOfApproachingTrainers = 0;
+ sShouldCheckTrainerBScript = FALSE;
+ gUnknown_03006080 = 0;
+ gMain.savedCallback = CB2_EndTrainerBattle;
+
+ if (InBattlePyramid() || sub_81D5C18())
+ sub_80B0828();
+ else
+ DoTrainerBattle();
+
+ ScriptContext1_Stop();
+}
+
+static void CB2_EndTrainerBattle(void)
+{
+ if (gTrainerBattleOpponent_A == TRAINER_SECRET_BASE)
+ {
+ SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
+ }
+ else if (IsPlayerDefeated(gBattleOutcome) == TRUE)
+ {
+ if (InBattlePyramid() || sub_81D5C18())
+ SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
+ else
+ SetMainCallback2(CB2_WhiteOut);
+ }
+ else
+ {
+ SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
+ if (!InBattlePyramid() && !sub_81D5C18())
+ {
+ RegisterTrainerInMatchCall();
+ SetBattledTrainersFlags();
+ }
+ }
+}
+
+static void CB2_EndRematchBattle(void)
+{
+ if (gTrainerBattleOpponent_A == TRAINER_SECRET_BASE)
+ {
+ SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
+ }
+ else if (IsPlayerDefeated(gBattleOutcome) == TRUE)
+ {
+ SetMainCallback2(CB2_WhiteOut);
+ }
+ else
+ {
+ SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
+ RegisterTrainerInMatchCall();
+ SetBattledTrainersFlags();
+ HandleRematchVarsOnBattleEnd();
+ }
+}
+
+void BattleSetup_StartRematchBattle(void)
+{
+ gBattleTypeFlags = BATTLE_TYPE_TRAINER;
+ gMain.savedCallback = CB2_EndRematchBattle;
+ DoTrainerBattle();
+ ScriptContext1_Stop();
+}
+
+void ShowTrainerIntroSpeech(void)
+{
+ if (InBattlePyramid())
+ {
+ if (gNoOfApproachingTrainers == 0 || gNoOfApproachingTrainers == 1)
+ sub_81A9EDC(sub_81A9AA8(gSpecialVar_LastTalked));
+ else
+ sub_81A9EDC(sub_81A9AA8(gEventObjects[gApproachingTrainers[gApproachingTrainerId].eventObjectId].localId));
+
+ sub_80982B8();
+ }
+ else if (sub_81D5C18())
+ {
+ if (gNoOfApproachingTrainers == 0 || gNoOfApproachingTrainers == 1)
+ sub_81D572C(2, sub_81D6180(gSpecialVar_LastTalked));
+ else
+ sub_81D572C(2, sub_81D6180(gEventObjects[gApproachingTrainers[gApproachingTrainerId].eventObjectId].localId));
+
+ sub_80982B8();
+ }
+ else
+ {
+ ShowFieldMessage(GetIntroSpeechOfApproachingTrainer());
+ }
+}
+
+const u8 *BattleSetup_GetScriptAddrAfterBattle(void)
+{
+ if (sTrainerBattleEndScript != NULL)
+ return sTrainerBattleEndScript;
+ else
+ return EventScript_TestSignpostMsg;
+}
+
+const u8 *BattleSetup_GetTrainerPostBattleScript(void)
+{
+ if (sShouldCheckTrainerBScript)
+ {
+ sShouldCheckTrainerBScript = FALSE;
+ if (sTrainerBBattleScriptRetAddr != NULL)
+ {
+ gUnknown_03006080 = 1;
+ return sTrainerBBattleScriptRetAddr;
+ }
+ }
+ else
+ {
+ if (sTrainerABattleScriptRetAddr != NULL)
+ {
+ gUnknown_03006080 = 0;
+ return sTrainerABattleScriptRetAddr;
+ }
+ }
+
+ return EventScript_TryGetTrainerScript;
+}
+
+void ShowTrainerCantBattleSpeech(void)
+{
+ ShowFieldMessage(GetTrainerCantBattleSpeech());
+}
+
+void SetUpTrainerEncounterMusic(void)
+{
+ u16 trainerId;
+ u16 music;
+
+ if (gApproachingTrainerId == 0)
+ trainerId = gTrainerBattleOpponent_A;
+ else
+ trainerId = gTrainerBattleOpponent_B;
+
+ if (sTrainerBattleMode != TRAINER_BATTLE_CONTINUE_SCRIPT_NO_MUSIC
+ && sTrainerBattleMode != TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE_NO_MUSIC)
+ {
+ switch (GetTrainerEncounterMusicId(trainerId))
+ {
+ case TRAINER_ENCOUNTER_MUSIC_MALE:
+ music = MUS_BOYEYE;
+ break;
+ case TRAINER_ENCOUNTER_MUSIC_FEMALE:
+ music = MUS_GIRLEYE;
+ break;
+ case TRAINER_ENCOUNTER_MUSIC_GIRL:
+ music = MUS_SYOUJOEYE;
+ break;
+ case TRAINER_ENCOUNTER_MUSIC_INTENSE:
+ music = MUS_HAGESHII;
+ break;
+ case TRAINER_ENCOUNTER_MUSIC_COOL:
+ music = MUS_KAKKOII;
+ break;
+ case TRAINER_ENCOUNTER_MUSIC_AQUA:
+ music = MUS_AQA_0;
+ break;
+ case TRAINER_ENCOUNTER_MUSIC_MAGMA:
+ music = MUS_MGM0;
+ break;
+ case TRAINER_ENCOUNTER_MUSIC_SWIMMER:
+ music = MUS_SWIMEYE;
+ break;
+ case TRAINER_ENCOUNTER_MUSIC_TWINS:
+ music = MUS_HUTAGO;
+ break;
+ case TRAINER_ENCOUNTER_MUSIC_ELITE_FOUR:
+ music = MUS_SITENNOU;
+ break;
+ case TRAINER_ENCOUNTER_MUSIC_HIKER:
+ music = MUS_YAMA_EYE;
+ break;
+ case TRAINER_ENCOUNTER_MUSIC_INTERVIEWER:
+ music = MUS_INTER_V;
+ break;
+ case TRAINER_ENCOUNTER_MUSIC_RICH:
+ music = MUS_TEST;
+ break;
+ default:
+ music = MUS_AYASII;
+ }
+ PlayNewMapMusic(music);
+ }
+}
+
+static const u8 *ReturnEmptyStringIfNull(const u8 *string)
+{
+ if (string == NULL)
+ return gText_EmptyString2;
+ else
+ return string;
+}
+
+static const u8 *GetIntroSpeechOfApproachingTrainer(void)
+{
+ if (gApproachingTrainerId == 0)
+ return ReturnEmptyStringIfNull(sTrainerAIntroSpeech);
+ else
+ return ReturnEmptyStringIfNull(sTrainerBIntroSpeech);
+}
+
+const u8 *GetTrainerALoseText(void)
+{
+ const u8 *string;
+
+ if (gTrainerBattleOpponent_A == TRAINER_SECRET_BASE)
+ string = GetSecretBaseTrainerLoseText();
+ else
+ string = sTrainerADefeatSpeech;
+
+ StringExpandPlaceholders(gStringVar4, ReturnEmptyStringIfNull(string));
+ return gStringVar4;
+}
+
+const u8 *GetTrainerBLoseText(void)
+{
+ StringExpandPlaceholders(gStringVar4, ReturnEmptyStringIfNull(sTrainerBDefeatSpeech));
+ return gStringVar4;
+}
+
+const u8 *GetTrainerWonSpeech(void)
+{
+ return ReturnEmptyStringIfNull(sTrainerVictorySpeech);
+}
+
+static const u8 *GetTrainerCantBattleSpeech(void)
+{
+ return ReturnEmptyStringIfNull(sTrainerCannotBattleSpeech);
+}
+
+static s32 FirstBattleTrainerIdToRematchTableId(const struct RematchTrainer *table, u16 trainerId)
+{
+ s32 i;
+
+ for (i = 0; i < REMATCH_TABLE_ENTRIES; i++)
+ {
+ if (table[i].trainerIds[0] == trainerId)
+ return i;
+ }
+
+ return -1;
+}
+
+static s32 TrainerIdToRematchTableId(const struct RematchTrainer *table, u16 trainerId)
+{
+ s32 i, j;
+
+ for (i = 0; i < REMATCH_TABLE_ENTRIES; i++)
+ {
+ for (j = 0; j < REMATCHES_COUNT; j++)
+ {
+ if (table[i].trainerIds[j] == 0)
+ break;
+ if (table[i].trainerIds[j] == trainerId)
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static bool32 sub_80B1D94(s32 rematchTableId)
+{
+ if (rematchTableId >= REMATCH_ELITE_FOUR_ENTRIES)
+ return TRUE;
+ else if (rematchTableId == REMATCH_WALLY_ENTRY)
+ return (FlagGet(FLAG_0x07E) == FALSE);
+ else
+ return FALSE;
+}
+
+static void SetRematchIdForTrainer(const struct RematchTrainer *table, u32 tableId)
+{
+ s32 i;
+
+ for (i = 1; i < REMATCHES_COUNT; i++)
+ {
+ u16 trainerId = table[tableId].trainerIds[i];
+
+ if (trainerId == 0)
+ break;
+ if (!HasTrainerBeenFought(trainerId))
+ break;
+ }
+
+ gSaveBlock1Ptr->trainerRematches[tableId] = i;
+}
+
+static bool32 UpdateRandomTrainerRematches(const struct RematchTrainer *table, u16 mapGroup, u16 mapNum)
+{
+ s32 i;
+ bool32 ret = FALSE;
+
+ for (i = 0; i <= REMATCH_WALLY_ENTRY; i++)
+ {
+ if (table[i].mapGroup == mapGroup && table[i].mapNum == mapNum && !sub_80B1D94(i))
+ {
+ if (gSaveBlock1Ptr->trainerRematches[i] != 0)
+ {
+ // Trainer already wants a rematch. Don't bother updating it.
+ ret = TRUE;
+ }
+ else if (FlagGet(FLAG_MATCH_CALL_REGISTERED + i)
+ && (Random() % 100) <= 30) // 31% chance of getting a rematch.
+ {
+ SetRematchIdForTrainer(table, i);
+ ret = TRUE;
+ }
+ }
+ }
+
+ return ret;
+}
+
+void UpdateRematchIfDefeated(s32 rematchTableId)
+{
+ if (HasTrainerBeenFought(gRematchTable[rematchTableId].trainerIds[0]) == TRUE)
+ SetRematchIdForTrainer(gRematchTable, rematchTableId);
+}
+
+static bool32 DoesSomeoneWantRematchIn_(const struct RematchTrainer *table, u16 mapGroup, u16 mapNum)
+{
+ s32 i;
+
+ for (i = 0; i < REMATCH_TABLE_ENTRIES; i++)
+ {
+ if (table[i].mapGroup == mapGroup && table[i].mapNum == mapNum && gSaveBlock1Ptr->trainerRematches[i] != 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static bool32 IsRematchTrainerIn_(const struct RematchTrainer *table, u16 mapGroup, u16 mapNum)
+{
+ s32 i;
+
+ for (i = 0; i < REMATCH_TABLE_ENTRIES; i++)
+ {
+ if (table[i].mapGroup == mapGroup && table[i].mapNum == mapNum)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static bool8 IsFirstTrainerIdReadyForRematch(const struct RematchTrainer *table, u16 firstBattleTrainerId)
+{
+ s32 tableId = FirstBattleTrainerIdToRematchTableId(table, firstBattleTrainerId);
+
+ if (tableId == -1)
+ return FALSE;
+ if (tableId >= 100)
+ return FALSE;
+ if (gSaveBlock1Ptr->trainerRematches[tableId] == 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static bool8 IsTrainerReadyForRematch_(const struct RematchTrainer *table, u16 trainerId)
+{
+ s32 tableId = TrainerIdToRematchTableId(table, trainerId);
+
+ if (tableId == -1)
+ return FALSE;
+ if (tableId >= 100)
+ return FALSE;
+ if (gSaveBlock1Ptr->trainerRematches[tableId] == 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static u16 GetRematchTrainerIdFromTable(const struct RematchTrainer *table, u16 firstBattleTrainerId)
+{
+ const struct RematchTrainer *trainerEntry;
+ s32 i;
+ s32 tableId = FirstBattleTrainerIdToRematchTableId(table, firstBattleTrainerId);
+
+ if (tableId == -1)
+ return FALSE;
+
+ trainerEntry = &table[tableId];
+ for (i = 1; i < REMATCHES_COUNT; i++)
+ {
+ if (trainerEntry->trainerIds[i] == 0) // previous entry was this trainer's last one
+ return trainerEntry->trainerIds[i - 1];
+ if (!HasTrainerBeenFought(trainerEntry->trainerIds[i]))
+ return trainerEntry->trainerIds[i];
+ }
+
+ return trainerEntry->trainerIds[REMATCHES_COUNT - 1]; // already beaten at max stage
+}
+
+static u16 GetLastBeatenRematchTrainerIdFromTable(const struct RematchTrainer *table, u16 firstBattleTrainerId)
+{
+ const struct RematchTrainer *trainerEntry;
+ s32 i;
+ s32 tableId = FirstBattleTrainerIdToRematchTableId(table, firstBattleTrainerId);
+
+ if (tableId == -1)
+ return FALSE;
+
+ trainerEntry = &table[tableId];
+ for (i = 1; i < REMATCHES_COUNT; i++)
+ {
+ if (trainerEntry->trainerIds[i] == 0) // previous entry was this trainer's last one
+ return trainerEntry->trainerIds[i - 1];
+ if (!HasTrainerBeenFought(trainerEntry->trainerIds[i]))
+ return trainerEntry->trainerIds[i - 1];
+ }
+
+ return trainerEntry->trainerIds[REMATCHES_COUNT - 1]; // already beaten at max stage
+}
+
+static void ClearTrainerWantRematchState(const struct RematchTrainer *table, u16 firstBattleTrainerId)
+{
+ s32 tableId = TrainerIdToRematchTableId(table, firstBattleTrainerId);
+
+ if (tableId != -1)
+ gSaveBlock1Ptr->trainerRematches[tableId] = 0;
+}
+
+static u32 GetTrainerMatchCallFlag(u32 trainerId)
+{
+ s32 i;
+
+ for (i = 0; i < REMATCH_TABLE_ENTRIES; i++)
+ {
+ if (gRematchTable[i].trainerIds[0] == trainerId)
+ return FLAG_MATCH_CALL_REGISTERED + i;
+ }
+
+ return 0xFFFF;
+}
+
+static void RegisterTrainerInMatchCall(void)
+{
+ if (FlagGet(FLAG_HAS_MATCH_CALL))
+ {
+ u32 matchCallFlagId = GetTrainerMatchCallFlag(gTrainerBattleOpponent_A);
+ if (matchCallFlagId != 0xFFFF)
+ FlagSet(matchCallFlagId);
+ }
+}
+
+static bool8 WasSecondRematchWon(const struct RematchTrainer *table, u16 firstBattleTrainerId)
+{
+ s32 tableId = FirstBattleTrainerIdToRematchTableId(table, firstBattleTrainerId);
+
+ if (tableId == -1)
+ return FALSE;
+ if (!HasTrainerBeenFought(table[tableId].trainerIds[1]))
+ return FALSE;
+
+ return TRUE;
+}
+
+static bool32 HasAtLeastFiveBadges(void)
+{
+ s32 i, count;
+
+ for (count = 0, i = 0; i < ARRAY_COUNT(sBadgeFlags); i++)
+ {
+ if (FlagGet(sBadgeFlags[i]) == TRUE)
+ {
+ if (++count >= 5)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+#define STEP_COUNTER_MAX 255
+
+void IncrementRematchStepCounter(void)
+{
+ if (HasAtLeastFiveBadges())
+ {
+ if (gSaveBlock1Ptr->trainerRematchStepCounter >= STEP_COUNTER_MAX)
+ gSaveBlock1Ptr->trainerRematchStepCounter = STEP_COUNTER_MAX;
+ else
+ gSaveBlock1Ptr->trainerRematchStepCounter++;
+ }
+}
+
+static bool32 IsRematchStepCounterMaxed(void)
+{
+ if (HasAtLeastFiveBadges() && gSaveBlock1Ptr->trainerRematchStepCounter >= STEP_COUNTER_MAX)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+void TryUpdateRandomTrainerRematches(u16 mapGroup, u16 mapNum)
+{
+ if (IsRematchStepCounterMaxed() && UpdateRandomTrainerRematches(gRematchTable, mapGroup, mapNum) == TRUE)
+ gSaveBlock1Ptr->trainerRematchStepCounter = 0;
+}
+
+bool32 DoesSomeoneWantRematchIn(u16 mapGroup, u16 mapNum)
+{
+ return DoesSomeoneWantRematchIn_(gRematchTable, mapGroup, mapNum);
+}
+
+bool32 IsRematchTrainerIn(u16 mapGroup, u16 mapNum)
+{
+ return IsRematchTrainerIn_(gRematchTable, mapGroup, mapNum);
+}
+
+static u16 GetRematchTrainerId(u16 trainerId)
+{
+ return GetRematchTrainerIdFromTable(gRematchTable, trainerId);
+}
+
+u16 GetLastBeatenRematchTrainerId(u16 trainerId)
+{
+ return GetLastBeatenRematchTrainerIdFromTable(gRematchTable, trainerId);
+}
+
+bool8 ShouldTryRematchBattle(void)
+{
+ if (IsFirstTrainerIdReadyForRematch(gRematchTable, gTrainerBattleOpponent_A))
+ return TRUE;
+
+ return WasSecondRematchWon(gRematchTable, gTrainerBattleOpponent_A);
+}
+
+bool8 IsTrainerReadyForRematch(void)
+{
+ return IsTrainerReadyForRematch_(gRematchTable, gTrainerBattleOpponent_A);
+}
+
+static void HandleRematchVarsOnBattleEnd(void)
+{
+ ClearTrainerWantRematchState(gRematchTable, gTrainerBattleOpponent_A);
+ SetBattledTrainersFlags();
+}
+
+void ShouldTryGetTrainerScript(void)
+{
+ if (sNoOfPossibleTrainerRetScripts > 1)
+ {
+ sNoOfPossibleTrainerRetScripts = 0;
+ sShouldCheckTrainerBScript = TRUE;
+ gSpecialVar_Result = TRUE;
+ }
+ else
+ {
+ sShouldCheckTrainerBScript = FALSE;
+ gSpecialVar_Result = FALSE;
+ }
+}
+
+u16 CountBattledRematchTeams(u16 trainerId)
+{
+ s32 i;
+
+ if (HasTrainerBeenFought(gRematchTable[trainerId].trainerIds[0]) != TRUE)
+ return 0;
+
+ for (i = 1; i < REMATCHES_COUNT; i++)
+ {
+ if (gRematchTable[trainerId].trainerIds[i] == 0)
+ break;
+ if (!HasTrainerBeenFought(gRematchTable[trainerId].trainerIds[i]))
+ break;
+ }
+
+ return i;
+}