pokeemmo/src/battle_setup.c

1967 lines
66 KiB
C

#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;
}