diff options
Diffstat (limited to 'src/battle_setup.c')
-rw-r--r-- | src/battle_setup.c | 1060 |
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); +} |