#include "global.h" #include "battle.h" #include "battle_setup.h" #include "battle_transition.h" #include "main.h" #include "task.h" #include "pokemon_3.h" #include "safari_zone.h" #include "script.h" #include "constants/game_stat.h" #include "event_data.h" #include "constants/species.h" #include "songs.h" #include "metatile_behavior.h" #include "constants/maps.h" #include "field_player_avatar.h" #include "fieldmap.h" #include "trainer_classes.h" #include "trainer_ids.h" #include "random.h" #include "starter_choose.h" #include "script_pokemon_80F8.h" #include "constants/items.h" #include "palette.h" #include "window.h" #include "field_map_obj.h" #include "event_scripts.h" #include "trainer_see.h" #include "field_message_box.h" #include "trainer.h" #include "sound.h" #include "strings.h" #include "secret_base.h" #include "string_util.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 overworld_free_bg_tilemaps(void); extern void prev_quest_postbuffer_cursor_backup_reset(void); extern void ResetPoisonStepCounter(void); extern void sub_81BE72C(void); extern void FreezeMapObjects(void); extern void sub_808BCF4(void); extern void sub_80EECC8(void); extern void c2_exit_to_overworld_1_continue_scripts_restart_music(void); extern void c2_exit_to_overworld_2_switch(void); extern void Overworld_ClearSavedMusic(void); extern void CB2_WhiteOut(void); extern void sub_80AF6F0(void); extern void PlayBattleBGM(void); extern void sub_81DA57C(void); extern u8 GetSav1Weather(void); extern u8 Overworld_GetFlashLevel(void); extern u16 sub_81A9AA8(u8 localId); extern u16 sub_81D6180(u8 localId); extern bool8 GetBattlePyramidTrainerFlag(u8 mapObjId); extern bool8 GetTrainerHillTrainerFlag(u8 mapObjId); 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); extern void IncrementGameStat(u8 statId); extern u32 GetGameStat(u8 statId); extern u32 gBattleTypeFlags; extern u8 gBattleOutcome; extern void (*gFieldCallback)(void); extern u8 gApproachingTrainerId; extern u8 gNoOfApproachingTrainers; extern u16 gUnknown_03006080; // 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 data 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 sTrainerMapObjectLocalId = 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}, {&sTrainerMapObjectLocalId, 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}, {&sTrainerMapObjectLocalId, 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}, {&sTrainerMapObjectLocalId, 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}, {&sTrainerMapObjectLocalId, 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}, {&sTrainerMapObjectLocalId, 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}, {&sTrainerMapObjectLocalId, 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}, {&sTrainerMapObjectLocalId, 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] = { {{0x0025, 0x0028, 0x0029, 0x002a, 0x002b}, 0x0000, 0x0021}, {{0x02e1, 0x032c, 0x032d, 0x032e, 0x032f}, 0x0000, 0x0014}, {{0x002c, 0x002f, 0x0030, 0x0031, 0x0032}, 0x0000, 0x001a}, {{0x0039, 0x003c, 0x003d, 0x003e, 0x003f}, 0x0000, 0x0018}, {{0x0040, 0x0043, 0x0044, 0x0045, 0x0046}, 0x0000, 0x0018}, {{0x02af, 0x02b0, 0x02b1, 0x02b2, 0x02b3}, 0x0000, 0x0027}, {{0x02ff, 0x033c, 0x033d, 0x033e, 0x033f}, 0x0000, 0x0024}, {{0x005e, 0x0065, 0x0066, 0x0067, 0x0068}, 0x0000, 0x001a}, {{0x004e, 0x0054, 0x0055, 0x0056, 0x0057}, 0x0000, 0x001a}, {{0x006c, 0x006e, 0x006f, 0x0070, 0x0071}, 0x0018, 0x0014}, {{0x0072, 0x0078, 0x0079, 0x007a, 0x007b}, 0x0000, 0x0013}, {{0x0090, 0x034c, 0x034d, 0x034e, 0x034f}, 0x0018, 0x0038}, {{0x007f, 0x0084, 0x0085, 0x0086, 0x0087}, 0x0000, 0x0024}, {{0x0088, 0x008b, 0x008c, 0x008d, 0x008e}, 0x0000, 0x0013}, {{0x008f, 0x0093, 0x0094, 0x0095, 0x0096}, 0x0000, 0x001d}, {{0x009b, 0x00af, 0x00b0, 0x00b1, 0x00b2}, 0x0000, 0x0016}, {{0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb}, 0x0000, 0x001e}, {{0x02a0, 0x0338, 0x0339, 0x033a, 0x033b}, 0x0000, 0x002a}, {{0x00c3, 0x0340, 0x0341, 0x0342, 0x0343}, 0x0000, 0x0026}, {{0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8}, 0x0000, 0x0021}, {{0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2}, 0x0000, 0x001d}, {{0x00d8, 0x00db, 0x00dc, 0x00dd, 0x00de}, 0x0018, 0x000d}, {{0x02a9, 0x02aa, 0x02ab, 0x02ac, 0x02ad}, 0x0018, 0x0001}, {{0x00e2, 0x00e4, 0x00e5, 0x00e6, 0x00e7}, 0x0000, 0x0023}, {{0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2}, 0x0000, 0x0026}, {{0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd}, 0x0000, 0x0026}, {{0x00fe, 0x0101, 0x0102, 0x0103, 0x0104}, 0x0000, 0x0024}, {{0x0118, 0x011a, 0x011b, 0x011c, 0x011d}, 0x0000, 0x001f}, {{0x0111, 0x0114, 0x0115, 0x0116, 0x0117}, 0x0000, 0x001f}, {{0x011f, 0x0120, 0x0121, 0x0122, 0x0123}, 0x0000, 0x0020}, {{0x012e, 0x012f, 0x0130, 0x0131, 0x0132}, 0x0000, 0x0019}, {{0x0125, 0x0127, 0x0128, 0x0129, 0x012a}, 0x0000, 0x0012}, {{0x0133, 0x0134, 0x0135, 0x0136, 0x0137}, 0x0000, 0x001e}, {{0x0139, 0x013a, 0x013b, 0x013c, 0x013d}, 0x0018, 0x000c}, {{0x013e, 0x0148, 0x0149, 0x014a, 0x014b}, 0x0000, 0x0011}, {{0x0153, 0x015a, 0x015b, 0x015c, 0x015d}, 0x0000, 0x0015}, {{0x0178, 0x017b, 0x017c, 0x017d, 0x017e}, 0x0000, 0x002b}, {{0x0171, 0x0172, 0x0173, 0x0174, 0x0175}, 0x0000, 0x0020}, {{0x0166, 0x0168, 0x0169, 0x016a, 0x016b}, 0x0000, 0x0019}, {{0x016c, 0x016d, 0x016e, 0x016f, 0x0170}, 0x0000, 0x0020}, {{0x0182, 0x0184, 0x0185, 0x0186, 0x0187}, 0x0000, 0x002b}, {{0x0161, 0x0162, 0x0163, 0x0164, 0x0165}, 0x0000, 0x0019}, {{0x0179, 0x0334, 0x0335, 0x0336, 0x0337}, 0x0000, 0x0029}, {{0x0188, 0x0189, 0x018a, 0x018b, 0x018c}, 0x0018, 0x0001}, {{0x0196, 0x0199, 0x019a, 0x019b, 0x019c}, 0x0000, 0x0023}, {{0x01a3, 0x01a5, 0x01a6, 0x01a7, 0x01a8}, 0x0000, 0x001c}, {{0x01ab, 0x01ae, 0x01af, 0x01b0, 0x01b1}, 0x0000, 0x001e}, {{0x01b2, 0x01b5, 0x01b6, 0x01b7, 0x01b8}, 0x0000, 0x001c}, {{0x01c1, 0x01d1, 0x01d2, 0x01d3, 0x01d4}, 0x0000, 0x0027}, {{0x01da, 0x01dd, 0x01de, 0x01df, 0x01e0}, 0x0018, 0x000d}, {{0x01e1, 0x01e2, 0x01e7, 0x01e8, 0x01e9}, 0x0000, 0x0012}, {{0x01ec, 0x01f1, 0x01f2, 0x01f3, 0x01f4}, 0x0000, 0x0028}, {{0x02e4, 0x0330, 0x0331, 0x0332, 0x0333}, 0x0000, 0x0017}, {{0x0200, 0x0203, 0x0204, 0x0205, 0x0206}, 0x0000, 0x0019}, {{0x0221, 0x0224, 0x0225, 0x0226, 0x0227}, 0x0000, 0x0020}, {{0x021a, 0x021d, 0x021e, 0x021f, 0x0220}, 0x0000, 0x0020}, {{0x0009, 0x0348, 0x0349, 0x034a, 0x034b}, 0x0018, 0x0011}, {{0x022f, 0x0232, 0x0233, 0x0234, 0x0235}, 0x0000, 0x0022}, {{0x0228, 0x022b, 0x022c, 0x022d, 0x022e}, 0x0000, 0x0022}, {{0x025c, 0x025f, 0x0260, 0x0261, 0x0262}, 0x0000, 0x0013}, {{0x026d, 0x026e, 0x026f, 0x0270, 0x0271}, 0x0018, 0x000b}, {{0x0273, 0x027c, 0x027d, 0x027e, 0x027f}, 0x0000, 0x001b}, {{0x0001, 0x0344, 0x0345, 0x0346, 0x0347}, 0x0018, 0x000c}, {{0x0282, 0x0283, 0x0284, 0x0285, 0x0286}, 0x0018, 0x003e}, {{0x0291, 0x0292, 0x0293, 0x0294, 0x0294}, 0x0018, 0x002b}, {{0x0109, 0x0302, 0x0303, 0x0304, 0x0305}, 0x0000, 0x0003}, {{0x010a, 0x0306, 0x0307, 0x0308, 0x0309}, 0x0000, 0x000b}, {{0x010b, 0x030a, 0x030b, 0x030c, 0x030d}, 0x0000, 0x0002}, {{0x010c, 0x030e, 0x030f, 0x0310, 0x0311}, 0x0000, 0x000c}, {{0x010d, 0x0312, 0x0313, 0x0314, 0x0315}, 0x0000, 0x0000}, {{0x010e, 0x0316, 0x0317, 0x0318, 0x0319}, 0x0000, 0x0004}, {{0x010f, 0x031a, 0x031b, 0x031c, 0x031d}, 0x0000, 0x0006}, {{0x0110, 0x031e, 0x031f, 0x0320, 0x0321}, 0x0000, 0x0007}, {{0x0105, 0x0105, 0x0105, 0x0105, 0x0105}, 0x0000, 0x0008}, {{0x0106, 0x0106, 0x0106, 0x0106, 0x0106}, 0x0000, 0x0008}, {{0x0107, 0x0107, 0x0107, 0x0107, 0x0107}, 0x0000, 0x0008}, {{0x0108, 0x0108, 0x0108, 0x0108, 0x0108}, 0x0000, 0x0008}, {{0x014f, 0x014f, 0x014f, 0x014f, 0x014f}, 0x0000, 0x0008}, }; 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(); FreezeMapObjects(); sub_808BCF4(); gMain.savedCallback = CB2_EndWildBattle; gBattleTypeFlags = 0; if (InBattlePyramid()) { VarSet(VAR_0x400E, 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(); FreezeMapObjects(); 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(); FreezeMapObjects(); sub_808BCF4(); gMain.savedCallback = CB2_EndSafariBattle; gBattleTypeFlags = BATTLE_TYPE_SAFARI; CreateBattleStartTask(GetWildBattleTransition(), 0); } static void DoBattlePikeWildBattle(void) { ScriptContext2_Enable(); FreezeMapObjects(); 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 = c2_exit_to_overworld_1_continue_scripts_restart_music; 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, BGM_BATTLE34); break; case SPECIES_KYOGRE: gBattleTypeFlags |= BATTLE_TYPE_KYOGRE; CreateBattleStartTask(B_TRANSITION_KYOGRE, BGM_BATTLE34); break; case SPECIES_RAYQUAZA: gBattleTypeFlags |= BATTLE_TYPE_RAYQUAZA; CreateBattleStartTask(B_TRANSITION_RAYQUAZA, BGM_BATTLE_LEGENDARY); break; case SPECIES_DEOXYS: CreateBattleStartTask(B_TRANSITION_BLUR, BGM_FRLG_BATTLE_DEOXYS); break; case SPECIES_LUGIA: case SPECIES_HO_OH: CreateBattleStartTask(B_TRANSITION_BLUR, BGM_FRLG_BATTLE_LEGENDARY); break; case SPECIES_MEW: CreateBattleStartTask(B_TRANSITION_GRID_SQUARES, BGM_BATTLE_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, BGM_BATTLE34); // GROUDON else CreateBattleStartTask(B_TRANSITION_RIPPLE, BGM_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, BGM_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(c2_exit_to_overworld_2_switch); 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(c2_exit_to_overworld_1_continue_scripts_restart_music); else SetMainCallback2(CB2_WhiteOut); } else { SetMainCallback2(c2_exit_to_overworld_1_continue_scripts_restart_music); } } 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 PARTY_FLAG_CUSTOM_MOVES: { const struct TrainerMonNoItemCustomMoves *party; party = gTrainers[opponentId].party.NoItemCustomMoves; for (i = 0; i < count; i++) sum += party[i].lvl; } break; case PARTY_FLAG_HAS_ITEM: { const struct TrainerMonItemDefaultMoves *party; party = gTrainers[opponentId].party.ItemDefaultMoves; for (i = 0; i < count; i++) sum += party[i].lvl; } break; case PARTY_FLAG_CUSTOM_MOVES | PARTY_FLAG_HAS_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 == SECRET_BASE_OPPONENT) return B_TRANSITION_CHAMPION; if (gTrainers[gTrainerBattleOpponent_A].trainerClass == CLASS_ELITE_FOUR) { if (gTrainerBattleOpponent_A == TRAINER_ID_SIDNEY) return B_TRANSITION_SYDNEY; if (gTrainerBattleOpponent_A == TRAINER_ID_PHOEBE) return B_TRANSITION_PHOEBE; if (gTrainerBattleOpponent_A == TRAINER_ID_GLACIA) return B_TRANSITION_GLACIA; if (gTrainerBattleOpponent_A == TRAINER_ID_DRAKE) return B_TRANSITION_DRAKE; return B_TRANSITION_CHAMPION; } if (gTrainers[gTrainerBattleOpponent_A].trainerClass == CLASS_CHAMPION) return B_TRANSITION_CHAMPION; if (gTrainers[gTrainerBattleOpponent_A].trainerClass == CLASS_TEAM_MAGMA || gTrainers[gTrainerBattleOpponent_A].trainerClass == CLASS_MAGMA_LEADER || gTrainers[gTrainerBattleOpponent_A].trainerClass == CLASS_MAGMA_ADMIN) return B_TRANSITION_MAGMA; if (gTrainers[gTrainerBattleOpponent_A].trainerClass == CLASS_TEAM_AQUA || gTrainers[gTrainerBattleOpponent_A].trainerClass == CLASS_AQUA_LEADER || gTrainers[gTrainerBattleOpponent_A].trainerClass == 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->field_CB4[gSaveBlock2Ptr->battlePyramidWildHeaderId * 2 + 0] + gSaveBlock2Ptr->field_CB4[gSaveBlock2Ptr->battlePyramidWildHeaderId * 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(c2_exit_to_overworld_1_continue_scripts_restart_music); } static void sub_80B1218(void) { if (GetGameStat(GAME_STAT_WILD_BATTLES) % 60 == 0) sub_81DA57C(); } static void sub_80B1234(void) { if (GetGameStat(GAME_STAT_TRAINER_BATTLES) % 20 == 0) sub_81DA57C(); } // 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 BATTLE_LOST: case BATTLE_DREW: return TRUE; case BATTLE_WON: case BATTLE_RAN: case BATTLE_PLAYER_TELEPORTED: case BATTLE_POKE_FLED: case BATTLE_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; } sTrainerMapObjectLocalId = 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 (sTrainerMapObjectLocalId != 0) { gSpecialVar_LastTalked = sTrainerMapObjectLocalId; gSelectedMapObject = GetFieldObjectIdByLocalIdAndMap(sTrainerMapObjectLocalId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); } } const u8 *BattleSetup_ConfigureTrainerBattle(const u8 *data) { InitTrainerBattleVariables(); sTrainerBattleMode = TrainerBattleLoadArg8(data); switch (sTrainerBattleMode) { case 3: TrainerBattleLoadArgs(sOrdinaryNoIntroBattleParams, data); return EventScript_2713C2; case 4: TrainerBattleLoadArgs(sDoubleBattleParams, data); SetMapVarsToTrainer(); return EventScript_TryDoDoubleTrainerBattle; case 2: if (gApproachingTrainerId == 0) { TrainerBattleLoadArgs(sContinueScriptBattleParams, data); SetMapVarsToTrainer(); } else { TrainerBattleLoadArgs(sTrainerBContinueScriptBattleParams, data); } return EventScript_271362; case 1: TrainerBattleLoadArgs(sContinueScriptBattleParams, data); SetMapVarsToTrainer(); return EventScript_271362; case 6: case 8: TrainerBattleLoadArgs(sContinueScriptDoubleBattleParams, data); SetMapVarsToTrainer(); return EventScript_TryDoDoubleTrainerBattle; case 7: TrainerBattleLoadArgs(sDoubleBattleParams, data); SetMapVarsToTrainer(); gTrainerBattleOpponent_A = GetRematchTrainerId(gTrainerBattleOpponent_A); return EventScript_TryDoDoubleRematchBattle; case 5: TrainerBattleLoadArgs(sOrdinaryBattleParams, data); SetMapVarsToTrainer(); gTrainerBattleOpponent_A = GetRematchTrainerId(gTrainerBattleOpponent_A); return EventScript_2713D1; case 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 10: TrainerBattleLoadArgs(sOrdinaryBattleParams, data); return NULL; case 11: TrainerBattleLoadArgs(sTrainerBOrdinaryBattleParams, data); return NULL; case 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 trainerMapObjId, const u8 *trainerScript) { gSelectedMapObject = trainerMapObjId; gSpecialVar_LastTalked = gMapObjects[trainerMapObjId].localId; BattleSetup_ConfigureTrainerBattle(trainerScript + 1); ScriptContext1_SetupScript(EventScript_271354); ScriptContext2_Enable(); } void ConfigureTwoTrainersBattle(u8 trainerMapObjId, const u8 *trainerScript) { gSelectedMapObject = trainerMapObjId; gSpecialVar_LastTalked = gMapObjects[trainerMapObjId].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 MapObject *mapObject = &gMapObjects[gSelectedMapObject]; npc_set_running_behaviour_etc(mapObject, npc_running_behaviour_by_direction(mapObject->mapobj_unk_18)); } u8 GetTrainerBattleMode(void) { return sTrainerBattleMode; } bool8 GetTrainerFlag(void) { if (InBattlePyramid()) return GetBattlePyramidTrainerFlag(gSelectedMapObject); else if (InTrainerHill()) return GetTrainerHillTrainerFlag(gSelectedMapObject); 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_0x400E, 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 == SECRET_BASE_OPPONENT) { SetMainCallback2(c2_exit_to_overworld_1_continue_scripts_restart_music); } else if (IsPlayerDefeated(gBattleOutcome) == TRUE) { if (InBattlePyramid() || sub_81D5C18()) SetMainCallback2(c2_exit_to_overworld_1_continue_scripts_restart_music); else SetMainCallback2(CB2_WhiteOut); } else { SetMainCallback2(c2_exit_to_overworld_1_continue_scripts_restart_music); if (!InBattlePyramid() && !sub_81D5C18()) { RegisterTrainerInMatchCall(); SetBattledTrainersFlags(); } } } static void CB2_EndRematchBattle(void) { if (gTrainerBattleOpponent_A == SECRET_BASE_OPPONENT) { SetMainCallback2(c2_exit_to_overworld_1_continue_scripts_restart_music); } else if (IsPlayerDefeated(gBattleOutcome) == TRUE) { SetMainCallback2(CB2_WhiteOut); } else { SetMainCallback2(c2_exit_to_overworld_1_continue_scripts_restart_music); 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(gMapObjects[gApproachingTrainers[gApproachingTrainerId].mapObjectId].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(gMapObjects[gApproachingTrainers[gApproachingTrainerId].mapObjectId].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 = BGM_BOYEYE; break; case TRAINER_ENCOUNTER_MUSIC_FEMALE: music = BGM_GIRLEYE; break; case TRAINER_ENCOUNTER_MUSIC_GIRL: music = BGM_SYOUJOEYE; break; case TRAINER_ENCOUNTER_MUSIC_INTENSE: music = BGM_HAGESHII; break; case TRAINER_ENCOUNTER_MUSIC_COOL: music = BGM_KAKKOII; break; case TRAINER_ENCOUNTER_MUSIC_AQUA: music = BGM_AQA_0; break; case TRAINER_ENCOUNTER_MUSIC_MAGMA: music = BGM_MGM0; break; case TRAINER_ENCOUNTER_MUSIC_SWIMMER: music = BGM_SWIMEYE; break; case TRAINER_ENCOUNTER_MUSIC_TWINS: music = BGM_HUTAGO; break; case TRAINER_ENCOUNTER_MUSIC_ELITE_FOUR: music = BGM_SITENNOU; break; case TRAINER_ENCOUNTER_MUSIC_HIKER: music = BGM_YAMA_EYE; break; case TRAINER_ENCOUNTER_MUSIC_INTERVIEWER: music = BGM_INTER_V; break; case TRAINER_ENCOUNTER_MUSIC_RICH: music = BGM_TEST; break; default: music = BGM_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 == SECRET_BASE_OPPONENT) 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; }