#include "global.h" #include "battle.h" #include "load_save.h" #include "battle_setup.h" #include "battle_tower.h" #include "battle_transition.h" #include "main.h" #include "task.h" #include "safari_zone.h" #include "script.h" #include "event_data.h" #include "metatile_behavior.h" #include "field_player_avatar.h" #include "fieldmap.h" #include "follower_npc.h" #include "random.h" #include "starter_choose.h" #include "script_pokemon_util.h" #include "palette.h" #include "window.h" #include "event_object_movement.h" #include "event_scripts.h" #include "tv.h" #include "trainer_see.h" #include "field_message_box.h" #include "sound.h" #include "strings.h" #include "trainer_hill.h" #include "secret_base.h" #include "string_util.h" #include "overworld.h" #include "field_weather.h" #include "battle_tower.h" #include "gym_leader_rematch.h" #include "battle_pike.h" #include "battle_pyramid.h" #include "fldeff.h" #include "fldeff_misc.h" #include "field_control_avatar.h" #include "mirage_tower.h" #include "field_screen_effect.h" #include "data.h" #include "vs_seeker.h" #include "item.h" #include "field_name_box.h" #include "constants/battle_frontier.h" #include "constants/battle_setup.h" #include "constants/event_objects.h" #include "constants/game_stat.h" #include "constants/items.h" #include "constants/songs.h" #include "constants/trainers.h" #include "constants/trainer_hill.h" #include "constants/weather.h" #include "fishing.h" enum TransitionType { TRANSITION_TYPE_NORMAL, TRANSITION_TYPE_CAVE, TRANSITION_TYPE_FLASH, TRANSITION_TYPE_WATER, }; // this file's functions static void DoBattlePikeWildBattle(void); static void DoSafariBattle(void); static void DoStandardWildBattle(bool32 isDouble); static void CB2_EndWildBattle(void); static void CB2_EndScriptedWildBattle(void); static void TryUpdateGymLeaderRematchFromWild(void); static void TryUpdateGymLeaderRematchFromTrainer(void); static void CB2_GiveStarter(void); static void CB2_StartFirstBattle(void); static void CB2_EndFirstBattle(void); static void SaveChangesToPlayerParty(void); static void HandleBattleVariantEndParty(void); static void CB2_EndTrainerBattle(void); static bool32 IsPlayerDefeated(u32 battleOutcome); #if FREE_MATCH_CALL == FALSE static u16 GetRematchTrainerId(u16 trainerId); #endif //FREE_MATCH_CALL static void RegisterTrainerInMatchCall(void); static void HandleRematchVarsOnBattleEnd(void); static const u8 *GetIntroSpeechOfApproachingTrainer(void); static const u8 *GetTrainerCantBattleSpeech(void); EWRAM_DATA TrainerBattleParameter gTrainerBattleParameter = {0}; EWRAM_DATA u16 gPartnerTrainerId = 0; EWRAM_DATA static u8 *sTrainerBattleEndScript = NULL; EWRAM_DATA static bool8 sShouldCheckTrainerBScript = FALSE; EWRAM_DATA static u8 sNoOfPossibleTrainerRetScripts = 0; // The first transition is used if the enemy Pokémon are lower level than our Pokémon. // Otherwise, the second transition is used. static const u8 sBattleTransitionTable_Wild[][2] = { [TRANSITION_TYPE_NORMAL] = {B_TRANSITION_SLICE, B_TRANSITION_WHITE_BARS_FADE}, [TRANSITION_TYPE_CAVE] = {B_TRANSITION_CLOCKWISE_WIPE, B_TRANSITION_GRID_SQUARES}, [TRANSITION_TYPE_FLASH] = {B_TRANSITION_BLUR, B_TRANSITION_GRID_SQUARES}, [TRANSITION_TYPE_WATER] = {B_TRANSITION_WAVE, B_TRANSITION_RIPPLE}, }; static const u8 sBattleTransitionTable_Trainer[][2] = { [TRANSITION_TYPE_NORMAL] = {B_TRANSITION_POKEBALLS_TRAIL, B_TRANSITION_ANGLED_WIPES}, [TRANSITION_TYPE_CAVE] = {B_TRANSITION_SHUFFLE, B_TRANSITION_BIG_POKEBALL}, [TRANSITION_TYPE_FLASH] = {B_TRANSITION_BLUR, B_TRANSITION_GRID_SQUARES}, [TRANSITION_TYPE_WATER] = {B_TRANSITION_SWIRL, B_TRANSITION_RIPPLE}, }; // Battle Frontier (excluding Pyramid and Dome, which have their own tables below) static const u8 sBattleTransitionTable_BattleFrontier[] = { B_TRANSITION_FRONTIER_LOGO_WIGGLE, B_TRANSITION_FRONTIER_LOGO_WAVE, B_TRANSITION_FRONTIER_SQUARES, B_TRANSITION_FRONTIER_SQUARES_SCROLL, B_TRANSITION_FRONTIER_CIRCLES_MEET, B_TRANSITION_FRONTIER_CIRCLES_CROSS, B_TRANSITION_FRONTIER_CIRCLES_ASYMMETRIC_SPIRAL, B_TRANSITION_FRONTIER_CIRCLES_SYMMETRIC_SPIRAL, B_TRANSITION_FRONTIER_CIRCLES_MEET_IN_SEQ, B_TRANSITION_FRONTIER_CIRCLES_CROSS_IN_SEQ, B_TRANSITION_FRONTIER_CIRCLES_ASYMMETRIC_SPIRAL_IN_SEQ, B_TRANSITION_FRONTIER_CIRCLES_SYMMETRIC_SPIRAL_IN_SEQ }; static const u8 sBattleTransitionTable_BattlePyramid[] = { B_TRANSITION_FRONTIER_SQUARES, B_TRANSITION_FRONTIER_SQUARES_SCROLL, B_TRANSITION_FRONTIER_SQUARES_SPIRAL }; static const u8 sBattleTransitionTable_BattleDome[] = { B_TRANSITION_FRONTIER_LOGO_WIGGLE, B_TRANSITION_FRONTIER_SQUARES, B_TRANSITION_FRONTIER_SQUARES_SCROLL, B_TRANSITION_FRONTIER_SQUARES_SPIRAL }; #define REMATCH(trainer1, trainer2, trainer3, trainer4, trainer5, map) \ { \ .trainerIds = {trainer1, trainer2, trainer3, trainer4, trainer5}, \ .mapGroup = MAP_GROUP(map), \ .mapNum = MAP_NUM(map), \ } const struct RematchTrainer gRematchTable[REMATCH_TABLE_ENTRIES] = { [REMATCH_ROSE] = REMATCH(TRAINER_ROSE_1, TRAINER_ROSE_2, TRAINER_ROSE_3, TRAINER_ROSE_4, TRAINER_ROSE_5, MAP_ROUTE118), [REMATCH_ANDRES] = REMATCH(TRAINER_ANDRES_1, TRAINER_ANDRES_2, TRAINER_ANDRES_3, TRAINER_ANDRES_4, TRAINER_ANDRES_5, MAP_ROUTE105), [REMATCH_DUSTY] = REMATCH(TRAINER_DUSTY_1, TRAINER_DUSTY_2, TRAINER_DUSTY_3, TRAINER_DUSTY_4, TRAINER_DUSTY_5, MAP_ROUTE111), [REMATCH_LOLA] = REMATCH(TRAINER_LOLA_1, TRAINER_LOLA_2, TRAINER_LOLA_3, TRAINER_LOLA_4, TRAINER_LOLA_5, MAP_ROUTE109), [REMATCH_RICKY] = REMATCH(TRAINER_RICKY_1, TRAINER_RICKY_2, TRAINER_RICKY_3, TRAINER_RICKY_4, TRAINER_RICKY_5, MAP_ROUTE109), [REMATCH_LILA_AND_ROY] = REMATCH(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, MAP_ROUTE124), [REMATCH_CRISTIN] = REMATCH(TRAINER_CRISTIN_1, TRAINER_CRISTIN_2, TRAINER_CRISTIN_3, TRAINER_CRISTIN_4, TRAINER_CRISTIN_5, MAP_ROUTE121), [REMATCH_BROOKE] = REMATCH(TRAINER_BROOKE_1, TRAINER_BROOKE_2, TRAINER_BROOKE_3, TRAINER_BROOKE_4, TRAINER_BROOKE_5, MAP_ROUTE111), [REMATCH_WILTON] = REMATCH(TRAINER_WILTON_1, TRAINER_WILTON_2, TRAINER_WILTON_3, TRAINER_WILTON_4, TRAINER_WILTON_5, MAP_ROUTE111), [REMATCH_VALERIE] = REMATCH(TRAINER_VALERIE_1, TRAINER_VALERIE_2, TRAINER_VALERIE_3, TRAINER_VALERIE_4, TRAINER_VALERIE_5, MAP_MT_PYRE_6F), [REMATCH_CINDY] = REMATCH(TRAINER_CINDY_1, TRAINER_CINDY_3, TRAINER_CINDY_4, TRAINER_CINDY_5, TRAINER_CINDY_6, MAP_ROUTE104), [REMATCH_THALIA] = REMATCH(TRAINER_THALIA_1, TRAINER_THALIA_2, TRAINER_THALIA_3, TRAINER_THALIA_4, TRAINER_THALIA_5, MAP_ABANDONED_SHIP_ROOMS_1F), [REMATCH_JESSICA] = REMATCH(TRAINER_JESSICA_1, TRAINER_JESSICA_2, TRAINER_JESSICA_3, TRAINER_JESSICA_4, TRAINER_JESSICA_5, MAP_ROUTE121), [REMATCH_WINSTON] = REMATCH(TRAINER_WINSTON_1, TRAINER_WINSTON_2, TRAINER_WINSTON_3, TRAINER_WINSTON_4, TRAINER_WINSTON_5, MAP_ROUTE104), [REMATCH_STEVE] = REMATCH(TRAINER_STEVE_1, TRAINER_STEVE_2, TRAINER_STEVE_3, TRAINER_STEVE_4, TRAINER_STEVE_5, MAP_ROUTE114), [REMATCH_TONY] = REMATCH(TRAINER_TONY_1, TRAINER_TONY_2, TRAINER_TONY_3, TRAINER_TONY_4, TRAINER_TONY_5, MAP_ROUTE107), [REMATCH_NOB] = REMATCH(TRAINER_NOB_1, TRAINER_NOB_2, TRAINER_NOB_3, TRAINER_NOB_4, TRAINER_NOB_5, MAP_ROUTE115), [REMATCH_KOJI] = REMATCH(TRAINER_KOJI_1, TRAINER_KOJI_2, TRAINER_KOJI_3, TRAINER_KOJI_4, TRAINER_KOJI_5, MAP_ROUTE127), [REMATCH_FERNANDO] = REMATCH(TRAINER_FERNANDO_1, TRAINER_FERNANDO_2, TRAINER_FERNANDO_3, TRAINER_FERNANDO_4, TRAINER_FERNANDO_5, MAP_ROUTE123), [REMATCH_DALTON] = REMATCH(TRAINER_DALTON_1, TRAINER_DALTON_2, TRAINER_DALTON_3, TRAINER_DALTON_4, TRAINER_DALTON_5, MAP_ROUTE118), [REMATCH_BERNIE] = REMATCH(TRAINER_BERNIE_1, TRAINER_BERNIE_2, TRAINER_BERNIE_3, TRAINER_BERNIE_4, TRAINER_BERNIE_5, MAP_ROUTE114), [REMATCH_ETHAN] = REMATCH(TRAINER_ETHAN_1, TRAINER_ETHAN_2, TRAINER_ETHAN_3, TRAINER_ETHAN_4, TRAINER_ETHAN_5, MAP_JAGGED_PASS), [REMATCH_JOHN_AND_JAY] = REMATCH(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, MAP_METEOR_FALLS_1F_2R), [REMATCH_JEFFREY] = REMATCH(TRAINER_JEFFREY_1, TRAINER_JEFFREY_2, TRAINER_JEFFREY_3, TRAINER_JEFFREY_4, TRAINER_JEFFREY_5, MAP_ROUTE120), [REMATCH_CAMERON] = REMATCH(TRAINER_CAMERON_1, TRAINER_CAMERON_2, TRAINER_CAMERON_3, TRAINER_CAMERON_4, TRAINER_CAMERON_5, MAP_ROUTE123), [REMATCH_JACKI] = REMATCH(TRAINER_JACKI_1, TRAINER_JACKI_2, TRAINER_JACKI_3, TRAINER_JACKI_4, TRAINER_JACKI_5, MAP_ROUTE123), [REMATCH_WALTER] = REMATCH(TRAINER_WALTER_1, TRAINER_WALTER_2, TRAINER_WALTER_3, TRAINER_WALTER_4, TRAINER_WALTER_5, MAP_ROUTE121), [REMATCH_KAREN] = REMATCH(TRAINER_KAREN_1, TRAINER_KAREN_2, TRAINER_KAREN_3, TRAINER_KAREN_4, TRAINER_KAREN_5, MAP_ROUTE116), [REMATCH_JERRY] = REMATCH(TRAINER_JERRY_1, TRAINER_JERRY_2, TRAINER_JERRY_3, TRAINER_JERRY_4, TRAINER_JERRY_5, MAP_ROUTE116), [REMATCH_ANNA_AND_MEG] = REMATCH(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, MAP_ROUTE117), [REMATCH_ISABEL] = REMATCH(TRAINER_ISABEL_1, TRAINER_ISABEL_2, TRAINER_ISABEL_3, TRAINER_ISABEL_4, TRAINER_ISABEL_5, MAP_ROUTE110), [REMATCH_MIGUEL] = REMATCH(TRAINER_MIGUEL_1, TRAINER_MIGUEL_2, TRAINER_MIGUEL_3, TRAINER_MIGUEL_4, TRAINER_MIGUEL_5, MAP_ROUTE103), [REMATCH_TIMOTHY] = REMATCH(TRAINER_TIMOTHY_1, TRAINER_TIMOTHY_2, TRAINER_TIMOTHY_3, TRAINER_TIMOTHY_4, TRAINER_TIMOTHY_5, MAP_ROUTE115), [REMATCH_SHELBY] = REMATCH(TRAINER_SHELBY_1, TRAINER_SHELBY_2, TRAINER_SHELBY_3, TRAINER_SHELBY_4, TRAINER_SHELBY_5, MAP_MT_CHIMNEY), [REMATCH_CALVIN] = REMATCH(TRAINER_CALVIN_1, TRAINER_CALVIN_2, TRAINER_CALVIN_3, TRAINER_CALVIN_4, TRAINER_CALVIN_5, MAP_ROUTE102), [REMATCH_ELLIOT] = REMATCH(TRAINER_ELLIOT_1, TRAINER_ELLIOT_2, TRAINER_ELLIOT_3, TRAINER_ELLIOT_4, TRAINER_ELLIOT_5, MAP_ROUTE106), [REMATCH_ISAIAH] = REMATCH(TRAINER_ISAIAH_1, TRAINER_ISAIAH_2, TRAINER_ISAIAH_3, TRAINER_ISAIAH_4, TRAINER_ISAIAH_5, MAP_ROUTE128), [REMATCH_MARIA] = REMATCH(TRAINER_MARIA_1, TRAINER_MARIA_2, TRAINER_MARIA_3, TRAINER_MARIA_4, TRAINER_MARIA_5, MAP_ROUTE117), [REMATCH_ABIGAIL] = REMATCH(TRAINER_ABIGAIL_1, TRAINER_ABIGAIL_2, TRAINER_ABIGAIL_3, TRAINER_ABIGAIL_4, TRAINER_ABIGAIL_5, MAP_ROUTE110), [REMATCH_DYLAN] = REMATCH(TRAINER_DYLAN_1, TRAINER_DYLAN_2, TRAINER_DYLAN_3, TRAINER_DYLAN_4, TRAINER_DYLAN_5, MAP_ROUTE117), [REMATCH_KATELYN] = REMATCH(TRAINER_KATELYN_1, TRAINER_KATELYN_2, TRAINER_KATELYN_3, TRAINER_KATELYN_4, TRAINER_KATELYN_5, MAP_ROUTE128), [REMATCH_BENJAMIN] = REMATCH(TRAINER_BENJAMIN_1, TRAINER_BENJAMIN_2, TRAINER_BENJAMIN_3, TRAINER_BENJAMIN_4, TRAINER_BENJAMIN_5, MAP_ROUTE110), [REMATCH_PABLO] = REMATCH(TRAINER_PABLO_1, TRAINER_PABLO_2, TRAINER_PABLO_3, TRAINER_PABLO_4, TRAINER_PABLO_5, MAP_ROUTE126), [REMATCH_NICOLAS] = REMATCH(TRAINER_NICOLAS_1, TRAINER_NICOLAS_2, TRAINER_NICOLAS_3, TRAINER_NICOLAS_4, TRAINER_NICOLAS_5, MAP_METEOR_FALLS_1F_2R), [REMATCH_ROBERT] = REMATCH(TRAINER_ROBERT_1, TRAINER_ROBERT_2, TRAINER_ROBERT_3, TRAINER_ROBERT_4, TRAINER_ROBERT_5, MAP_ROUTE120), [REMATCH_LAO] = REMATCH(TRAINER_LAO_1, TRAINER_LAO_2, TRAINER_LAO_3, TRAINER_LAO_4, TRAINER_LAO_5, MAP_ROUTE113), [REMATCH_CYNDY] = REMATCH(TRAINER_CYNDY_1, TRAINER_CYNDY_2, TRAINER_CYNDY_3, TRAINER_CYNDY_4, TRAINER_CYNDY_5, MAP_ROUTE115), [REMATCH_MADELINE] = REMATCH(TRAINER_MADELINE_1, TRAINER_MADELINE_2, TRAINER_MADELINE_3, TRAINER_MADELINE_4, TRAINER_MADELINE_5, MAP_ROUTE113), [REMATCH_JENNY] = REMATCH(TRAINER_JENNY_1, TRAINER_JENNY_2, TRAINER_JENNY_3, TRAINER_JENNY_4, TRAINER_JENNY_5, MAP_ROUTE124), [REMATCH_DIANA] = REMATCH(TRAINER_DIANA_1, TRAINER_DIANA_2, TRAINER_DIANA_3, TRAINER_DIANA_4, TRAINER_DIANA_5, MAP_JAGGED_PASS), [REMATCH_AMY_AND_LIV] = REMATCH(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, MAP_ROUTE103), [REMATCH_ERNEST] = REMATCH(TRAINER_ERNEST_1, TRAINER_ERNEST_2, TRAINER_ERNEST_3, TRAINER_ERNEST_4, TRAINER_ERNEST_5, MAP_ROUTE125), [REMATCH_CORY] = REMATCH(TRAINER_CORY_1, TRAINER_CORY_2, TRAINER_CORY_3, TRAINER_CORY_4, TRAINER_CORY_5, MAP_ROUTE108), [REMATCH_EDWIN] = REMATCH(TRAINER_EDWIN_1, TRAINER_EDWIN_2, TRAINER_EDWIN_3, TRAINER_EDWIN_4, TRAINER_EDWIN_5, MAP_ROUTE110), [REMATCH_LYDIA] = REMATCH(TRAINER_LYDIA_1, TRAINER_LYDIA_2, TRAINER_LYDIA_3, TRAINER_LYDIA_4, TRAINER_LYDIA_5, MAP_ROUTE117), [REMATCH_ISAAC] = REMATCH(TRAINER_ISAAC_1, TRAINER_ISAAC_2, TRAINER_ISAAC_3, TRAINER_ISAAC_4, TRAINER_ISAAC_5, MAP_ROUTE117), [REMATCH_GABRIELLE] = REMATCH(TRAINER_GABRIELLE_1, TRAINER_GABRIELLE_2, TRAINER_GABRIELLE_3, TRAINER_GABRIELLE_4, TRAINER_GABRIELLE_5, MAP_MT_PYRE_3F), [REMATCH_CATHERINE] = REMATCH(TRAINER_CATHERINE_1, TRAINER_CATHERINE_2, TRAINER_CATHERINE_3, TRAINER_CATHERINE_4, TRAINER_CATHERINE_5, MAP_ROUTE119), [REMATCH_JACKSON] = REMATCH(TRAINER_JACKSON_1, TRAINER_JACKSON_2, TRAINER_JACKSON_3, TRAINER_JACKSON_4, TRAINER_JACKSON_5, MAP_ROUTE119), [REMATCH_HALEY] = REMATCH(TRAINER_HALEY_1, TRAINER_HALEY_2, TRAINER_HALEY_3, TRAINER_HALEY_4, TRAINER_HALEY_5, MAP_ROUTE104), [REMATCH_JAMES] = REMATCH(TRAINER_JAMES_1, TRAINER_JAMES_2, TRAINER_JAMES_3, TRAINER_JAMES_4, TRAINER_JAMES_5, MAP_PETALBURG_WOODS), [REMATCH_TRENT] = REMATCH(TRAINER_TRENT_1, TRAINER_TRENT_2, TRAINER_TRENT_3, TRAINER_TRENT_4, TRAINER_TRENT_5, MAP_ROUTE112), [REMATCH_SAWYER] = REMATCH(TRAINER_SAWYER_1, TRAINER_SAWYER_2, TRAINER_SAWYER_3, TRAINER_SAWYER_4, TRAINER_SAWYER_5, MAP_MT_CHIMNEY), [REMATCH_KIRA_AND_DAN] = REMATCH(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, MAP_ABANDONED_SHIP_ROOMS2_1F), [REMATCH_WALLY_VR] = REMATCH(TRAINER_WALLY_VR_2, TRAINER_WALLY_VR_3, TRAINER_WALLY_VR_4, TRAINER_WALLY_VR_5, TRAINER_WALLY_VR_5, MAP_VICTORY_ROAD_1F), [REMATCH_ROXANNE] = REMATCH(TRAINER_ROXANNE_1, TRAINER_ROXANNE_2, TRAINER_ROXANNE_3, TRAINER_ROXANNE_4, TRAINER_ROXANNE_5, MAP_RUSTBORO_CITY), [REMATCH_BRAWLY] = REMATCH(TRAINER_BRAWLY_1, TRAINER_BRAWLY_2, TRAINER_BRAWLY_3, TRAINER_BRAWLY_4, TRAINER_BRAWLY_5, MAP_DEWFORD_TOWN), [REMATCH_WATTSON] = REMATCH(TRAINER_WATTSON_1, TRAINER_WATTSON_2, TRAINER_WATTSON_3, TRAINER_WATTSON_4, TRAINER_WATTSON_5, MAP_MAUVILLE_CITY), [REMATCH_FLANNERY] = REMATCH(TRAINER_FLANNERY_1, TRAINER_FLANNERY_2, TRAINER_FLANNERY_3, TRAINER_FLANNERY_4, TRAINER_FLANNERY_5, MAP_LAVARIDGE_TOWN), [REMATCH_NORMAN] = REMATCH(TRAINER_NORMAN_1, TRAINER_NORMAN_2, TRAINER_NORMAN_3, TRAINER_NORMAN_4, TRAINER_NORMAN_5, MAP_PETALBURG_CITY), [REMATCH_WINONA] = REMATCH(TRAINER_WINONA_1, TRAINER_WINONA_2, TRAINER_WINONA_3, TRAINER_WINONA_4, TRAINER_WINONA_5, MAP_FORTREE_CITY), [REMATCH_TATE_AND_LIZA] = REMATCH(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, MAP_MOSSDEEP_CITY), [REMATCH_JUAN] = REMATCH(TRAINER_JUAN_1, TRAINER_JUAN_2, TRAINER_JUAN_3, TRAINER_JUAN_4, TRAINER_JUAN_5, MAP_SOOTOPOLIS_CITY), [REMATCH_SIDNEY] = REMATCH(TRAINER_SIDNEY, TRAINER_SIDNEY, TRAINER_SIDNEY, TRAINER_SIDNEY, TRAINER_SIDNEY, MAP_EVER_GRANDE_CITY), [REMATCH_PHOEBE] = REMATCH(TRAINER_PHOEBE, TRAINER_PHOEBE, TRAINER_PHOEBE, TRAINER_PHOEBE, TRAINER_PHOEBE, MAP_EVER_GRANDE_CITY), [REMATCH_GLACIA] = REMATCH(TRAINER_GLACIA, TRAINER_GLACIA, TRAINER_GLACIA, TRAINER_GLACIA, TRAINER_GLACIA, MAP_EVER_GRANDE_CITY), [REMATCH_DRAKE] = REMATCH(TRAINER_DRAKE, TRAINER_DRAKE, TRAINER_DRAKE, TRAINER_DRAKE, TRAINER_DRAKE, MAP_EVER_GRANDE_CITY), [REMATCH_WALLACE] = REMATCH(TRAINER_WALLACE, TRAINER_WALLACE, TRAINER_WALLACE, TRAINER_WALLACE, TRAINER_WALLACE, MAP_EVER_GRANDE_CITY), }; #define tState data[0] #define tTransition data[1] static void Task_BattleStart(u8 taskId) { s16 *data = gTasks[taskId].data; switch (tState) { case 0: if (!FldEffPoison_IsActive()) // is poison not active? { BattleTransition_StartOnField(tTransition); ClearMirageTowerPulseBlendEffect(); tState++; // go to case 1. } break; case 1: if (IsBattleTransitionDone() == TRUE) { PrepareForFollowerNPCBattle(); CleanupOverworldWindowsAndTilemaps(); SetMainCallback2(CB2_InitBattle); RestartWildEncounterImmunitySteps(); ClearPoisonStepCounter(); DestroyTask(taskId); } break; } } static void CreateBattleStartTask(enum BattleTransition transition, u16 song) { u8 taskId = CreateTask(Task_BattleStart, 1); gTasks[taskId].tTransition = transition; PlayMapChosenOrBattleBGM(song); } static void Task_BattleStart_Debug(u8 taskId) { s16 *data = gTasks[taskId].data; switch (tState) { case 0: if (!FldEffPoison_IsActive()) // is poison not active? { BattleTransition_StartOnField(tTransition); ClearMirageTowerPulseBlendEffect(); tState++; // go to case 1. } break; case 1: if (IsBattleTransitionDone() == TRUE) { CleanupOverworldWindowsAndTilemaps(); SetMainCallback2(CB2_InitBattle); RestartWildEncounterImmunitySteps(); ClearPoisonStepCounter(); DestroyTask(taskId); } break; } } static void CreateBattleStartTask_Debug(u8 transition, u16 song) { u8 taskId = CreateTask(Task_BattleStart_Debug, 1); gTasks[taskId].tTransition = transition; PlayMapChosenOrBattleBGM(song); } #undef tState #undef tTransition void BattleSetup_StartWildBattle(void) { if (GetSafariZoneFlag()) DoSafariBattle(); else DoStandardWildBattle(FALSE); } void BattleSetup_StartDoubleWildBattle(void) { DoStandardWildBattle(TRUE); } void BattleSetup_StartBattlePikeWildBattle(void) { DoBattlePikeWildBattle(); } static void DoStandardWildBattle(bool32 isDouble) { LockPlayerFieldControls(); FreezeObjectEvents(); StopPlayerAvatar(); gMain.savedCallback = CB2_EndWildBattle; gBattleTypeFlags = 0; if (IsNPCFollowerWildBattle()) { gBattleTypeFlags |= BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_DOUBLE; } else if (isDouble) gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) { VarSet(VAR_TEMP_E, 0); gBattleTypeFlags |= BATTLE_TYPE_PYRAMID; } CreateBattleStartTask(GetWildBattleTransition(), 0); IncrementGameStat(GAME_STAT_TOTAL_BATTLES); IncrementGameStat(GAME_STAT_WILD_BATTLES); IncrementDailyWildBattles(); TryUpdateGymLeaderRematchFromWild(); } void DoStandardWildBattle_Debug(void) { LockPlayerFieldControls(); FreezeObjectEvents(); StopPlayerAvatar(); gMain.savedCallback = CB2_EndWildBattle; gBattleTypeFlags = 0; if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) { VarSet(VAR_TEMP_PLAYING_PYRAMID_MUSIC, 0); gBattleTypeFlags |= BATTLE_TYPE_PYRAMID; } CreateBattleStartTask_Debug(GetWildBattleTransition(), 0); //IncrementGameStat(GAME_STAT_TOTAL_BATTLES); //IncrementGameStat(GAME_STAT_WILD_BATTLES); //IncrementDailyWildBattles(); //TryUpdateGymLeaderRematchFromWild(); } void BattleSetup_StartRoamerBattle(void) { LockPlayerFieldControls(); FreezeObjectEvents(); StopPlayerAvatar(); gMain.savedCallback = CB2_EndWildBattle; gBattleTypeFlags = BATTLE_TYPE_ROAMER; CreateBattleStartTask(GetWildBattleTransition(), 0); IncrementGameStat(GAME_STAT_TOTAL_BATTLES); IncrementGameStat(GAME_STAT_WILD_BATTLES); IncrementDailyWildBattles(); TryUpdateGymLeaderRematchFromWild(); } static void DoSafariBattle(void) { LockPlayerFieldControls(); FreezeObjectEvents(); StopPlayerAvatar(); gMain.savedCallback = CB2_EndSafariBattle; gBattleTypeFlags = BATTLE_TYPE_SAFARI; CreateBattleStartTask(GetWildBattleTransition(), 0); } static void DoBattlePikeWildBattle(void) { LockPlayerFieldControls(); FreezeObjectEvents(); StopPlayerAvatar(); gMain.savedCallback = CB2_EndWildBattle; gBattleTypeFlags = BATTLE_TYPE_PIKE; CreateBattleStartTask(GetWildBattleTransition(), 0); IncrementGameStat(GAME_STAT_TOTAL_BATTLES); IncrementGameStat(GAME_STAT_WILD_BATTLES); IncrementDailyWildBattles(); TryUpdateGymLeaderRematchFromWild(); } static void DoTrainerBattle(void) { CreateBattleStartTask(GetTrainerBattleTransition(), 0); IncrementGameStat(GAME_STAT_TOTAL_BATTLES); IncrementGameStat(GAME_STAT_TRAINER_BATTLES); TryUpdateGymLeaderRematchFromTrainer(); } static void DoBattlePyramidTrainerHillBattle(void) { if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) CreateBattleStartTask(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_PYRAMID), 0); else CreateBattleStartTask(GetSpecialBattleTransition(B_TRANSITION_GROUP_TRAINER_HILL), 0); IncrementGameStat(GAME_STAT_TOTAL_BATTLES); IncrementGameStat(GAME_STAT_TRAINER_BATTLES); TryUpdateGymLeaderRematchFromTrainer(); } // Initiates battle where Wally catches Ralts void StartWallyTutorialBattle(void) { CreateMaleMon(&gEnemyParty[0], SPECIES_RALTS, 5); LockPlayerFieldControls(); gMain.savedCallback = CB2_ReturnToFieldContinueScriptPlayMapMusic; gBattleTypeFlags = BATTLE_TYPE_WALLY_TUTORIAL; CreateBattleStartTask(B_TRANSITION_SLICE, 0); } void BattleSetup_StartScriptedWildBattle(void) { LockPlayerFieldControls(); gMain.savedCallback = CB2_EndScriptedWildBattle; gBattleTypeFlags = 0; CreateBattleStartTask(GetWildBattleTransition(), 0); IncrementGameStat(GAME_STAT_TOTAL_BATTLES); IncrementGameStat(GAME_STAT_WILD_BATTLES); IncrementDailyWildBattles(); TryUpdateGymLeaderRematchFromWild(); } void BattleSetup_StartScriptedDoubleWildBattle(void) { LockPlayerFieldControls(); gMain.savedCallback = CB2_EndScriptedWildBattle; gBattleTypeFlags = BATTLE_TYPE_DOUBLE; CreateBattleStartTask(GetWildBattleTransition(), 0); IncrementGameStat(GAME_STAT_TOTAL_BATTLES); IncrementGameStat(GAME_STAT_WILD_BATTLES); IncrementDailyWildBattles(); TryUpdateGymLeaderRematchFromWild(); } void BattleSetup_StartLatiBattle(void) { LockPlayerFieldControls(); gMain.savedCallback = CB2_EndScriptedWildBattle; gBattleTypeFlags = BATTLE_TYPE_LEGENDARY; CreateBattleStartTask(GetWildBattleTransition(), 0); IncrementGameStat(GAME_STAT_TOTAL_BATTLES); IncrementGameStat(GAME_STAT_WILD_BATTLES); IncrementDailyWildBattles(); TryUpdateGymLeaderRematchFromWild(); } void BattleSetup_StartLegendaryBattle(void) { LockPlayerFieldControls(); gMain.savedCallback = CB2_EndScriptedWildBattle; gBattleTypeFlags = BATTLE_TYPE_LEGENDARY; switch (GetMonData(&gEnemyParty[0], MON_DATA_SPECIES, NULL)) { default: case SPECIES_GROUDON: case SPECIES_GROUDON_PRIMAL: CreateBattleStartTask(B_TRANSITION_GROUDON, MUS_VS_KYOGRE_GROUDON); break; case SPECIES_KYOGRE: case SPECIES_KYOGRE_PRIMAL: CreateBattleStartTask(B_TRANSITION_KYOGRE, MUS_VS_KYOGRE_GROUDON); break; case SPECIES_RAYQUAZA: case SPECIES_RAYQUAZA_MEGA: CreateBattleStartTask(B_TRANSITION_RAYQUAZA, MUS_VS_RAYQUAZA); break; case SPECIES_DEOXYS_NORMAL: case SPECIES_DEOXYS_ATTACK: case SPECIES_DEOXYS_DEFENSE: case SPECIES_DEOXYS_SPEED: CreateBattleStartTask(B_TRANSITION_BLUR, MUS_RG_VS_DEOXYS); break; case SPECIES_LUGIA: case SPECIES_HO_OH: CreateBattleStartTask(B_TRANSITION_BLUR, MUS_RG_VS_LEGEND); break; case SPECIES_MEW: CreateBattleStartTask(B_TRANSITION_GRID_SQUARES, MUS_VS_MEW); break; } IncrementGameStat(GAME_STAT_TOTAL_BATTLES); IncrementGameStat(GAME_STAT_WILD_BATTLES); IncrementDailyWildBattles(); TryUpdateGymLeaderRematchFromWild(); } void StartGroudonKyogreBattle(void) { LockPlayerFieldControls(); gMain.savedCallback = CB2_EndScriptedWildBattle; gBattleTypeFlags = BATTLE_TYPE_LEGENDARY; if (gGameVersion == VERSION_RUBY) CreateBattleStartTask(B_TRANSITION_ANGLED_WIPES, MUS_VS_KYOGRE_GROUDON); // GROUDON else CreateBattleStartTask(B_TRANSITION_RIPPLE, MUS_VS_KYOGRE_GROUDON); // KYOGRE IncrementGameStat(GAME_STAT_TOTAL_BATTLES); IncrementGameStat(GAME_STAT_WILD_BATTLES); IncrementDailyWildBattles(); TryUpdateGymLeaderRematchFromWild(); } void StartRegiBattle(void) { enum BattleTransition transitionId; u16 species; LockPlayerFieldControls(); gMain.savedCallback = CB2_EndScriptedWildBattle; gBattleTypeFlags = BATTLE_TYPE_LEGENDARY; 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_VS_REGI); IncrementGameStat(GAME_STAT_TOTAL_BATTLES); IncrementGameStat(GAME_STAT_WILD_BATTLES); IncrementDailyWildBattles(); TryUpdateGymLeaderRematchFromWild(); } static void DowngradeBadPoison(void) { u8 i; u32 status = STATUS1_POISON; if (B_TOXIC_REVERSAL < GEN_5) return; for(i = 0; i < PARTY_SIZE; i++) { if (GetMonData(&gPlayerParty[i], MON_DATA_SANITY_HAS_SPECIES) && GetMonData(&gPlayerParty[i], MON_DATA_STATUS) == STATUS1_TOXIC_POISON) SetMonData(&gPlayerParty[i], MON_DATA_STATUS, &status); } } static void CB2_EndWildBattle(void) { CpuFill16(0, (void *)(BG_PLTT), BG_PLTT_SIZE); ResetOamRange(0, 128); if (IsNPCFollowerWildBattle()) { RestorePartyAfterFollowerNPCBattle(); if (FNPC_FLAG_HEAL_AFTER_FOLLOWER_BATTLE != 0 && (FNPC_FLAG_HEAL_AFTER_FOLLOWER_BATTLE == FNPC_ALWAYS || FlagGet(FNPC_FLAG_HEAL_AFTER_FOLLOWER_BATTLE))) HealPlayerParty(); } if (IsPlayerDefeated(gBattleOutcome) == TRUE && CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE && !InBattlePike()) { SetMainCallback2(CB2_WhiteOut); } else { SetMainCallback2(CB2_ReturnToField); DowngradeBadPoison(); gFieldCallback = FieldCB_ReturnToFieldNoScriptCheckMusic; } } static void CB2_EndScriptedWildBattle(void) { CpuFill16(0, (void *)(BG_PLTT), BG_PLTT_SIZE); ResetOamRange(0, 128); if (IsPlayerDefeated(gBattleOutcome) == TRUE) { if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); else SetMainCallback2(CB2_WhiteOut); } else { DowngradeBadPoison(); SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); } } enum BattleEnvironments BattleSetup_GetEnvironmentId(void) { u16 tileBehavior; s16 x, y; if (ShouldUseFishingEnvironmentInBattle()) GetXYCoordsOneStepInFrontOfPlayer(&x, &y); else PlayerGetDestCoords(&x, &y); tileBehavior = MapGridGetMetatileBehaviorAt(x, y); if (MetatileBehavior_IsTallGrass(tileBehavior)) return BATTLE_ENVIRONMENT_GRASS; if (MetatileBehavior_IsLongGrass(tileBehavior)) return BATTLE_ENVIRONMENT_LONG_GRASS; if (MetatileBehavior_IsSandOrDeepSand(tileBehavior)) return BATTLE_ENVIRONMENT_SAND; switch (gMapHeader.mapType) { case MAP_TYPE_TOWN: case MAP_TYPE_CITY: case MAP_TYPE_ROUTE: break; case MAP_TYPE_UNDERGROUND: if (MetatileBehavior_IsIndoorEncounter(tileBehavior)) return BATTLE_ENVIRONMENT_BUILDING; if (MetatileBehavior_IsSurfableWaterOrUnderwater(tileBehavior)) return BATTLE_ENVIRONMENT_POND; return BATTLE_ENVIRONMENT_CAVE; case MAP_TYPE_INDOOR: case MAP_TYPE_SECRET_BASE: return BATTLE_ENVIRONMENT_BUILDING; case MAP_TYPE_UNDERWATER: return BATTLE_ENVIRONMENT_UNDERWATER; case MAP_TYPE_OCEAN_ROUTE: if (MetatileBehavior_IsSurfableWaterOrUnderwater(tileBehavior)) return BATTLE_ENVIRONMENT_WATER; return BATTLE_ENVIRONMENT_PLAIN; } if (MetatileBehavior_IsDeepOrOceanWater(tileBehavior)) return BATTLE_ENVIRONMENT_WATER; if (MetatileBehavior_IsSurfableWaterOrUnderwater(tileBehavior)) return BATTLE_ENVIRONMENT_POND; if (MetatileBehavior_IsMountain(tileBehavior)) return BATTLE_ENVIRONMENT_MOUNTAIN; if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING)) { // Is BRIDGE_TYPE_POND_*? if (MetatileBehavior_GetBridgeType(tileBehavior) != BRIDGE_TYPE_OCEAN) return BATTLE_ENVIRONMENT_POND; if (MetatileBehavior_IsBridgeOverWater(tileBehavior) == TRUE) return BATTLE_ENVIRONMENT_WATER; } if (gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(MAP_ROUTE113) && gSaveBlock1Ptr->location.mapNum == MAP_NUM(MAP_ROUTE113)) return BATTLE_ENVIRONMENT_SAND; if (GetSavedWeather() == WEATHER_SANDSTORM) return BATTLE_ENVIRONMENT_SAND; return BATTLE_ENVIRONMENT_PLAIN; } static enum TransitionType GetBattleTransitionTypeByMap(void) { u16 tileBehavior; s16 x, y; PlayerGetDestCoords(&x, &y); tileBehavior = MapGridGetMetatileBehaviorAt(x, y); if (GetFlashLevel()) return TRANSITION_TYPE_FLASH; if (MetatileBehavior_IsSurfableWaterOrUnderwater(tileBehavior)) return TRANSITION_TYPE_WATER; switch (gMapHeader.mapType) { case MAP_TYPE_UNDERGROUND: return TRANSITION_TYPE_CAVE; case MAP_TYPE_UNDERWATER: return TRANSITION_TYPE_WATER; default: return TRANSITION_TYPE_NORMAL; } } static u16 GetSumOfPlayerPartyLevel(u8 numMons) { u8 sum = 0; int i; for (i = 0; i < PARTY_SIZE; i++) { u32 species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG); 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; const struct TrainerMon *party; if (GetTrainerPartySizeFromId(opponentId) < count) count = GetTrainerPartySizeFromId(opponentId); sum = 0; party = GetTrainerPartyFromId(opponentId); for (i = 0; i < count && party != NULL; i++) sum += party[i].lvl; return sum; } enum BattleTransition GetWildBattleTransition(void) { u8 transitionType = GetBattleTransitionTypeByMap(); u8 enemyLevel = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL); u8 playerLevel = GetSumOfPlayerPartyLevel(1); if (enemyLevel < playerLevel) { if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) return B_TRANSITION_BLUR; else return sBattleTransitionTable_Wild[transitionType][0]; } else { if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) return B_TRANSITION_GRID_SQUARES; else return sBattleTransitionTable_Wild[transitionType][1]; } } enum BattleTransition GetTrainerBattleTransition(void) { u8 minPartyCount = 1; u8 transitionType; u8 enemyLevel; u8 playerLevel; u32 trainerId = SanitizeTrainerId(TRAINER_BATTLE_PARAM.opponentA); enum TrainerClassID trainerClass = GetTrainerClassFromId(TRAINER_BATTLE_PARAM.opponentA); if (DoesTrainerHaveMugshot(trainerId)) return B_TRANSITION_MUGSHOT; if (trainerClass == TRAINER_CLASS_TEAM_MAGMA || trainerClass == TRAINER_CLASS_MAGMA_LEADER || trainerClass == TRAINER_CLASS_MAGMA_ADMIN) return B_TRANSITION_MAGMA; if (trainerClass == TRAINER_CLASS_TEAM_AQUA || trainerClass == TRAINER_CLASS_AQUA_LEADER || trainerClass == TRAINER_CLASS_AQUA_ADMIN) return B_TRANSITION_AQUA; switch (GetTrainerBattleType(trainerId)) { case TRAINER_BATTLE_TYPE_SINGLES: minPartyCount = 1; break; case TRAINER_BATTLE_TYPE_DOUBLES: minPartyCount = 2; // double battles always at least have 2 Pokémon. break; } transitionType = GetBattleTransitionTypeByMap(); enemyLevel = GetSumOfEnemyPartyLevel(trainerId, minPartyCount); playerLevel = GetSumOfPlayerPartyLevel(minPartyCount); if (enemyLevel < playerLevel) return sBattleTransitionTable_Trainer[transitionType][0]; else return sBattleTransitionTable_Trainer[transitionType][1]; } #define RANDOM_TRANSITION(table) (table[Random() % ARRAY_COUNT(table)]) enum BattleTransition GetSpecialBattleTransition(enum BattleTransitionGroup id) { u16 var; u8 enemyLevel = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL); u8 playerLevel = GetSumOfPlayerPartyLevel(1); if (enemyLevel < playerLevel) { switch (id) { case B_TRANSITION_GROUP_TRAINER_HILL: case B_TRANSITION_GROUP_SECRET_BASE: case B_TRANSITION_GROUP_E_READER: return B_TRANSITION_POKEBALLS_TRAIL; case B_TRANSITION_GROUP_B_PYRAMID: return RANDOM_TRANSITION(sBattleTransitionTable_BattlePyramid); case B_TRANSITION_GROUP_B_DOME: return RANDOM_TRANSITION(sBattleTransitionTable_BattleDome); default: break; } if (VarGet(VAR_FRONTIER_BATTLE_MODE) != FRONTIER_MODE_LINK_MULTIS) return RANDOM_TRANSITION(sBattleTransitionTable_BattleFrontier); } else { switch (id) { case B_TRANSITION_GROUP_TRAINER_HILL: case B_TRANSITION_GROUP_SECRET_BASE: case B_TRANSITION_GROUP_E_READER: return B_TRANSITION_BIG_POKEBALL; case B_TRANSITION_GROUP_B_PYRAMID: return RANDOM_TRANSITION(sBattleTransitionTable_BattlePyramid); case B_TRANSITION_GROUP_B_DOME: return RANDOM_TRANSITION(sBattleTransitionTable_BattleDome); default: break; } if (VarGet(VAR_FRONTIER_BATTLE_MODE) != FRONTIER_MODE_LINK_MULTIS) return RANDOM_TRANSITION(sBattleTransitionTable_BattleFrontier); } var = gSaveBlock2Ptr->frontier.trainerIds[gSaveBlock2Ptr->frontier.curChallengeBattleNum * 2 + 0] + gSaveBlock2Ptr->frontier.trainerIds[gSaveBlock2Ptr->frontier.curChallengeBattleNum * 2 + 1]; return sBattleTransitionTable_BattleFrontier[var % ARRAY_COUNT(sBattleTransitionTable_BattleFrontier)]; } void ChooseStarter(void) { SetMainCallback2(CB2_ChooseStarter); gMain.savedCallback = CB2_GiveStarter; } static void CB2_GiveStarter(void) { u16 starterMon; *GetVarPointer(VAR_STARTER_MON) = gSpecialVar_Result; starterMon = GetStarterPokemon(gSpecialVar_Result); ScriptGiveMon(starterMon, 5, ITEM_NONE); 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); RestartWildEncounterImmunitySteps(); ClearPoisonStepCounter(); IncrementGameStat(GAME_STAT_TOTAL_BATTLES); IncrementGameStat(GAME_STAT_WILD_BATTLES); IncrementDailyWildBattles(); TryUpdateGymLeaderRematchFromWild(); } } static void CB2_EndFirstBattle(void) { Overworld_ClearSavedMusic(); DowngradeBadPoison(); SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); } static void TryUpdateGymLeaderRematchFromWild(void) { if (GetGameStat(GAME_STAT_WILD_BATTLES) % 60 == 0) UpdateGymLeaderRematch(); } static void TryUpdateGymLeaderRematchFromTrainer(void) { if (GetGameStat(GAME_STAT_TRAINER_BATTLES) % 20 == 0) UpdateGymLeaderRematch(); } static u16 GetTrainerAFlag(void) { return TRAINER_FLAGS_START + TRAINER_BATTLE_PARAM.opponentA; } static u16 GetTrainerBFlag(void) { return TRAINER_FLAGS_START + TRAINER_BATTLE_PARAM.opponentB; } static bool32 IsPlayerDefeated(u32 battleOutcome) { switch (battleOutcome) { case B_OUTCOME_LOST: case B_OUTCOME_DREW: case B_OUTCOME_FORFEITED: 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) { TRAINER_BATTLE_PARAM.opponentA = 0; TRAINER_BATTLE_PARAM.opponentB = 0; } static void InitTrainerBattleVariables(void) { memset(gTrainerBattleParameter.data, 0, sizeof(TrainerBattleParameter)); sTrainerBattleEndScript = NULL; } void TrainerBattleLoadArgs(const u8 *data) { InitTrainerBattleVariables(); memcpy(gTrainerBattleParameter.data, data, sizeof(TrainerBattleParameter)); sTrainerBattleEndScript = (u8*)data + sizeof(TrainerBattleParameter); } void TrainerBattleLoadArgsTrainerA(const u8 *data) { TrainerBattleParameter *temp = (TrainerBattleParameter*)data; TRAINER_BATTLE_PARAM.playMusicA = temp->params.playMusicA; TRAINER_BATTLE_PARAM.objEventLocalIdA = temp->params.objEventLocalIdA; TRAINER_BATTLE_PARAM.opponentA = temp->params.opponentA; TRAINER_BATTLE_PARAM.introTextA = temp->params.introTextA; TRAINER_BATTLE_PARAM.defeatTextA = temp->params.defeatTextA; TRAINER_BATTLE_PARAM.battleScriptRetAddrA = temp->params.battleScriptRetAddrA; } void TrainerBattleLoadArgsTrainerB(const u8 *data) { TrainerBattleParameter *temp = (TrainerBattleParameter*)data; TRAINER_BATTLE_PARAM.playMusicB = temp->params.playMusicB; TRAINER_BATTLE_PARAM.objEventLocalIdB = temp->params.objEventLocalIdB; TRAINER_BATTLE_PARAM.opponentB = temp->params.opponentB; TRAINER_BATTLE_PARAM.introTextB = temp->params.introTextB; TRAINER_BATTLE_PARAM.defeatTextB = temp->params.defeatTextB; TRAINER_BATTLE_PARAM.battleScriptRetAddrB = temp->params.battleScriptRetAddrB; } // loads trainer A parameter to trainer B. Used for second trainer in trainer_see.c void TrainerBattleLoadArgsSecondTrainer(const u8 *data) { TrainerBattleParameter *temp = (TrainerBattleParameter*)data; TRAINER_BATTLE_PARAM.playMusicB = temp->params.playMusicA; TRAINER_BATTLE_PARAM.objEventLocalIdB = temp->params.objEventLocalIdA; TRAINER_BATTLE_PARAM.opponentB = temp->params.opponentA; TRAINER_BATTLE_PARAM.introTextB = temp->params.introTextA; TRAINER_BATTLE_PARAM.defeatTextB = temp->params.defeatTextA; TRAINER_BATTLE_PARAM.battleScriptRetAddrB = temp->params.battleScriptRetAddrA; } void SetMapVarsToTrainerA(void) { if (TRAINER_BATTLE_PARAM.objEventLocalIdA != LOCALID_NONE) { gSpecialVar_LastTalked = TRAINER_BATTLE_PARAM.objEventLocalIdA; gSelectedObjectEvent = GetObjectEventIdByLocalIdAndMap(TRAINER_BATTLE_PARAM.objEventLocalIdA, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); } } void SetMapVarsToTrainerB(void) { if (TRAINER_BATTLE_PARAM.objEventLocalIdB != LOCALID_NONE) { gSpecialVar_LastTalked = TRAINER_BATTLE_PARAM.objEventLocalIdB; gSelectedObjectEvent = GetObjectEventIdByLocalIdAndMap(TRAINER_BATTLE_PARAM.objEventLocalIdB, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); } } // expects parameters have been loaded correctly with TrainerBattleLoadArgs const u8 *BattleSetup_ConfigureTrainerBattle(const u8 *data) { switch (TRAINER_BATTLE_PARAM.mode) { case TRAINER_BATTLE_SINGLE_NO_INTRO_TEXT: return EventScript_DoNoIntroTrainerBattle; case TRAINER_BATTLE_DOUBLE: SetMapVarsToTrainerA(); return EventScript_TryDoDoubleTrainerBattle; case TRAINER_BATTLE_CONTINUE_SCRIPT: if (gApproachingTrainerId == 0) { SetMapVarsToTrainerA(); } return EventScript_TryDoNormalTrainerBattle; case TRAINER_BATTLE_CONTINUE_SCRIPT_NO_MUSIC: SetMapVarsToTrainerA(); return EventScript_TryDoNormalTrainerBattle; case TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE: case TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE_NO_MUSIC: SetMapVarsToTrainerA(); return EventScript_TryDoDoubleTrainerBattle; #if FREE_MATCH_CALL == FALSE case TRAINER_BATTLE_REMATCH_DOUBLE: SetMapVarsToTrainerA(); TRAINER_BATTLE_PARAM.opponentA = GetRematchTrainerId(TRAINER_BATTLE_PARAM.opponentA); return EventScript_TryDoDoubleRematchBattle; case TRAINER_BATTLE_REMATCH: SetMapVarsToTrainerA(); TRAINER_BATTLE_PARAM.opponentA = GetRematchTrainerId(TRAINER_BATTLE_PARAM.opponentA); return EventScript_TryDoRematchBattle; #endif //FREE_MATCH_CALL case TRAINER_BATTLE_PYRAMID: if (gApproachingTrainerId == 0) { SetMapVarsToTrainerA(); TRAINER_BATTLE_PARAM.opponentA = LocalIdToPyramidTrainerId(gSpecialVar_LastTalked); } else { TRAINER_BATTLE_PARAM.opponentB = LocalIdToPyramidTrainerId(gSpecialVar_LastTalked); } return EventScript_TryDoNormalTrainerBattle; case TRAINER_BATTLE_SET_TRAINERS_FOR_MULTI_BATTLE: return sTrainerBattleEndScript; case TRAINER_BATTLE_HILL: if (gApproachingTrainerId == 0) { SetMapVarsToTrainerA(); TRAINER_BATTLE_PARAM.opponentA = LocalIdToHillTrainerId(gSpecialVar_LastTalked); } else { TRAINER_BATTLE_PARAM.opponentB = LocalIdToHillTrainerId(gSpecialVar_LastTalked); } return EventScript_TryDoNormalTrainerBattle; case TRAINER_BATTLE_TWO_TRAINERS_NO_INTRO: gNoOfApproachingTrainers = 2; // set TWO_OPPONENTS gBattleTypeFlags gApproachingTrainerId = 1; // prevent trainer approach return EventScript_DoNoIntroTrainerBattle; default: if (gApproachingTrainerId == 0) { SetMapVarsToTrainerA(); } return EventScript_TryDoNormalTrainerBattle; } } void ConfigureAndSetUpOneTrainerBattle(u8 trainerObjEventId, const u8 *trainerScript) { gSelectedObjectEvent = trainerObjEventId; gSpecialVar_LastTalked = gObjectEvents[trainerObjEventId].localId; TrainerBattleLoadArgs(trainerScript + 1); BattleSetup_ConfigureTrainerBattle(trainerScript + 1); ScriptContext_SetupScript(EventScript_StartTrainerApproach); LockPlayerFieldControls(); } void ConfigureTwoTrainersBattle(u8 trainerObjEventId, const u8 *trainerScript) { gSelectedObjectEvent = trainerObjEventId; gSpecialVar_LastTalked = gObjectEvents[trainerObjEventId].localId; if (gApproachingTrainerId == 0) TrainerBattleLoadArgs(trainerScript + 1); else TrainerBattleLoadArgsSecondTrainer(trainerScript + 1); BattleSetup_ConfigureTrainerBattle(trainerScript + 1); } void SetUpTwoTrainersBattle(void) { ScriptContext_SetupScript(EventScript_StartTrainerApproach); LockPlayerFieldControls(); } #define OPCODE_OFFSET 1 bool32 GetTrainerFlagFromScriptPointer(const u8 *data) { TrainerBattleParameter *temp = (TrainerBattleParameter*)(data + OPCODE_OFFSET); return FlagGet(TRAINER_FLAGS_START + temp->params.opponentA); } bool32 GetRematchFromScriptPointer(const u8 *data) { #if FREE_MATCH_CALL return FALSE; #else TrainerBattleParameter *temp = (TrainerBattleParameter*)(data + OPCODE_OFFSET); return ShouldTryRematchBattleForTrainerId(temp->params.opponentA); #endif } #undef OPCODE_OFFSET // Set trainer's movement type so they stop and remain facing that direction // Note: Only for trainers who are spoken to directly // For trainers who spot the player this is handled by PlayerFaceApproachingTrainer void SetTrainerFacingDirection(void) { struct ObjectEvent *objectEvent = &gObjectEvents[gSelectedObjectEvent]; SetTrainerMovementType(objectEvent, GetTrainerFacingDirectionMovementType(objectEvent->facingDirection)); } u8 GetTrainerBattleMode(void) { return TRAINER_BATTLE_PARAM.mode; } bool8 GetTrainerFlag(void) { if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) return GetBattlePyramidTrainerFlag(gSelectedObjectEvent); else if (InTrainerHill()) return GetHillTrainerFlag(gSelectedObjectEvent); else return FlagGet(GetTrainerAFlag()); } static void SetBattledTrainersFlags(void) { if (TRAINER_BATTLE_PARAM.opponentB != 0) FlagSet(GetTrainerBFlag()); FlagSet(GetTrainerAFlag()); } static void UNUSED SetBattledTrainerFlag(void) { FlagSet(GetTrainerAFlag()); } bool8 HasTrainerBeenFought(u16 trainerId) { return FlagGet(TRAINER_FLAGS_START + trainerId); } void SetTrainerFlag(u16 trainerId) { FlagSet(TRAINER_FLAGS_START + trainerId); } void ClearTrainerFlag(u16 trainerId) { FlagClear(TRAINER_FLAGS_START + trainerId); } void BattleSetup_StartTrainerBattle(void) { if (gNoOfApproachingTrainers == 2) { if (FollowerNPCIsBattlePartner()) gBattleTypeFlags = (BATTLE_TYPE_MULTI | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_TRAINER); else gBattleTypeFlags = (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_TRAINER); } else { if (FollowerNPCIsBattlePartner()) { gBattleTypeFlags = (BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TRAINER); TRAINER_BATTLE_PARAM.opponentB = 0xFFFF; } else { gBattleTypeFlags = (BATTLE_TYPE_TRAINER); } } if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) { VarSet(VAR_TEMP_PLAYING_PYRAMID_MUSIC, 0); gBattleTypeFlags |= BATTLE_TYPE_PYRAMID; if (gNoOfApproachingTrainers == 2) { FillFrontierTrainersParties(1); ZeroMonData(&gEnemyParty[1]); ZeroMonData(&gEnemyParty[2]); ZeroMonData(&gEnemyParty[4]); ZeroMonData(&gEnemyParty[5]); } else { FillFrontierTrainerParty(1); ZeroMonData(&gEnemyParty[1]); ZeroMonData(&gEnemyParty[2]); } MarkApproachingPyramidTrainersAsBattled(); } else if (InTrainerHillChallenge()) { gBattleTypeFlags |= BATTLE_TYPE_TRAINER_HILL; if (gNoOfApproachingTrainers == 2) FillHillTrainersParties(); else FillHillTrainerParty(); SetHillTrainerFlag(); } sNoOfPossibleTrainerRetScripts = gNoOfApproachingTrainers; gNoOfApproachingTrainers = 0; sShouldCheckTrainerBScript = FALSE; gWhichTrainerToFaceAfterBattle = 0; gMain.savedCallback = CB2_EndTrainerBattle; if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE || InTrainerHillChallenge()) DoBattlePyramidTrainerHillBattle(); else DoTrainerBattle(); ScriptContext_Stop(); } void BattleSetup_StartTrainerBattle_Debug(void) { sNoOfPossibleTrainerRetScripts = gNoOfApproachingTrainers; gNoOfApproachingTrainers = 0; sShouldCheckTrainerBScript = FALSE; gWhichTrainerToFaceAfterBattle = 0; gMain.savedCallback = CB2_EndTrainerBattle; CreateBattleStartTask_Debug(GetWildBattleTransition(), 0); ScriptContext_Stop(); } static void SaveChangesToPlayerParty(void) { u8 i = 0, j = 0; u8 participatedPokemon = VarGet(B_VAR_SKY_BATTLE); for (i = 0; i < PARTY_SIZE; i++) { if ((participatedPokemon >> i & 1) == 1) { SavePlayerPartyMon(i, &gPlayerParty[j]); j++; } } } static void HandleBattleVariantEndParty(void) { if (B_FLAG_SKY_BATTLE == 0 || !FlagGet(B_FLAG_SKY_BATTLE)) return; SaveChangesToPlayerParty(); LoadPlayerParty(); FlagClear(B_FLAG_SKY_BATTLE); } static void CB2_EndTrainerBattle(void) { HandleBattleVariantEndParty(); gIsDebugBattle = FALSE; if (FollowerNPCIsBattlePartner()) { RestorePartyAfterFollowerNPCBattle(); if (FNPC_FLAG_HEAL_AFTER_FOLLOWER_BATTLE != 0 && (FNPC_FLAG_HEAL_AFTER_FOLLOWER_BATTLE == FNPC_ALWAYS || FlagGet(FNPC_FLAG_HEAL_AFTER_FOLLOWER_BATTLE))) HealPlayerParty(); } if (TRAINER_BATTLE_PARAM.opponentA == TRAINER_SECRET_BASE) { DowngradeBadPoison(); SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); } else if (DidPlayerForfeitNormalTrainerBattle()) { if (FlagGet(B_FLAG_NO_WHITEOUT) || CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE || InTrainerHillChallenge()) SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); else SetMainCallback2(CB2_WhiteOut); } else if (IsPlayerDefeated(gBattleOutcome) == TRUE) { if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE || InTrainerHillChallenge() || (!NoAliveMonsForPlayer()) || FlagGet(B_FLAG_NO_WHITEOUT)) SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); else SetMainCallback2(CB2_WhiteOut); } else { SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); DowngradeBadPoison(); if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE && !InTrainerHillChallenge()) { RegisterTrainerInMatchCall(); SetBattledTrainersFlags(); } } } static void CB2_EndRematchBattle(void) { if (TRAINER_BATTLE_PARAM.opponentA == TRAINER_SECRET_BASE) { DowngradeBadPoison(); SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); } else if (IsPlayerDefeated(gBattleOutcome) == TRUE) { SetMainCallback2(CB2_WhiteOut); } else { SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); RegisterTrainerInMatchCall(); SetBattledTrainersFlags(); HandleRematchVarsOnBattleEnd(); DowngradeBadPoison(); } } void BattleSetup_StartRematchBattle(void) { gBattleTypeFlags = BATTLE_TYPE_TRAINER; gMain.savedCallback = CB2_EndRematchBattle; DoTrainerBattle(); ScriptContext_Stop(); } void ShowTrainerIntroSpeech(void) { if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) { if (gNoOfApproachingTrainers == 0 || gNoOfApproachingTrainers == 1) CopyPyramidTrainerSpeechBefore(LocalIdToPyramidTrainerId(gSpecialVar_LastTalked)); else CopyPyramidTrainerSpeechBefore(LocalIdToPyramidTrainerId(gObjectEvents[gApproachingTrainers[gApproachingTrainerId].objectEventId].localId)); ShowFieldMessageFromBuffer(); } else if (InTrainerHillChallenge()) { if (gNoOfApproachingTrainers == 0 || gNoOfApproachingTrainers == 1) CopyTrainerHillTrainerText(TRAINER_HILL_TEXT_INTRO, LocalIdToHillTrainerId(gSpecialVar_LastTalked)); else CopyTrainerHillTrainerText(TRAINER_HILL_TEXT_INTRO, LocalIdToHillTrainerId(gObjectEvents[gApproachingTrainers[gApproachingTrainerId].objectEventId].localId)); ShowFieldMessageFromBuffer(); } 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 (TRAINER_BATTLE_PARAM.battleScriptRetAddrB != NULL) { gWhichTrainerToFaceAfterBattle = 1; return TRAINER_BATTLE_PARAM.battleScriptRetAddrB; } } else { if (TRAINER_BATTLE_PARAM.battleScriptRetAddrA != NULL) { gWhichTrainerToFaceAfterBattle = 0; return TRAINER_BATTLE_PARAM.battleScriptRetAddrA; } } return EventScript_TryGetTrainerScript; } void ShowTrainerCantBattleSpeech(void) { ShowFieldMessage(GetTrainerCantBattleSpeech()); } void PlayTrainerEncounterMusic(void) { u16 trainerId; u16 music; if (gApproachingTrainerId == 0) trainerId = TRAINER_BATTLE_PARAM.opponentA; else trainerId = TRAINER_BATTLE_PARAM.opponentB; if (TRAINER_BATTLE_PARAM.mode != TRAINER_BATTLE_CONTINUE_SCRIPT_NO_MUSIC && TRAINER_BATTLE_PARAM.mode != TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE_NO_MUSIC) { switch (GetTrainerEncounterMusicId(trainerId)) { case TRAINER_ENCOUNTER_MUSIC_MALE: music = MUS_ENCOUNTER_MALE; break; case TRAINER_ENCOUNTER_MUSIC_FEMALE: music = MUS_ENCOUNTER_FEMALE; break; case TRAINER_ENCOUNTER_MUSIC_GIRL: music = MUS_ENCOUNTER_GIRL; break; case TRAINER_ENCOUNTER_MUSIC_INTENSE: music = MUS_ENCOUNTER_INTENSE; break; case TRAINER_ENCOUNTER_MUSIC_COOL: music = MUS_ENCOUNTER_COOL; break; case TRAINER_ENCOUNTER_MUSIC_AQUA: music = MUS_ENCOUNTER_AQUA; break; case TRAINER_ENCOUNTER_MUSIC_MAGMA: music = MUS_ENCOUNTER_MAGMA; break; case TRAINER_ENCOUNTER_MUSIC_SWIMMER: music = MUS_ENCOUNTER_SWIMMER; break; case TRAINER_ENCOUNTER_MUSIC_TWINS: music = MUS_ENCOUNTER_TWINS; break; case TRAINER_ENCOUNTER_MUSIC_ELITE_FOUR: music = MUS_ENCOUNTER_ELITE_FOUR; break; case TRAINER_ENCOUNTER_MUSIC_HIKER: music = MUS_ENCOUNTER_HIKER; break; case TRAINER_ENCOUNTER_MUSIC_INTERVIEWER: music = MUS_ENCOUNTER_INTERVIEWER; break; case TRAINER_ENCOUNTER_MUSIC_RICH: music = MUS_ENCOUNTER_RICH; break; default: music = MUS_ENCOUNTER_SUSPICIOUS; } 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) { if (OW_NAME_BOX_NPC_TRAINER) gSpeakerName = GetTrainerNameFromId(TRAINER_BATTLE_PARAM.opponentA); return ReturnEmptyStringIfNull(TRAINER_BATTLE_PARAM.introTextA); } else { if (OW_NAME_BOX_NPC_TRAINER) gSpeakerName = GetTrainerNameFromId(TRAINER_BATTLE_PARAM.opponentB); return ReturnEmptyStringIfNull(TRAINER_BATTLE_PARAM.introTextB); } } const u8 *GetTrainerALoseText(void) { const u8 *string; if (TRAINER_BATTLE_PARAM.opponentA == TRAINER_SECRET_BASE) string = GetSecretBaseTrainerLoseText(); else string = TRAINER_BATTLE_PARAM.defeatTextA; StringExpandPlaceholders(gStringVar4, ReturnEmptyStringIfNull(string)); return gStringVar4; } const u8 *GetTrainerBLoseText(void) { StringExpandPlaceholders(gStringVar4, ReturnEmptyStringIfNull(TRAINER_BATTLE_PARAM.defeatTextB)); return gStringVar4; } const u8 *GetTrainerWonSpeech(void) { return ReturnEmptyStringIfNull(TRAINER_BATTLE_PARAM.victoryText); } static const u8 *GetTrainerCantBattleSpeech(void) { return ReturnEmptyStringIfNull(TRAINER_BATTLE_PARAM.cannotBattleText); } 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; } 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; // one line required to match -g if (table[i].trainerIds[j] == trainerId) return i; } } return -1; } // Returns TRUE if the given trainer (by their entry in the rematch table) is not allowed to have rematches. // This applies to the Elite Four and Victory Road Wally (if he's not been defeated yet) static inline bool32 IsRematchForbidden(s32 rematchTableId) { if (rematchTableId >= REMATCH_ELITE_FOUR_ENTRIES) return TRUE; else if (rematchTableId == REMATCH_WALLY_VR) return !FlagGet(FLAG_DEFEATED_WALLY_VICTORY_ROAD); else return FALSE; } static void SetRematchIdForTrainer(const struct RematchTrainer *table, u32 tableId) { #if FREE_MATCH_CALL == FALSE 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; #endif //FREE_MATCH_CALL } static inline bool32 DoesCurrentMapMatchRematchTrainerMap(s32 i, const struct RematchTrainer *table, u16 mapGroup, u16 mapNum) { return table[i].mapGroup == mapGroup && table[i].mapNum == mapNum; } bool32 TrainerIsMatchCallRegistered(s32 i) { return FlagGet(TRAINER_REGISTERED_FLAGS_START + i); } #if FREE_MATCH_CALL == FALSE static bool32 UpdateRandomTrainerRematches(const struct RematchTrainer *table, u16 mapGroup, u16 mapNum) { s32 i; if (CheckBagHasItem(ITEM_VS_SEEKER, 1) && I_VS_SEEKER_CHARGING != 0) return FALSE; for (i = 0; i <= REMATCH_SPECIAL_TRAINER_START; i++) { if (!DoesCurrentMapMatchRematchTrainerMap(i,table,mapGroup,mapNum) || IsRematchForbidden(i)) continue; // Only check permitted trainers within the current map. if (gSaveBlock1Ptr->trainerRematches[i] != 0) { // Trainer already wants a rematch. Don't bother updating it. return TRUE; } else if (TrainerIsMatchCallRegistered(i) && ((Random() % 100) <= 30)) // 31% chance of getting a rematch. { SetRematchIdForTrainer(table, i); return TRUE; } } return FALSE; } #endif //FREE_MATCH_CALL 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) { #if FREE_MATCH_CALL == FALSE 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; } #endif //FREE_MATCH_CALL 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 >= MAX_REMATCH_ENTRIES) return FALSE; #if FREE_MATCH_CALL == FALSE if (gSaveBlock1Ptr->trainerRematches[tableId] == 0) return FALSE; #endif //FREE_MATCH_CALL return TRUE; } static bool8 IsTrainerReadyForRematch_(const struct RematchTrainer *table, u16 trainerId) { s32 tableId = TrainerIdToRematchTableId(table, trainerId); if (tableId == -1) return FALSE; if (tableId >= MAX_REMATCH_ENTRIES) return FALSE; #if FREE_MATCH_CALL == FALSE if (gSaveBlock1Ptr->trainerRematches[tableId] == 0) return FALSE; #endif //FREE_MATCH_CALL return TRUE; } 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) { #if FREE_MATCH_CALL == FALSE s32 tableId = TrainerIdToRematchTableId(table, firstBattleTrainerId); if (tableId != -1) gSaveBlock1Ptr->trainerRematches[tableId] = 0; #endif //FREE_MATCH_CALL } void ClearCurrentTrainerWantRematchVsSeeker(void) { #if FREE_MATCH_CALL == FALSE if ((gBattleTypeFlags & BATTLE_TYPE_TRAINER) && FlagGet(I_VS_SEEKER_CHARGING) && (I_VS_SEEKER_CHARGING != 0)) { for (u32 i = 0; i < REMATCH_TABLE_ENTRIES; i++) { if (gSaveBlock1Ptr->trainerRematches[i] == TRAINER_BATTLE_PARAM.opponentA) gSaveBlock1Ptr->trainerRematches[i] = 0; } } #endif //FREE_MATCH_CALL } static u32 GetTrainerMatchCallFlag(u32 trainerId) { s32 i; for (i = 0; i < REMATCH_TABLE_ENTRIES; i++) { if (gRematchTable[i].trainerIds[0] == trainerId) return TRAINER_REGISTERED_FLAGS_START + i; } return 0xFFFF; } static void RegisterTrainerInMatchCall(void) { if (FlagGet(FLAG_HAS_MATCH_CALL)) { u32 matchCallFlagId = GetTrainerMatchCallFlag(TRAINER_BATTLE_PARAM.opponentA); 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; #if FREE_MATCH_CALL == FALSE if (I_VS_SEEKER_CHARGING) { if (gSaveBlock1Ptr->trainerRematches[tableId] == 0) return FALSE; } #endif return TRUE; } #if FREE_MATCH_CALL == FALSE static bool32 HasEnoughBadgesForRematch(void) { s32 i, count; for (count = 0, i = 0; i < ARRAY_COUNT(gBadgeFlags); i++) { if (FlagGet(gBadgeFlags[i]) == TRUE) { if (++count >= OW_REMATCH_BADGE_COUNT) return TRUE; } } return FALSE; } #endif //FREE_MATCH_CALL #define STEP_COUNTER_MAX 255 void IncrementRematchStepCounter(void) { #if FREE_MATCH_CALL == FALSE if (!HasEnoughBadgesForRematch()) return; if (IsVsSeekerEnabled()) return; if (gSaveBlock1Ptr->trainerRematchStepCounter >= STEP_COUNTER_MAX) gSaveBlock1Ptr->trainerRematchStepCounter = STEP_COUNTER_MAX; else gSaveBlock1Ptr->trainerRematchStepCounter++; #endif //FREE_MATCH_CALL } #if FREE_MATCH_CALL == FALSE static bool32 IsRematchStepCounterMaxed(void) { if (HasEnoughBadgesForRematch() && 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; } #endif //FREE_MATCH_CALL bool32 DoesSomeoneWantRematchIn(u16 mapGroup, u16 mapNum) { return DoesSomeoneWantRematchIn_(gRematchTable, mapGroup, mapNum); } bool32 IsRematchTrainerIn(u16 mapGroup, u16 mapNum) { return IsRematchTrainerIn_(gRematchTable, mapGroup, mapNum); } #if FREE_MATCH_CALL == FALSE static u16 GetRematchTrainerId(u16 trainerId) { if (FlagGet(I_VS_SEEKER_CHARGING) && (I_VS_SEEKER_CHARGING != 0)) return GetRematchTrainerIdVSSeeker(trainerId); else return GetRematchTrainerIdFromTable(gRematchTable, trainerId); } #endif //FREE_MATCH_CALL u16 GetLastBeatenRematchTrainerId(u16 trainerId) { return GetLastBeatenRematchTrainerIdFromTable(gRematchTable, trainerId); } bool8 ShouldTryRematchBattle(void) { return ShouldTryRematchBattleForTrainerId(TRAINER_BATTLE_PARAM.opponentA); } bool8 ShouldTryRematchBattleForTrainerId(u16 trainerId) { if (IsFirstTrainerIdReadyForRematch(gRematchTable, trainerId)) return TRUE; return WasSecondRematchWon(gRematchTable, trainerId); } bool8 IsTrainerReadyForRematch(void) { return IsTrainerReadyForRematch_(gRematchTable, TRAINER_BATTLE_PARAM.opponentA); } static void HandleRematchVarsOnBattleEnd(void) { if ((gBattleTypeFlags & BATTLE_TYPE_TRAINER) && (I_VS_SEEKER_CHARGING != 0)) ClearRematchMovementByTrainerId(); ClearTrainerWantRematchState(gRematchTable, TRAINER_BATTLE_PARAM.opponentA); 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; }