summaryrefslogtreecommitdiff
path: root/src/battle_setup.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/battle_setup.c')
-rw-r--r--src/battle_setup.c1060
1 files changed, 1060 insertions, 0 deletions
diff --git a/src/battle_setup.c b/src/battle_setup.c
new file mode 100644
index 000000000..98437e279
--- /dev/null
+++ b/src/battle_setup.c
@@ -0,0 +1,1060 @@
+#include "global.h"
+#include "task.h"
+#include "help_system.h"
+#include "overworld.h"
+#include "item.h"
+#include "sound.h"
+#include "pokemon.h"
+#include "load_save.h"
+#include "safari_zone.h"
+#include "quest_log.h"
+#include "script.h"
+#include "script_pokemon_util_80A0058.h"
+#include "strings.h"
+#include "string_util.h"
+#include "event_data.h"
+#include "unk_8159F40.h"
+#include "map_obj_80688E4.h"
+#include "metatile_behavior.h"
+#include "event_scripts.h"
+#include "fldeff.h"
+#include "fieldmap.h"
+#include "field_control_avatar.h"
+#include "field_player_avatar.h"
+#include "field_screen_effect.h"
+#include "field_message_box.h"
+#include "field_map_obj.h"
+#include "vs_seeker.h"
+#include "battle.h"
+#include "battle_setup.h"
+#include "battle_transition.h"
+#include "constants/battle_setup.h"
+#include "constants/flags.h"
+#include "constants/items.h"
+#include "constants/maps.h"
+#include "constants/songs.h"
+#include "constants/species.h"
+#include "constants/pokemon.h"
+#include "constants/trainers.h"
+#include "constants/trainer_classes.h"
+#include "constants/map_types.h"
+
+enum
+{
+ TRAINER_PARAM_LOAD_VAL_8BIT,
+ TRAINER_PARAM_LOAD_VAL_16BIT,
+ TRAINER_PARAM_LOAD_VAL_32BIT,
+ TRAINER_PARAM_CLEAR_VAL_8BIT,
+ TRAINER_PARAM_CLEAR_VAL_16BIT,
+ TRAINER_PARAM_CLEAR_VAL_32BIT,
+ TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR,
+};
+
+struct TrainerBattleParameter
+{
+ void *varPtr;
+ u8 ptrType;
+};
+
+static void DoSafariBattle(void);
+static void DoGhostBattle(void);
+static void DoStandardWildBattle(void);
+static void CB2_EndWildBattle(void);
+static u8 GetWildBattleTransition(void);
+static u8 GetTrainerBattleTransition(void);
+static void CB2_EndScriptedWildBattle(void);
+static void CB2_EndMarowakBattle(void);
+static bool32 IsPlayerDefeated(u32 battleOutcome);
+static void CB2_EndTrainerBattle(void);
+static const u8 *GetIntroSpeechOfApproachingTrainer(void);
+static const u8 *GetTrainerCantBattleSpeech(void);
+
+static EWRAM_DATA u16 sTrainerBattleMode = 0;
+EWRAM_DATA u16 gTrainerBattleOpponent_A = 0;
+static EWRAM_DATA u16 sTrainerEventObjectLocalId = 0;
+static EWRAM_DATA u8 *sTrainerAIntroSpeech = NULL;
+static EWRAM_DATA u8 *sTrainerADefeatSpeech = NULL;
+static EWRAM_DATA u8 *sTrainerVictorySpeech = NULL;
+static EWRAM_DATA u8 *sTrainerCannotBattleSpeech = NULL;
+static EWRAM_DATA u8 *sTrainerBattleEndScript = NULL;
+static EWRAM_DATA u8 *sTrainerABattleScriptRetAddr = NULL;
+static EWRAM_DATA u16 gUnknown_20386CC = 0;
+
+static const u8 sBattleTransitionTable_Wild[][2] =
+{
+ B_TRANSITION_SLICED_SCREEN, B_TRANSITION_WHITEFADE_IN_STRIPES,
+ B_TRANSITION_CLOCKWISE_BLACKFADE, B_TRANSITION_GRID_SQUARES,
+ B_TRANSITION_BLUR, B_TRANSITION_GRID_SQUARES,
+ B_TRANSITION_BLACK_WAVE_TO_RIGHT, B_TRANSITION_FULLSCREEN_WAVE,
+};
+
+static const u8 sBattleTransitionTable_Trainer[][2] =
+{
+ B_TRANSITION_SLIDING_POKEBALLS, B_TRANSITION_BLACK_DOODLES,
+ B_TRANSITION_HORIZONTAL_CORRUGATE, B_TRANSITION_BIG_POKEBALL,
+ B_TRANSITION_BLUR, B_TRANSITION_GRID_SQUARES,
+ B_TRANSITION_DISTORTED_WAVE, B_TRANSITION_FULLSCREEN_WAVE,
+};
+
+static const struct TrainerBattleParameter sOrdinaryBattleParams[] =
+{
+ {&sTrainerBattleMode, TRAINER_PARAM_LOAD_VAL_8BIT},
+ {&gTrainerBattleOpponent_A, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerEventObjectLocalId, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerAIntroSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerADefeatSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerVictorySpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerCannotBattleSpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerABattleScriptRetAddr, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerBattleEndScript, TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR},
+};
+
+static const struct TrainerBattleParameter sContinueScriptBattleParams[] =
+{
+ {&sTrainerBattleMode, TRAINER_PARAM_LOAD_VAL_8BIT},
+ {&gTrainerBattleOpponent_A, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerEventObjectLocalId, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerAIntroSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerADefeatSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerVictorySpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerCannotBattleSpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerABattleScriptRetAddr, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerBattleEndScript, TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR},
+};
+
+static const struct TrainerBattleParameter sDoubleBattleParams[] =
+{
+ {&sTrainerBattleMode, TRAINER_PARAM_LOAD_VAL_8BIT},
+ {&gTrainerBattleOpponent_A, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerEventObjectLocalId, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerAIntroSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerADefeatSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerVictorySpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerCannotBattleSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerABattleScriptRetAddr, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerBattleEndScript, TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR},
+};
+
+static const struct TrainerBattleParameter sOrdinaryNoIntroBattleParams[] =
+{
+ {&sTrainerBattleMode, TRAINER_PARAM_LOAD_VAL_8BIT},
+ {&gTrainerBattleOpponent_A, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerEventObjectLocalId, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerAIntroSpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerADefeatSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerVictorySpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerCannotBattleSpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerABattleScriptRetAddr, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerBattleEndScript, TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR},
+};
+
+static const struct TrainerBattleParameter sTutorialBattleParams[] =
+{
+ {&sTrainerBattleMode, TRAINER_PARAM_LOAD_VAL_8BIT},
+ {&gTrainerBattleOpponent_A, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&gUnknown_20386CC, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerAIntroSpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerADefeatSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerVictorySpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerCannotBattleSpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerABattleScriptRetAddr, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerBattleEndScript, TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR},
+};
+
+static const struct TrainerBattleParameter sContinueScriptDoubleBattleParams[] =
+{
+ {&sTrainerBattleMode, TRAINER_PARAM_LOAD_VAL_8BIT},
+ {&gTrainerBattleOpponent_A, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerEventObjectLocalId, TRAINER_PARAM_LOAD_VAL_16BIT},
+ {&sTrainerAIntroSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerADefeatSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerVictorySpeech, TRAINER_PARAM_CLEAR_VAL_32BIT},
+ {&sTrainerCannotBattleSpeech, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerABattleScriptRetAddr, TRAINER_PARAM_LOAD_VAL_32BIT},
+ {&sTrainerBattleEndScript, TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR},
+};
+
+
+#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())
+ {
+ HelpSystem_Disable();
+ BT_StartOnField(tTransition);
+ ++tState;
+ }
+ break;
+ case 1:
+ if (BT_IsDone() == TRUE)
+ {
+ HelpSystem_Enable();
+ CleanupOverworldWindowsAndTilemaps();
+ SetMainCallback2(CB2_InitBattle);
+ RestartWildEncounterImmunitySteps();
+ ClearPoisonStepCounter();
+ DestroyTask(taskId);
+ }
+ break;
+ }
+}
+
+static void CreateBattleStartTask(u8 transition, u16 song) // song == 0 means default music for current map
+{
+ u8 taskId = CreateTask(Task_BattleStart, 1);
+
+ gTasks[taskId].tTransition = transition;
+ PlayMapChosenOrBattleBGM(song);
+}
+
+static bool8 CheckSilphScopeInPokemonTower(u16 mapGroup, u16 mapNum)
+{
+ if (mapGroup == MAP_GROUP(POKEMON_TOWER_1F)
+ && ((u16)(mapNum - MAP_NUM(POKEMON_TOWER_1F)) <= 6)
+ && !(CheckBagHasItem(ITEM_SILPH_SCOPE, 1)))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+void BattleSetup_StartWildBattle(void)
+{
+ if (GetSafariZoneFlag())
+ DoSafariBattle();
+ else if (CheckSilphScopeInPokemonTower(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum))
+ DoGhostBattle();
+ else
+ DoStandardWildBattle();
+}
+
+static void DoStandardWildBattle(void)
+{
+ ScriptContext2_Enable();
+ FreezeEventObjects();
+ sub_805C780();
+ gMain.savedCallback = CB2_EndWildBattle;
+ gBattleTypeFlags = 0;
+ CreateBattleStartTask(GetWildBattleTransition(), 0);
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_WILD_BATTLES);
+}
+
+void BattleSetup_StartRoamerBattle(void)
+{
+ ScriptContext2_Enable();
+ FreezeEventObjects();
+ sub_805C780();
+ gMain.savedCallback = CB2_EndWildBattle;
+ gBattleTypeFlags = BATTLE_TYPE_ROAMER;
+ CreateBattleStartTask(GetWildBattleTransition(), MUS_VS_DEN);
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_WILD_BATTLES);
+}
+
+static void DoSafariBattle(void)
+{
+ ScriptContext2_Enable();
+ FreezeEventObjects();
+ sub_805C780();
+ gMain.savedCallback = CB2_EndSafariBattle;
+ gBattleTypeFlags = BATTLE_TYPE_SAFARI;
+ CreateBattleStartTask(GetWildBattleTransition(), 0);
+}
+
+static void DoGhostBattle(void)
+{
+ ScriptContext2_Enable();
+ FreezeEventObjects();
+ sub_805C780();
+ gMain.savedCallback = CB2_EndWildBattle;
+ gBattleTypeFlags = BATTLE_TYPE_GHOST;
+ CreateBattleStartTask(GetWildBattleTransition(), 0);
+ SetMonData(&gEnemyParty[0], MON_DATA_NICKNAME, gUnknown_841D148);
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_WILD_BATTLES);
+}
+
+static void DoTrainerBattle(void)
+{
+ CreateBattleStartTask(GetTrainerBattleTransition(), 0);
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_TRAINER_BATTLES);
+}
+
+void ScrSpecial_StartOldManTutorialBattle(void)
+{
+ CreateMaleMon(&gEnemyParty[0], SPECIES_WEEDLE, 5);
+ ScriptContext2_Enable();
+ gMain.savedCallback = CB2_ReturnToFieldContinueScriptPlayMapMusic;
+ gBattleTypeFlags = BATTLE_TYPE_OLD_MAN_TUTORIAL;
+ CreateBattleStartTask(B_TRANSITION_SLICED_SCREEN, 0);
+}
+
+void BattleSetup_StartScriptedWildBattle(void)
+{
+ ScriptContext2_Enable();
+ gMain.savedCallback = CB2_EndScriptedWildBattle;
+ gBattleTypeFlags = BATTLE_TYPE_PALACE;
+ CreateBattleStartTask(GetWildBattleTransition(), 0);
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_WILD_BATTLES);
+}
+
+void ScrSpecial_StartMarowakBattle(void)
+{
+ ScriptContext2_Enable();
+ gMain.savedCallback = CB2_EndMarowakBattle;
+ if (CheckBagHasItem(ITEM_SILPH_SCOPE, 1))
+ {
+ gBattleTypeFlags = BATTLE_TYPE_GHOST | BATTLE_TYPE_LEGENDARY;
+ CreateMonWithGenderNatureLetter(gEnemyParty, SPECIES_MAROWAK, 30, 31, MON_FEMALE, NATURE_SERIOUS, 0);
+ }
+ else
+ {
+ gBattleTypeFlags = BATTLE_TYPE_GHOST;
+ }
+ CreateBattleStartTask(GetWildBattleTransition(), 0);
+ SetMonData(&gEnemyParty[0], MON_DATA_NICKNAME, gUnknown_841D148);
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_WILD_BATTLES);
+}
+
+void ScrSpecial_StartSouthernIslandBattle(void)
+{
+ ScriptContext2_Enable();
+ gMain.savedCallback = CB2_EndScriptedWildBattle;
+ gBattleTypeFlags = BATTLE_TYPE_LEGENDARY;
+ CreateBattleStartTask(GetWildBattleTransition(), 0);
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_WILD_BATTLES);
+}
+
+void Special_StartLegendaryBattle(void)
+{
+ u16 species;
+
+ ScriptContext2_Enable();
+ gMain.savedCallback = CB2_EndScriptedWildBattle;
+ gBattleTypeFlags = BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_ARENA;
+ species = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES);
+ switch (species)
+ {
+ case SPECIES_MEWTWO:
+ CreateBattleStartTask(B_TRANSITION_BLUR, MUS_VS_MYU2);
+ break;
+ case SPECIES_DEOXYS:
+ CreateBattleStartTask(B_TRANSITION_BLUR, MUS_VS_DEO);
+ break;
+ case SPECIES_MOLTRES:
+ case SPECIES_ARTICUNO:
+ case SPECIES_ZAPDOS:
+ case SPECIES_HO_OH:
+ case SPECIES_LUGIA:
+ CreateBattleStartTask(B_TRANSITION_BLUR, MUS_VS_DEN);
+ break;
+ default:
+ CreateBattleStartTask(B_TRANSITION_BLUR, MUS_BATTLE20);
+ break;
+ }
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_WILD_BATTLES);
+}
+
+void Special_StartGroudonKyogreBattle(void)
+{
+ ScriptContext2_Enable();
+ gMain.savedCallback = CB2_EndScriptedWildBattle;
+ gBattleTypeFlags = BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_KYOGRE_GROUDON;
+ if (gGameVersion == VERSION_FIRE_RED)
+ CreateBattleStartTask(B_TRANSITION_BLACK_DOODLES, MUS_BATTLE20);
+ else // pointless, exactly the same
+ CreateBattleStartTask(B_TRANSITION_BLACK_DOODLES, MUS_BATTLE20);
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_WILD_BATTLES);
+}
+
+void Special_StartRegiBattle(void)
+{
+ ScriptContext2_Enable();
+ gMain.savedCallback = CB2_EndScriptedWildBattle;
+ gBattleTypeFlags = BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_REGI;
+ CreateBattleStartTask(B_TRANSITION_BLUR, MUS_BATTLE20);
+ IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
+ IncrementGameStat(GAME_STAT_WILD_BATTLES);
+}
+
+// not used
+static void sub_807FAF8(void)
+{
+ LoadPlayerParty();
+ CB2_EndWildBattle();
+}
+
+// not used
+static void sub_807FB08(void)
+{
+ ScriptContext2_Enable();
+ FreezeEventObjects();
+ sub_805C780();
+ gMain.savedCallback = sub_807FAF8;
+ SavePlayerParty();
+ InitPokedudePartyAndOpponent();
+ CreateBattleStartTask(GetWildBattleTransition(), 0);
+}
+
+static void CB2_EndWildBattle(void)
+{
+ CpuFill16(0, (void *)BG_PLTT, BG_PLTT_SIZE);
+ ResetOamRange(0, 128);
+ if (IsPlayerDefeated(gBattleOutcome) == TRUE)
+ {
+ SetMainCallback2(CB2_WhiteOut);
+ }
+ else
+ {
+ SetMainCallback2(CB2_ReturnToField);
+ gFieldCallback = sub_807E3EC;
+ }
+}
+
+static void CB2_EndScriptedWildBattle(void)
+{
+ CpuFill16(0, (void *)BG_PLTT, BG_PLTT_SIZE);
+ ResetOamRange(0, 128);
+ if (IsPlayerDefeated(gBattleOutcome) == TRUE)
+ SetMainCallback2(CB2_WhiteOut);
+ else
+ SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
+}
+
+static void CB2_EndMarowakBattle(void)
+{
+ CpuFill16(0, (void *)BG_PLTT, BG_PLTT_SIZE);
+ ResetOamRange(0, 128);
+ if (IsPlayerDefeated(gBattleOutcome))
+ {
+ SetMainCallback2(CB2_WhiteOut);
+ }
+ else
+ {
+ if (gBattleOutcome == B_OUTCOME_WON)
+ gSpecialVar_Result = 0;
+ else
+ gSpecialVar_Result = 1;
+ SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
+ }
+}
+
+u8 BattleSetup_GetTerrainId(void)
+{
+ u16 tileBehavior;
+ s16 x, y;
+
+ PlayerGetDestCoords(&x, &y);
+ tileBehavior = MapGridGetMetatileBehaviorAt(x, y);
+ if (MetatileBehavior_IsTallGrass_2(tileBehavior))
+ return BATTLE_TERRAIN_GRASS;
+ if (MetatileBehavior_IsLongGrass(tileBehavior))
+ return BATTLE_TERRAIN_LONG_GRASS;
+ if (MetatileBehavior_IsSandOrDeepSand(tileBehavior))
+ return BATTLE_TERRAIN_SAND;
+ switch (gMapHeader.mapType)
+ {
+ case MAP_TYPE_TOWN:
+ case MAP_TYPE_CITY:
+ case MAP_TYPE_ROUTE:
+ break;
+ case MAP_TYPE_UNDERGROUND:
+ if (MetatileBehavior_IsIndoorEncounter(tileBehavior))
+ return BATTLE_TERRAIN_BUILDING;
+ if (MetatileBehavior_IsSurfable(tileBehavior))
+ return BATTLE_TERRAIN_POND;
+ return BATTLE_TERRAIN_CAVE;
+ case MAP_TYPE_INDOOR:
+ case MAP_TYPE_SECRET_BASE:
+ return BATTLE_TERRAIN_BUILDING;
+ case MAP_TYPE_UNDERWATER:
+ return BATTLE_TERRAIN_UNDERWATER;
+ case MAP_TYPE_OCEAN_ROUTE:
+ if (MetatileBehavior_IsSurfable(tileBehavior))
+ return BATTLE_TERRAIN_WATER;
+ return BATTLE_TERRAIN_PLAIN;
+ }
+ if (MetatileBehavior_IsDeepSemiDeepOrSplashingWater(tileBehavior))
+ return BATTLE_TERRAIN_WATER;
+ if (MetatileBehavior_IsSurfable(tileBehavior))
+ return BATTLE_TERRAIN_POND;
+ if (MetatileBehavior_IsMountain(tileBehavior))
+ return BATTLE_TERRAIN_MOUNTAIN;
+ if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING))
+ {
+ if (MetatileBehavior_GetBridgeType(tileBehavior))
+ return BATTLE_TERRAIN_POND;
+ if (MetatileBehavior_IsBridge(tileBehavior) == TRUE)
+ return BATTLE_TERRAIN_WATER;
+ }
+ return BATTLE_TERRAIN_PLAIN;
+}
+
+static u8 GetBattleTransitionTypeByMap(void)
+{
+ u16 tileBehavior;
+ s16 x, y;
+
+ PlayerGetDestCoords(&x, &y);
+ tileBehavior = MapGridGetMetatileBehaviorAt(x, y);
+ if (Overworld_GetFlashLevel())
+ return B_TRANSITION_HORIZONTAL_CORRUGATE;
+ if (!MetatileBehavior_IsSurfable(tileBehavior))
+ {
+ switch (gMapHeader.mapType)
+ {
+ case MAP_TYPE_UNDERGROUND:
+ return B_TRANSITION_DISTORTED_WAVE;
+ case MAP_TYPE_UNDERWATER:
+ return B_TRANSITION_BIG_POKEBALL;
+ default:
+ return B_TRANSITION_BLUR;
+ }
+ }
+ return B_TRANSITION_BIG_POKEBALL;
+}
+
+static u16 GetSumOfPlayerPartyLevel(u8 numMons)
+{
+ u8 sum = 0;
+ s32 i;
+
+ for (i = 0; i < PARTY_SIZE; ++i)
+ {
+ u32 species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2);
+
+ if (species != SPECIES_EGG && species != SPECIES_NONE && GetMonData(&gPlayerParty[i], MON_DATA_HP) != 0)
+ {
+ sum += GetMonData(&gPlayerParty[i], MON_DATA_LEVEL);
+ if (--numMons == 0)
+ break;
+ }
+ }
+ return sum;
+}
+
+static u8 GetSumOfEnemyPartyLevel(u16 opponentId, u8 numMons)
+{
+ u8 i;
+ u8 sum;
+ u32 count = numMons;
+
+ if (gTrainers[opponentId].partySize < count)
+ count = gTrainers[opponentId].partySize;
+ sum = 0;
+ switch (gTrainers[opponentId].partyFlags)
+ {
+ case 0:
+ {
+ const struct TrainerMonNoItemDefaultMoves *party;
+
+ party = gTrainers[opponentId].party.NoItemDefaultMoves;
+ for (i = 0; i < count; ++i)
+ sum += party[i].lvl;
+ }
+ break;
+ case F_TRAINER_PARTY_CUSTOM_MOVESET:
+ {
+ const struct TrainerMonNoItemCustomMoves *party;
+
+ party = gTrainers[opponentId].party.NoItemCustomMoves;
+ for (i = 0; i < count; ++i)
+ sum += party[i].lvl;
+ }
+ break;
+ case F_TRAINER_PARTY_HELD_ITEM:
+ {
+ const struct TrainerMonItemDefaultMoves *party;
+
+ party = gTrainers[opponentId].party.ItemDefaultMoves;
+ for (i = 0; i < count; ++i)
+ sum += party[i].lvl;
+ }
+ break;
+ case F_TRAINER_PARTY_CUSTOM_MOVESET | F_TRAINER_PARTY_HELD_ITEM:
+ {
+ const struct TrainerMonItemCustomMoves *party;
+
+ party = gTrainers[opponentId].party.ItemCustomMoves;
+ for (i = 0; i < count; ++i)
+ sum += party[i].lvl;
+ }
+ break;
+ }
+ return sum;
+}
+
+static u8 GetWildBattleTransition(void)
+{
+ u8 transitionType = GetBattleTransitionTypeByMap();
+ u8 enemyLevel = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL);
+ u8 playerLevel = GetSumOfPlayerPartyLevel(1);
+
+ if (enemyLevel < playerLevel)
+ return sBattleTransitionTable_Wild[transitionType][0];
+ else
+ return sBattleTransitionTable_Wild[transitionType][1];
+}
+
+static u8 GetTrainerBattleTransition(void)
+{
+ u8 minPartyCount;
+ u8 transitionType;
+ u8 enemyLevel;
+ u8 playerLevel;
+
+ if (gTrainerBattleOpponent_A == TRAINER_SECRET_BASE)
+ return B_TRANSITION_BLUE;
+ if (gTrainers[gTrainerBattleOpponent_A].trainerClass == CLASS_ELITE_FOUR_2)
+ {
+ if (gTrainerBattleOpponent_A == TRAINER_ELITE_FOUR_LORELEI || gTrainerBattleOpponent_A == TRAINER_ELITE_FOUR_LORELEI_2)
+ return B_TRANSITION_LORELEI;
+ if (gTrainerBattleOpponent_A == TRAINER_ELITE_FOUR_BRUNO || gTrainerBattleOpponent_A == TRAINER_ELITE_FOUR_BRUNO_2)
+ return B_TRANSITION_BRUNO;
+ if (gTrainerBattleOpponent_A == TRAINER_ELITE_FOUR_AGATHA || gTrainerBattleOpponent_A == TRAINER_ELITE_FOUR_AGATHA_2)
+ return B_TRANSITION_AGATHA;
+ if (gTrainerBattleOpponent_A == TRAINER_ELITE_FOUR_LANCE || gTrainerBattleOpponent_A == TRAINER_ELITE_FOUR_LANCE_2)
+ return B_TRANSITION_LANCE;
+ return B_TRANSITION_BLUE;
+ }
+ if (gTrainers[gTrainerBattleOpponent_A].trainerClass == CLASS_CHAMPION_2)
+ return B_TRANSITION_BLUE;
+ if (gTrainers[gTrainerBattleOpponent_A].doubleBattle == TRUE)
+ minPartyCount = 2; // double battles always at least have 2 pokemon.
+ else
+ minPartyCount = 1;
+ transitionType = GetBattleTransitionTypeByMap();
+ enemyLevel = GetSumOfEnemyPartyLevel(gTrainerBattleOpponent_A, minPartyCount);
+ playerLevel = GetSumOfPlayerPartyLevel(minPartyCount);
+ if (enemyLevel < playerLevel)
+ return sBattleTransitionTable_Trainer[transitionType][0];
+ else
+ return sBattleTransitionTable_Trainer[transitionType][1];
+}
+
+u8 sub_8080060(void)
+{
+ u8 enemyLevel = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL);
+ u8 playerLevel = GetSumOfPlayerPartyLevel(1);
+
+ if (enemyLevel < playerLevel)
+ return 4;
+ else
+ return 3;
+}
+
+static u32 TrainerBattleLoadArg32(const u8 *ptr)
+{
+ return T1_READ_32(ptr);
+}
+
+static u16 TrainerBattleLoadArg16(const u8 *ptr)
+{
+ return T1_READ_16(ptr);
+}
+
+static u8 TrainerBattleLoadArg8(const u8 *ptr)
+{
+ return T1_READ_8(ptr);
+}
+
+static u16 GetTrainerAFlag(void)
+{
+ return FLAG_TRAINER_FLAG_START + gTrainerBattleOpponent_A;
+}
+
+static bool32 IsPlayerDefeated(u32 battleOutcome)
+{
+ switch (battleOutcome)
+ {
+ case B_OUTCOME_LOST:
+ case B_OUTCOME_DREW:
+ return TRUE;
+ case B_OUTCOME_WON:
+ case B_OUTCOME_RAN:
+ case B_OUTCOME_PLAYER_TELEPORTED:
+ case B_OUTCOME_MON_FLED:
+ case B_OUTCOME_CAUGHT:
+ return FALSE;
+ default:
+ return FALSE;
+ }
+}
+
+static void InitTrainerBattleVariables(void)
+{
+ sTrainerBattleMode = 0;
+ gTrainerBattleOpponent_A = 0;
+ sTrainerEventObjectLocalId = 0;
+ sTrainerAIntroSpeech = NULL;
+ sTrainerADefeatSpeech = NULL;
+ sTrainerVictorySpeech = NULL;
+ sTrainerCannotBattleSpeech = NULL;
+ sTrainerBattleEndScript = NULL;
+ sTrainerABattleScriptRetAddr = NULL;
+ gUnknown_20386CC = 0;
+}
+
+static inline void SetU8(void *ptr, u8 value)
+{
+ *(u8 *)(ptr) = value;
+}
+
+static inline void SetU16(void *ptr, u16 value)
+{
+ *(u16 *)(ptr) = value;
+}
+
+static inline void SetU32(void *ptr, u32 value)
+{
+ *(u32 *)(ptr) = value;
+}
+
+static inline void SetPtr(const void *ptr, const void *value)
+{
+ *(const void **)(ptr) = value;
+}
+
+static void TrainerBattleLoadArgs(const struct TrainerBattleParameter *specs, const u8 *data)
+{
+ while (1)
+ {
+ switch (specs->ptrType)
+ {
+ case TRAINER_PARAM_LOAD_VAL_8BIT:
+ SetU8(specs->varPtr, TrainerBattleLoadArg8(data));
+ data += 1;
+ break;
+ case TRAINER_PARAM_LOAD_VAL_16BIT:
+ SetU16(specs->varPtr, TrainerBattleLoadArg16(data));
+ data += 2;
+ break;
+ case TRAINER_PARAM_LOAD_VAL_32BIT:
+ SetU32(specs->varPtr, TrainerBattleLoadArg32(data));
+ data += 4;
+ break;
+ case TRAINER_PARAM_CLEAR_VAL_8BIT:
+ SetU8(specs->varPtr, 0);
+ break;
+ case TRAINER_PARAM_CLEAR_VAL_16BIT:
+ SetU16(specs->varPtr, 0);
+ break;
+ case TRAINER_PARAM_CLEAR_VAL_32BIT:
+ SetU32(specs->varPtr, 0);
+ break;
+ case TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR:
+ SetPtr(specs->varPtr, data);
+ return;
+ }
+ ++specs;
+ }
+}
+
+static void SetMapVarsToTrainer(void)
+{
+ if (sTrainerEventObjectLocalId != 0)
+ {
+ gSpecialVar_LastTalked = sTrainerEventObjectLocalId;
+ gSelectedEventObject = GetFieldObjectIdByLocalIdAndMap(sTrainerEventObjectLocalId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup);
+ }
+}
+
+const u8 *BattleSetup_ConfigureTrainerBattle(const u8 *data)
+{
+ InitTrainerBattleVariables();
+ sTrainerBattleMode = TrainerBattleLoadArg8(data);
+ switch (sTrainerBattleMode)
+ {
+ case TRAINER_BATTLE_SINGLE_NO_INTRO_TEXT:
+ TrainerBattleLoadArgs(sOrdinaryNoIntroBattleParams, data);
+ return EventScript_DoTrainerBattle;
+ case TRAINER_BATTLE_DOUBLE:
+ TrainerBattleLoadArgs(sDoubleBattleParams, data);
+ SetMapVarsToTrainer();
+ return EventScript_TryDoDoubleTrainerBattle;
+ case TRAINER_BATTLE_CONTINUE_SCRIPT:
+ case TRAINER_BATTLE_CONTINUE_SCRIPT_NO_MUSIC:
+ TrainerBattleLoadArgs(sContinueScriptBattleParams, data);
+ SetMapVarsToTrainer();
+ return EventScript_TryDoNormalTrainerBattle;
+ case TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE:
+ case TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE_NO_MUSIC:
+ TrainerBattleLoadArgs(sContinueScriptDoubleBattleParams, data);
+ SetMapVarsToTrainer();
+ return EventScript_TryDoDoubleTrainerBattle;
+ case TRAINER_BATTLE_REMATCH_DOUBLE:
+ sub_811231C();
+ TrainerBattleLoadArgs(sDoubleBattleParams, data);
+ SetMapVarsToTrainer();
+ gTrainerBattleOpponent_A = GetRematchTrainerId(gTrainerBattleOpponent_A);
+ return EventScript_TryDoDoubleRematchBattle;
+ case TRAINER_BATTLE_REMATCH:
+ sub_811231C();
+ TrainerBattleLoadArgs(sOrdinaryBattleParams, data);
+ SetMapVarsToTrainer();
+ gTrainerBattleOpponent_A = GetRematchTrainerId(gTrainerBattleOpponent_A);
+ return EventScript_TryDoRematchBattle;
+ case TRAINER_BATTLE_TUTORIAL:
+ TrainerBattleLoadArgs(sTutorialBattleParams, data);
+ return EventScript_DoTrainerBattle;
+ default:
+ TrainerBattleLoadArgs(sOrdinaryBattleParams, data);
+ SetMapVarsToTrainer();
+ return EventScript_TryDoNormalTrainerBattle;
+ }
+}
+
+void ConfigureAndSetUpOneTrainerBattle(u8 trainerEventObjId, const u8 *trainerScript)
+{
+ gSelectedEventObject = trainerEventObjId;
+ gSpecialVar_LastTalked = gMapObjects[trainerEventObjId].localId;
+ BattleSetup_ConfigureTrainerBattle(trainerScript + 1);
+ ScriptContext1_SetupScript(gUnknown_81A4EB4);
+ ScriptContext2_Enable();
+}
+
+bool32 GetTrainerFlagFromScriptPointer(const u8 *data)
+{
+ u32 flag = TrainerBattleLoadArg16(data + 2);
+
+ return FlagGet(FLAG_TRAINER_FLAG_START + flag);
+}
+
+void SetUpTrainerMovement(void)
+{
+ struct MapObject *eventObject = &gMapObjects[gSelectedEventObject];
+
+ SetTrainerMovementType(eventObject, GetTrainerFacingDirectionMovementType(eventObject->facingDirection));
+}
+
+u8 ScrSpecial_GetTrainerBattleMode(void)
+{
+ return sTrainerBattleMode;
+}
+
+u16 sub_80803D8(void)
+{
+ return gUnknown_20386CC;
+}
+
+u16 ScrSpecial_HasTrainerBeenFought(void)
+{
+ return FlagGet(GetTrainerAFlag());
+}
+
+void SetBattledTrainerFlag(void)
+{
+ FlagSet(GetTrainerAFlag());
+}
+
+// not used
+static void SetBattledTrainerFlag2(void)
+{
+ FlagSet(GetTrainerAFlag());
+}
+
+bool8 HasTrainerBeenFought(u16 trainerId)
+{
+ return FlagGet(FLAG_TRAINER_FLAG_START + trainerId);
+}
+
+void SetTrainerFlag(u16 trainerId)
+{
+ FlagSet(FLAG_TRAINER_FLAG_START + trainerId);
+}
+
+void ClearTrainerFlag(u16 trainerId)
+{
+ FlagClear(FLAG_TRAINER_FLAG_START + trainerId);
+}
+
+void BattleSetup_StartTrainerBattle(void)
+{
+ gBattleTypeFlags = BATTLE_TYPE_TRAINER;
+ if (ScrSpecial_GetTrainerBattleMode() == TRAINER_BATTLE_TUTORIAL
+ && sub_80803D8() & 3)
+ gBattleTypeFlags |= BATTLE_TYPE_FIRST_BATTLE;
+ gMain.savedCallback = CB2_EndTrainerBattle;
+ DoTrainerBattle();
+ ScriptContext1_Stop();
+}
+
+static void CB2_EndTrainerBattle(void)
+{
+ if (sTrainerBattleMode == TRAINER_BATTLE_TUTORIAL)
+ {
+ if (IsPlayerDefeated(gBattleOutcome) == TRUE)
+ {
+ gSpecialVar_Result = 1;
+ if (gUnknown_20386CC & 1)
+ {
+ sp000_heal_pokemon();
+ }
+ else
+ {
+ SetMainCallback2(CB2_WhiteOut);
+ return;
+ }
+ SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
+ SetBattledTrainerFlag();
+ sub_81139BC();
+ }
+ else
+ {
+ gSpecialVar_Result = 0;
+ SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
+ SetBattledTrainerFlag();
+ sub_81139BC();
+ }
+
+ }
+ else
+ {
+ if (gTrainerBattleOpponent_A == TRAINER_SECRET_BASE)
+ {
+ SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
+ }
+ else if (IsPlayerDefeated(gBattleOutcome) == TRUE)
+ {
+ SetMainCallback2(CB2_WhiteOut);
+ }
+ else
+ {
+ SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
+ SetBattledTrainerFlag();
+ sub_81139BC();
+ }
+ }
+}
+
+static void CB2_EndRematchBattle(void)
+{
+ if (gTrainerBattleOpponent_A == TRAINER_SECRET_BASE)
+ {
+ SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
+ }
+ else if (IsPlayerDefeated(gBattleOutcome) == TRUE)
+ {
+ SetMainCallback2(CB2_WhiteOut);
+ }
+ else
+ {
+ SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
+ SetBattledTrainerFlag();
+ sub_810CDE8();
+ sub_81138F8();
+ }
+}
+
+void ScrSpecial_StartTrainerEyeRematch(void)
+{
+ gBattleTypeFlags = BATTLE_TYPE_TRAINER;
+ gMain.savedCallback = CB2_EndRematchBattle;
+ DoTrainerBattle();
+ ScriptContext1_Stop();
+}
+
+void ScrSpecial_ShowTrainerIntroSpeech(void)
+{
+ ShowFieldMessage(GetIntroSpeechOfApproachingTrainer());
+}
+
+const u8 *BattleSetup_GetScriptAddrAfterBattle(void)
+{
+ if (sTrainerBattleEndScript != NULL)
+ return sTrainerBattleEndScript;
+ else
+ return EventScript_1C555B;
+}
+
+const u8 *BattleSetup_GetTrainerPostBattleScript(void)
+{
+ if (sTrainerABattleScriptRetAddr != NULL)
+ return sTrainerABattleScriptRetAddr;
+ else
+ return EventScript_1C555B;
+}
+
+void ScrSpecial_ShowTrainerNonBattlingSpeech(void)
+{
+ ShowFieldMessage(GetTrainerCantBattleSpeech());
+}
+
+void PlayTrainerEncounterMusic(void)
+{
+ u16 music;
+
+ if (gUnknown_203ADFA != 2
+ && gUnknown_203ADFA != 3
+ && sTrainerBattleMode != TRAINER_BATTLE_CONTINUE_SCRIPT_NO_MUSIC
+ && sTrainerBattleMode != TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE_NO_MUSIC)
+ {
+ switch (GetTrainerEncounterMusicId(gTrainerBattleOpponent_A))
+ {
+ case TRAINER_ENCOUNTER_MUSIC_FEMALE:
+ case TRAINER_ENCOUNTER_MUSIC_GIRL:
+ case TRAINER_ENCOUNTER_MUSIC_TWINS:
+ music = MUS_SHOUJO;
+ break;
+ case TRAINER_ENCOUNTER_MUSIC_MALE:
+ case TRAINER_ENCOUNTER_MUSIC_INTENSE:
+ case TRAINER_ENCOUNTER_MUSIC_COOL:
+ case TRAINER_ENCOUNTER_MUSIC_SWIMMER:
+ case TRAINER_ENCOUNTER_MUSIC_ELITE_FOUR:
+ case TRAINER_ENCOUNTER_MUSIC_HIKER:
+ case TRAINER_ENCOUNTER_MUSIC_INTERVIEWER:
+ case TRAINER_ENCOUNTER_MUSIC_RICH:
+ music = MUS_SHOUNEN;
+ break;
+ default:
+ music = MUS_ROCKET;
+ break;
+ }
+ PlayNewMapMusic(music);
+ }
+}
+
+static const u8 *ReturnEmptyStringIfNull(const u8 *string)
+{
+ if (string == NULL)
+ return gString_Dummy;
+ else
+ return string;
+}
+
+static const u8 *GetIntroSpeechOfApproachingTrainer(void)
+{
+ return ReturnEmptyStringIfNull(sTrainerAIntroSpeech);
+}
+
+const u8 *GetTrainerALoseText(void)
+{
+ const u8 *string = sTrainerADefeatSpeech;
+
+ StringExpandPlaceholders(gStringVar4, ReturnEmptyStringIfNull(string));
+ return gStringVar4;
+}
+
+const u8 *GetTrainerWonSpeech(void)
+{
+ StringExpandPlaceholders(gStringVar4, ReturnEmptyStringIfNull(sTrainerVictorySpeech));
+ return gStringVar4;
+}
+
+static const u8 *GetTrainerCantBattleSpeech(void)
+{
+ return ReturnEmptyStringIfNull(sTrainerCannotBattleSpeech);
+}