diff --git a/asm/macros/battle_frontier/battle_tower.inc b/asm/macros/battle_frontier/battle_tower.inc index 6c9e606dde..f3abc0cfd4 100644 --- a/asm/macros/battle_frontier/battle_tower.inc +++ b/asm/macros/battle_frontier/battle_tower.inc @@ -98,14 +98,13 @@ waitstate .endm - .macro multi_do type:req, partnerId:req + .macro multi_do type:req special ReducePlayerPartyToSelectedMons setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SET_DATA setvar VAR_0x8005, FRONTIER_DATA_SELECTED_MON_ORDER special CallFrontierUtilFunc @ saves the mon order, so the non-selected mons get restored afterwards setvar VAR_0x8004, SPECIAL_BATTLE_MULTI setvar VAR_0x8005, \type | MULTI_BATTLE_CHOOSE_MONS - setvar VAR_0x8006, \partnerId special DoSpecialTrainerBattle waitstate setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SAVE_PARTY @@ -115,26 +114,26 @@ .macro multi_2_vs_2 trainer1Id:req, trainer1LoseText:req, trainer2Id:req, trainer2LoseText:req, partnerId:req special SavePlayerParty - trainerbattle TRAINER_BATTLE_SET_TRAINERS_FOR_MULTI_BATTLE, OBJ_ID_NONE, \trainer1Id, NULL, \trainer1LoseText, NULL, OBJ_ID_NONE, \trainer2Id, NULL, \trainer2LoseText, NULL, NULL, NULL, FALSE, TRUE, FALSE, FALSE - multi_do MULTI_BATTLE_2_VS_2, \partnerId + setmultitrainerbattle \trainer1Id, \trainer1LoseText, \trainer2Id, \trainer2LoseText, \partnerId + multi_do MULTI_BATTLE_2_VS_2 .endm .macro multi_2_vs_1 trainer1Id:req, trainer1LoseText:req, partnerId:req special SavePlayerParty - trainerbattle TRAINER_BATTLE_SET_TRAINERS_FOR_MULTI_BATTLE, OBJ_ID_NONE, \trainer1Id, NULL, \trainer1LoseText, NULL, OBJ_ID_NONE, TRAINER_NONE, NULL, NULL, NULL, NULL, NULL, FALSE, TRUE, FALSE, FALSE - multi_do MULTI_BATTLE_2_VS_1, \partnerId + setmultitrainerbattle \trainer1Id, \trainer1LoseText, TRAINER_NONE, NULL, \partnerId + multi_do MULTI_BATTLE_2_VS_1 .endm @ Wild mons need to be assigned to gEnemyParty 0 and 3 slots, other slots need to be cleared out. .macro multi_wild partnerId:req special SavePlayerParty - multi_do MULTI_BATTLE_2_VS_WILD, \partnerId + setmultitrainerbattle TRAINER_NONE, NULL, TRAINER_NONE, NULL, \partnerId + multi_do MULTI_BATTLE_2_VS_WILD .endm - .macro multi_do_fixed type:req, partnerId:req + .macro multi_do_fixed type:req setvar VAR_0x8004, SPECIAL_BATTLE_MULTI setvar VAR_0x8005, \type - setvar VAR_0x8006, \partnerId special DoSpecialTrainerBattle waitstate setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SAVE_PARTY @@ -144,18 +143,19 @@ .macro multi_fixed_2_vs_2 trainer1Id:req, trainer1LoseText:req, trainer2Id:req, trainer2LoseText:req, partnerId:req special SavePlayerParty - trainerbattle TRAINER_BATTLE_SET_TRAINERS_FOR_MULTI_BATTLE, OBJ_ID_NONE, \trainer1Id, NULL, \trainer1LoseText, NULL, OBJ_ID_NONE, \trainer2Id, NULL, \trainer2LoseText, NULL, NULL, NULL, FALSE, TRUE, FALSE, FALSE - multi_do_fixed MULTI_BATTLE_2_VS_2, \partnerId + setmultitrainerbattle \trainer1Id, \trainer1LoseText, \trainer2Id, \trainer2LoseText, \partnerId + multi_do_fixed MULTI_BATTLE_2_VS_2 .endm .macro multi_fixed_2_vs_1 trainer1Id:req, trainer1LoseText:req, partnerId:req special SavePlayerParty - trainerbattle TRAINER_BATTLE_SET_TRAINERS_FOR_MULTI_BATTLE, OBJ_ID_NONE, \trainer1Id, NULL, \trainer1LoseText, NULL, OBJ_ID_NONE, TRAINER_NONE, NULL, NULL, NULL, NULL, NULL, FALSE, TRUE, FALSE, FALSE - multi_do_fixed MULTI_BATTLE_2_VS_1, \partnerId + setmultitrainerbattle \trainer1Id, \trainer1LoseText, TRAINER_NONE, NULL, \partnerId + multi_do_fixed MULTI_BATTLE_2_VS_1 .endm @ Wild mons need to be assigned to gEnemyParty 0 and 3 slots, other slots need to be cleared out. .macro multi_fixed_wild partnerId:req special SavePlayerParty - multi_do_fixed MULTI_BATTLE_2_VS_WILD, \partnerId + setmultitrainerbattle TRAINER_NONE, NULL, TRAINER_NONE, NULL, \partnerId + multi_do_fixed MULTI_BATTLE_2_VS_WILD .endm diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 874d026338..22e2c9b164 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2599,6 +2599,27 @@ cant_see_if 5 .endm + .macro setmultitrainerbattle trainer_a:req, lose_text_a:req, trainer_b:req, lose_text_b:req, partnerId:req + callnative SetMultiTrainerBattle + .2byte \trainer_a + .4byte \lose_text_a + .2byte \trainer_b + .4byte \lose_text_b + .2byte \partnerId + .endm + + @ facility version of `trainerbattle` macro. Used in Battle Pyramid and Trainer Hill + .macro facilitytrainerbattle facility:req + callnative FacilityTrainerBattle + .byte \facility + .endm + + @ immediately starts a battle of the given facility + .macro dofacilitytrainerbattle facility:req + callnative DoFacilityTrainerBattle + .byte \facility + .endm + @ Follower NPCs @ Sets an existing NPC up to follow the player. diff --git a/data/event_scripts.s b/data/event_scripts.s index bc9388be64..4fe74213f9 100644 --- a/data/event_scripts.s +++ b/data/event_scripts.s @@ -13,6 +13,7 @@ #include "constants/battle_pike.h" #include "constants/battle_pyramid.h" #include "constants/battle_setup.h" +#include "constants/battle_special.h" #include "constants/battle_tent.h" #include "constants/battle_tower.h" #include "constants/berry.h" diff --git a/data/maps/BattleFrontier_BattleArenaBattleRoom/scripts.inc b/data/maps/BattleFrontier_BattleArenaBattleRoom/scripts.inc index 1dec6328d6..725783a1ec 100644 --- a/data/maps/BattleFrontier_BattleArenaBattleRoom/scripts.inc +++ b/data/maps/BattleFrontier_BattleArenaBattleRoom/scripts.inc @@ -364,9 +364,7 @@ BattleFrontier_BattleArenaBattleRoom_EventScript_DoArenaBattle:: setvar VAR_TEMP_2, 0 frontier_set FRONTIER_DATA_RECORD_DISABLED, FALSE special HealPlayerParty - setvar VAR_0x8004, SPECIAL_BATTLE_ARENA - setvar VAR_0x8005, 0 - special DoSpecialTrainerBattle + dofacilitytrainerbattle FACILITY_BATTLE_ARENA waitstate frontier_restorehelditems special HealPlayerParty diff --git a/data/maps/BattleFrontier_BattleDomeBattleRoom/scripts.inc b/data/maps/BattleFrontier_BattleDomeBattleRoom/scripts.inc index 9f5a5f4d77..bcd2a482aa 100644 --- a/data/maps/BattleFrontier_BattleDomeBattleRoom/scripts.inc +++ b/data/maps/BattleFrontier_BattleDomeBattleRoom/scripts.inc @@ -445,10 +445,8 @@ BattleFrontier_BattleDomeBattleRoom_EventScript_TuckerDraw:: BattleFrontier_BattleDomeBattleRoom_EventScript_DoDomeBattle:: frontier_set FRONTIER_DATA_RECORD_DISABLED, FALSE special HealPlayerParty - setvar VAR_0x8004, SPECIAL_BATTLE_DOME - setvar VAR_0x8005, 0 setvar VAR_TEMP_9, 1 - special DoSpecialTrainerBattle + dofacilitytrainerbattle FACILITY_BATTLE_DOME waitstate setvar VAR_TEMP_9, 0 dome_restorehelditems diff --git a/data/maps/BattleFrontier_BattleFactoryBattleRoom/scripts.inc b/data/maps/BattleFrontier_BattleFactoryBattleRoom/scripts.inc index e6da4070f7..80ac23075e 100644 --- a/data/maps/BattleFrontier_BattleFactoryBattleRoom/scripts.inc +++ b/data/maps/BattleFrontier_BattleFactoryBattleRoom/scripts.inc @@ -80,9 +80,7 @@ BattleFrontier_BattleFactoryBattleRoom_EventScript_BattleOpponent:: closemessage frontier_set FRONTIER_DATA_RECORD_DISABLED, FALSE special HealPlayerParty - setvar VAR_0x8004, SPECIAL_BATTLE_FACTORY - setvar VAR_0x8005, 0 - special DoSpecialTrainerBattle + dofacilitytrainerbattle FRONTIER_FACILITY_FACTORY waitstate switch VAR_RESULT case 1, BattleFrontier_BattleFactoryBattleRoom_EventScript_DefeatedOpponent @@ -171,9 +169,7 @@ BattleFrontier_BattleFactoryBattleRoom_EventScript_DoNolandBattle:: closemessage frontier_set FRONTIER_DATA_RECORD_DISABLED, FALSE special HealPlayerParty - setvar VAR_0x8004, SPECIAL_BATTLE_FACTORY - setvar VAR_0x8005, 0 - special DoSpecialTrainerBattle + dofacilitytrainerbattle FACILITY_BATTLE_FACTORY waitstate return diff --git a/data/maps/BattleFrontier_BattlePalaceBattleRoom/scripts.inc b/data/maps/BattleFrontier_BattlePalaceBattleRoom/scripts.inc index 7223b14f3d..4c4b01c592 100644 --- a/data/maps/BattleFrontier_BattlePalaceBattleRoom/scripts.inc +++ b/data/maps/BattleFrontier_BattlePalaceBattleRoom/scripts.inc @@ -274,9 +274,7 @@ BattleFrontier_BattlePalaceBattleRoom_EventScript_DoPalaceBattle:: setvar VAR_TEMP_2, 0 frontier_set FRONTIER_DATA_RECORD_DISABLED, FALSE special HealPlayerParty - setvar VAR_0x8004, SPECIAL_BATTLE_PALACE - setvar VAR_0x8005, 0 - special DoSpecialTrainerBattle + dofacilitytrainerbattle FACILITY_BATTLE_PALACE waitstate frontier_restorehelditems special HealPlayerParty diff --git a/data/maps/BattleFrontier_BattlePikeRoomNormal/scripts.inc b/data/maps/BattleFrontier_BattlePikeRoomNormal/scripts.inc index 44da68a6d7..fca4949818 100644 --- a/data/maps/BattleFrontier_BattlePikeRoomNormal/scripts.inc +++ b/data/maps/BattleFrontier_BattlePikeRoomNormal/scripts.inc @@ -31,9 +31,7 @@ BattleFrontier_BattlePikeRoomNormal_EventScript_EnterSingleBattleRoom:: waitmessage closemessage releaseall - setvar VAR_0x8004, SPECIAL_BATTLE_PIKE_SINGLE - setvar VAR_0x8005, 0 - special DoSpecialTrainerBattle + dofacilitytrainerbattle FACILITY_BATTLE_PIKE_SINGLE waitstate switch VAR_RESULT case 1, BattleFrontier_BattlePikeRoomNormal_EventScript_WonSingleBattle @@ -61,9 +59,7 @@ BattleFrontier_BattlePikeRoomNormal_EventScript_EnterHardBattleRoom:: waitmessage closemessage releaseall - setvar VAR_0x8004, SPECIAL_BATTLE_PIKE_SINGLE - setvar VAR_0x8005, 0 - special DoSpecialTrainerBattle + dofacilitytrainerbattle FACILITY_BATTLE_PIKE_SINGLE waitstate switch VAR_RESULT case 1, BattleFrontier_BattlePikeRoomNormal_EventScript_WonHardBattle @@ -252,9 +248,7 @@ BattleFrontier_BattlePikeRoomNormal_EventScript_EnterDoubleBattleRoom:: pike_gettrainerintro 1 msgbox gStringVar4, MSGBOX_DEFAULT closemessage - setvar VAR_0x8004, SPECIAL_BATTLE_PIKE_DOUBLE - setvar VAR_0x8005, 0 - special DoSpecialTrainerBattle + dofacilitytrainerbattle FACILITY_BATTLE_PIKE_DOUBLE waitstate switch VAR_RESULT case 1, BattleFrontier_BattlePikeRoomNormal_EventScript_WonDoubleBattle @@ -380,9 +374,7 @@ BattleFrontier_BattlePikeRoomNormal_EventScript_DoPikeQueenBattle:: closemessage applymovement LOCALID_PLAYER, BattleFrontier_BattlePikeRoomNormal_Movement_PlayerWalkUp2 waitmovement 0 - setvar VAR_0x8004, SPECIAL_BATTLE_PIKE_SINGLE - setvar VAR_0x8005, 0 - special DoSpecialTrainerBattle + dofacilitytrainerbattle FACILITY_BATTLE_PIKE_SINGLE waitstate return diff --git a/data/maps/BattleFrontier_BattlePyramidFloor/scripts.inc b/data/maps/BattleFrontier_BattlePyramidFloor/scripts.inc index 791f29f6e7..a9473d8750 100644 --- a/data/maps/BattleFrontier_BattlePyramidFloor/scripts.inc +++ b/data/maps/BattleFrontier_BattlePyramidFloor/scripts.inc @@ -105,7 +105,7 @@ BattlePyramid_WarpToTop:: @ TRAINER_PHILLIP is used as a placeholder BattlePyramid_TrainerBattle:: - trainerbattle TRAINER_BATTLE_HILL, LOCALID_NONE, TRAINER_PHILLIP, BattleFacility_TrainerBattle_PlaceholderText, BattleFacility_TrainerBattle_PlaceholderText, NULL, LOCALID_NONE, TRAINER_NONE, NULL, NULL, NULL, NULL, NULL, FALSE, FALSE, FALSE, FALSE + facilitytrainerbattle FACILITY_BATTLE_PYRAMID pyramid_showhint waitmessage waitbuttonpress diff --git a/data/maps/BattleFrontier_BattlePyramidTop/scripts.inc b/data/maps/BattleFrontier_BattlePyramidTop/scripts.inc index 3bf7b7b17e..db2add717c 100644 --- a/data/maps/BattleFrontier_BattlePyramidTop/scripts.inc +++ b/data/maps/BattleFrontier_BattlePyramidTop/scripts.inc @@ -180,9 +180,7 @@ BattleFrontier_BattlePyramidTop_EventScript_DefeatedBrandonGold:: BattleFrontier_BattlePyramidTop_EventScript_DoBrandonBattle:: closemessage - setvar VAR_0x8004, SPECIAL_BATTLE_PYRAMID - setvar VAR_0x8005, 0 - special DoSpecialTrainerBattle + dofacilitytrainerbattle FACILITY_BATTLE_PYRAMID waitstate return diff --git a/data/maps/BattleFrontier_BattleTowerBattleRoom/scripts.inc b/data/maps/BattleFrontier_BattleTowerBattleRoom/scripts.inc index bfa94f04b8..24caccc783 100644 --- a/data/maps/BattleFrontier_BattleTowerBattleRoom/scripts.inc +++ b/data/maps/BattleFrontier_BattleTowerBattleRoom/scripts.inc @@ -296,9 +296,7 @@ BattleFrontier_BattleTowerBattleRoom_EventScript_DoTowerBattle:: setvar VAR_TEMP_2, 0 frontier_set FRONTIER_DATA_RECORD_DISABLED, FALSE special HealPlayerParty - setvar VAR_0x8004, SPECIAL_BATTLE_TOWER - setvar VAR_0x8005, 0 - special DoSpecialTrainerBattle + dofacilitytrainerbattle FACILITY_BATTLE_TOWER waitstate copyvar VAR_0x8004, VAR_FRONTIER_BATTLE_MODE goto_if_eq VAR_0x8004, FRONTIER_MODE_LINK_MULTIS, BattleFrontier_BattleTowerBattleRoom_EventScript_EndTowerBattle diff --git a/data/maps/SlateportCity_BattleTentBattleRoom/scripts.inc b/data/maps/SlateportCity_BattleTentBattleRoom/scripts.inc index 78d5527311..1f3dbf2e6a 100644 --- a/data/maps/SlateportCity_BattleTentBattleRoom/scripts.inc +++ b/data/maps/SlateportCity_BattleTentBattleRoom/scripts.inc @@ -57,9 +57,7 @@ SlateportCity_BattleTentBattleRoom_EventScript_EnterRoom:: waitmessage closemessage special HealPlayerParty - setvar VAR_0x8004, SPECIAL_BATTLE_FACTORY - setvar VAR_0x8005, 0 - special DoSpecialTrainerBattle + dofacilitytrainerbattle FACILITY_BATTLE_PYRAMID waitstate switch VAR_RESULT case 1, SlateportCity_BattleTentBattleRoom_EventScript_DefeatedOpponent diff --git a/data/scripts/trainer_hill.inc b/data/scripts/trainer_hill.inc index ece0b01ee2..7ea5c6d774 100644 --- a/data/scripts/trainer_hill.inc +++ b/data/scripts/trainer_hill.inc @@ -60,7 +60,7 @@ TrainerHill_1F_Movement_SetInvisible:: @ TRAINER_PHILLIP is an actual Trainer on the SS Tidal, but is used as a placeholder here TrainerHill_EventScript_TrainerBattle:: - trainerbattle TRAINER_BATTLE_HILL, LOCALID_NONE, TRAINER_PHILLIP, BattleFacility_TrainerBattle_PlaceholderText, BattleFacility_TrainerBattle_PlaceholderText, NULL, LOCALID_NONE, TRAINER_NONE, NULL, NULL, NULL, NULL, NULL, FALSE, FALSE, FALSE, FALSE + facilitytrainerbattle FACILITY_BATTLE_TRAINER_HILL trainerhill_postbattletext waitmessage waitbuttonpress diff --git a/include/battle_dome.h b/include/battle_dome.h index b7572451ca..d794741279 100644 --- a/include/battle_dome.h +++ b/include/battle_dome.h @@ -1,6 +1,8 @@ #ifndef GUARD_BATTLE_DOME_H #define GUARD_BATTLE_DOME_H +#include "constants/battle_dome.h" + int GetDomeTrainerSelectedMons(u16 tournamentTrainerId); int TrainerIdToDomeTournamentId(u16 trainerId); diff --git a/include/battle_factory.h b/include/battle_factory.h index c266cc32c5..96f65fc9d7 100644 --- a/include/battle_factory.h +++ b/include/battle_factory.h @@ -8,5 +8,6 @@ void FillFactoryBrainParty(void); u8 GetNumPastRentalsRank(u8 battleMode, u8 lvlMode); u64 GetAiScriptsInBattleFactory(void); void SetMonMoveAvoidReturn(struct Pokemon *mon, u16 moveArg, u8 moveSlot); +void FillFactoryTrainerParty(void); #endif // GUARD_BATTLE_FACTORY_H diff --git a/include/battle_frontier.h b/include/battle_frontier.h new file mode 100644 index 0000000000..71461057b2 --- /dev/null +++ b/include/battle_frontier.h @@ -0,0 +1,32 @@ +#ifndef GUARD_BATTLE_FRONTIER_H +#define GUARD_BATTLE_FRONTIER_H + +#include "data.h" +#include "script.h" + +struct BattleFrontierTrainer +{ + u8 facilityClass; + u8 filler1[3]; + u8 trainerName[PLAYER_NAME_LENGTH + 1]; + u16 speechBefore[EASY_CHAT_BATTLE_WORDS_COUNT]; + u16 speechWin[EASY_CHAT_BATTLE_WORDS_COUNT]; + u16 speechLose[EASY_CHAT_BATTLE_WORDS_COUNT]; + const u16 *monSet; +}; + +// Temporary storage for monIds of the opponent team +// during team generation in battle factory and similar facilities. +extern u16 gFrontierTempParty[MAX_FRONTIER_PARTY_SIZE]; + +extern const struct BattleFrontierTrainer *gFacilityTrainers; +extern const struct TrainerMon *gFacilityTrainerMons; +extern const struct BattleFrontierTrainer gBattleFrontierTrainers[]; +extern const struct TrainerMon gBattleFrontierMons[]; + +void DoFacilityTrainerBattle(struct ScriptContext *ctx); +void FillFrontierTrainerParty(u8 monsCount); +void FillFrontierTrainersParties(u8 monsCount); +void CreateFacilityMon(const struct TrainerMon *fmon, u16 level, u8 fixedIV, u32 otID, u32 flags, struct Pokemon *dst); + +#endif // GUARD_BATTLE_FRONTIER_H diff --git a/include/battle_partner.h b/include/battle_partner.h new file mode 100644 index 0000000000..75e97a667f --- /dev/null +++ b/include/battle_partner.h @@ -0,0 +1,11 @@ +#ifndef BATTLE_PARTNER_H +#define BATTLE_PARTNER_H + +#include "difficulty.h" +#include "constants/battle_partner.h" + +extern const struct Trainer gBattlePartners[DIFFICULTY_COUNT][PARTNER_COUNT]; + +void FillPartnerParty(u16 trainerId); + +#endif // BATTLE_PARTNER_H diff --git a/include/battle_setup.h b/include/battle_setup.h index 689d163696..6793b37fb6 100644 --- a/include/battle_setup.h +++ b/include/battle_setup.h @@ -42,7 +42,6 @@ typedef union PACKED TrainerBattleParameter u8 data[sizeof(struct _TrainerBattleParameter)]; } TrainerBattleParameter; - extern const struct RematchTrainer gRematchTable[REMATCH_TABLE_ENTRIES]; extern TrainerBattleParameter gTrainerBattleParameter; @@ -70,6 +69,7 @@ void ResetTrainerOpponentIds(void); void SetMapVarsToTrainerA(void); void SetMapVarsToTrainerB(void); const u8 *BattleSetup_ConfigureTrainerBattle(const u8 *data); +const u8* BattleSetup_ConfigureFacilityTrainerBattle(u8 facility, const u8* scriptEndPtr); void ConfigureAndSetUpOneTrainerBattle(u8 trainerObjEventId, const u8 *trainerScript); void ConfigureTwoTrainersBattle(u8 trainerObjEventId, const u8 *trainerScript); void SetUpTwoTrainersBattle(void); @@ -107,6 +107,7 @@ void TrainerBattleLoadArgs(const u8 *data); void TrainerBattleLoadArgsTrainerA(const u8 *data); void TrainerBattleLoadArgsTrainerB(const u8 *data); void TrainerBattleLoadArgsSecondTrainer(const u8 *data); +void InitTrainerBattleParameter(void); void DoStandardWildBattle_Debug(void); void BattleSetup_StartTrainerBattle_Debug(void); diff --git a/include/battle_special.h b/include/battle_special.h new file mode 100644 index 0000000000..90f6bfc320 --- /dev/null +++ b/include/battle_special.h @@ -0,0 +1,13 @@ +#ifndef GUARD_BATTLE_SPECIAL_H +#define GUARD_BATTLE_SPECIAL_H + +void DoSpecialTrainerBattle(void); +void SetEReaderTrainerGfxId(void); +u8 GetEreaderTrainerFrontSpriteId(void); +enum TrainerClassID GetEreaderTrainerClassId(void); +void GetEreaderTrainerName(u8 *dst); +void ValidateEReaderTrainer(void); +void ClearEReaderTrainer(struct BattleTowerEReaderTrainer *ereaderTrainer); +void CopyEReaderTrainerGreeting(void); + +#endif // GUARD_BATTLE_SPECIAL_H diff --git a/include/battle_tower.h b/include/battle_tower.h index 5887c07d9b..901eb640e1 100644 --- a/include/battle_tower.h +++ b/include/battle_tower.h @@ -2,6 +2,7 @@ #define GUARD_BATTLE_TOWER_H #include "data.h" +#include "battle_frontier.h" struct RSBattleTowerRecord { @@ -15,68 +16,27 @@ struct RSBattleTowerRecord /*0xA0*/ u32 checksum; }; -struct BattleFrontierTrainer -{ - u8 facilityClass; - u8 filler1[3]; - u8 trainerName[PLAYER_NAME_LENGTH + 1]; - u16 speechBefore[EASY_CHAT_BATTLE_WORDS_COUNT]; - u16 speechWin[EASY_CHAT_BATTLE_WORDS_COUNT]; - u16 speechLose[EASY_CHAT_BATTLE_WORDS_COUNT]; - const u16 *monSet; -}; - extern const u8 gTowerMaleFacilityClasses[30]; extern const u16 gTowerMaleTrainerGfxIds[30]; extern const u8 gTowerFemaleFacilityClasses[20]; extern const u16 gTowerFemaleTrainerGfxIds[20]; -extern const struct TrainerMon gBattleFrontierMons[]; -extern const struct BattleFrontierTrainer gBattleFrontierTrainers[]; extern const struct TrainerMon gSlateportBattleTentMons[]; extern const struct BattleFrontierTrainer gSlateportBattleTentTrainers[]; -// Temporary storage for monIds of the opponent team -// during team generation in battle factory and similar facilities. -extern u16 gFrontierTempParty[]; - -extern const struct BattleFrontierTrainer *gFacilityTrainers; -extern const struct TrainerMon *gFacilityTrainerMons; - void CallBattleTowerFunc(void); -u16 GetRandomScaledFrontierTrainerId(u8 challengeNum, u8 battleNum); -void SetBattleFacilityTrainerGfxId(u16 trainerId, u8 tempVarId); -void SetEReaderTrainerGfxId(void); -u16 GetBattleFacilityTrainerGfxId(u16 trainerId); void PutNewBattleTowerRecord(struct EmeraldBattleTowerRecord *newRecordEm); -u8 GetFrontierTrainerFrontSpriteId(u16 trainerId); -enum TrainerClassID GetFrontierOpponentClass(u16 trainerId); -void GetFrontierTrainerName(u8 *dst, u16 trainerId); -void FillFrontierTrainerParty(u8 monsCount); -void FillFrontierTrainersParties(u8 monsCount); -u16 GetRandomFrontierMonFromSet(u16 trainerId); -void FrontierSpeechToString(const u16 *words); -void DoSpecialTrainerBattle(void); void CalcEmeraldBattleTowerChecksum(struct EmeraldBattleTowerRecord *record); void CalcRubyBattleTowerChecksum(struct RSBattleTowerRecord *record); u16 GetCurrentBattleTowerWinStreak(u8 lvlMode, u8 battleMode); -u8 GetEreaderTrainerFrontSpriteId(void); -enum TrainerClassID GetEreaderTrainerClassId(void); -void GetEreaderTrainerName(u8 *dst); -void ValidateEReaderTrainer(void); -void ClearEReaderTrainer(struct BattleTowerEReaderTrainer *ereaderTrainer); -void CopyEReaderTrainerGreeting(void); void TryHideBattleTowerReporter(void); bool32 RubyBattleTowerRecordToEmerald(struct RSBattleTowerRecord *src, struct EmeraldBattleTowerRecord *dst); bool32 EmeraldBattleTowerRecordToRuby(struct EmeraldBattleTowerRecord *src, struct RSBattleTowerRecord *dst); void CalcApprenticeChecksum(struct Apprentice *apprentice); void GetBattleTowerTrainerLanguage(u8 *dst, u16 trainerId); -u8 SetFacilityPtrsGetLevel(void); -u8 GetFrontierEnemyMonLevel(u8 lvlMode); -s32 GetHighestLevelInPlayerParty(void); -u16 FacilityClassToGraphicsId(u8 facilityClass); +u8 SetTentPtrsGetLevel(void); bool32 ValidateBattleTowerRecord(u8 recordId); // unused void TrySetLinkBattleTowerEnemyPartyLevel(void); -void CreateFacilityMon(const struct TrainerMon *fmon, u16 level, u8 fixedIV, u32 otID, u32 flags, struct Pokemon *dst); -void FillPartnerParty(u16 trainerId); +void FillTentTrainerParty(u8 monsCount); + #endif //GUARD_BATTLE_TOWER_H diff --git a/include/constants/battle_frontier.h b/include/constants/battle_frontier.h index 7b3c77cc8f..43be80dc86 100644 --- a/include/constants/battle_frontier.h +++ b/include/constants/battle_frontier.h @@ -33,17 +33,20 @@ #define CHALLENGE_STATUS_LOST 4 // Special trainer battles. -#define SPECIAL_BATTLE_TOWER 0 #define SPECIAL_BATTLE_SECRET_BASE 1 #define SPECIAL_BATTLE_EREADER 2 -#define SPECIAL_BATTLE_DOME 3 -#define SPECIAL_BATTLE_PALACE 4 -#define SPECIAL_BATTLE_ARENA 5 -#define SPECIAL_BATTLE_FACTORY 6 -#define SPECIAL_BATTLE_PIKE_SINGLE 7 -#define SPECIAL_BATTLE_PIKE_DOUBLE 8 -#define SPECIAL_BATTLE_PYRAMID 9 -#define SPECIAL_BATTLE_MULTI 10 +#define SPECIAL_BATTLE_MULTI 3 + +// Facility trainer Battles +#define FACILITY_BATTLE_TOWER 0 +#define FACILITY_BATTLE_DOME 1 +#define FACILITY_BATTLE_PALACE 2 +#define FACILITY_BATTLE_ARENA 3 +#define FACILITY_BATTLE_FACTORY 4 +#define FACILITY_BATTLE_PIKE_SINGLE 5 +#define FACILITY_BATTLE_PIKE_DOUBLE 6 +#define FACILITY_BATTLE_PYRAMID 7 +#define FACILITY_BATTLE_TRAINER_HILL 8 #define MAX_BATTLE_FRONTIER_POINTS 9999 #define MAX_STREAK 9999 diff --git a/include/constants/battle_setup.h b/include/constants/battle_setup.h index 91c89d0631..6202c4a1b6 100644 --- a/include/constants/battle_setup.h +++ b/include/constants/battle_setup.h @@ -10,9 +10,6 @@ #define TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE 6 #define TRAINER_BATTLE_REMATCH_DOUBLE 7 #define TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE_NO_MUSIC 8 -#define TRAINER_BATTLE_PYRAMID 9 -#define TRAINER_BATTLE_SET_TRAINERS_FOR_MULTI_BATTLE 10 -#define TRAINER_BATTLE_HILL 12 #define TRAINER_BATTLE_TWO_TRAINERS_NO_INTRO 13 #endif // GUARD_CONSTANTS_BATTLE_SETUP_H diff --git a/include/constants/battle_special.h b/include/constants/battle_special.h new file mode 100644 index 0000000000..82d70a8efe --- /dev/null +++ b/include/constants/battle_special.h @@ -0,0 +1,10 @@ +#ifndef GUARD_CONSTANTS_BATTLE_SPECIAL_H +#define GUARD_CONSTANTS_BATTLE_SPECIAL_H + +// Ids for special multi battle types +#define MULTI_BATTLE_2_VS_2 0 +#define MULTI_BATTLE_2_VS_WILD 1 +#define MULTI_BATTLE_2_VS_1 2 +#define MULTI_BATTLE_CHOOSE_MONS 0x80 + +#endif // GUARD_CONSTANTS_BATTLE_SPECIAL_H diff --git a/include/constants/battle_tower.h b/include/constants/battle_tower.h index 47858e807c..002422d850 100644 --- a/include/constants/battle_tower.h +++ b/include/constants/battle_tower.h @@ -38,10 +38,4 @@ #define BATTLE_TOWER_LINKSTAT_MEMBER_RETIRE 2 #define BATTLE_TOWER_LINKSTAT_LEADER_RETIRE 3 -// Ids for special multi battle types -#define MULTI_BATTLE_2_VS_2 0 -#define MULTI_BATTLE_2_VS_WILD 1 -#define MULTI_BATTLE_2_VS_1 2 -#define MULTI_BATTLE_CHOOSE_MONS 0x80 - #endif //GUARD_CONSTANTS_BATTLE_TOWER_H diff --git a/include/frontier_util.h b/include/frontier_util.h index cd99f7a181..ee853cfc79 100644 --- a/include/frontier_util.h +++ b/include/frontier_util.h @@ -25,6 +25,21 @@ u8 GetFrontierBrainMonNature(u8 monId); u8 GetFrontierBrainMonEvs(u8 monId, u8 evStatId); s32 GetFronterBrainSymbol(void); void ClearEnemyPartyAfterChallenge(void); +bool8 IsFrontierTrainerFemale(u16 trainerId); +u8 GetFrontierTrainerFixedIvs(u16 trainerId); +u16 GetRandomScaledFrontierTrainerId(u8 challengeNum, u8 battleNum); +void SetBattleFacilityTrainerGfxId(u16 trainerId, u8 tempVarId); +u16 GetBattleFacilityTrainerGfxId(u16 trainerId); +u8 GetFrontierTrainerFrontSpriteId(u16 trainerId); +enum TrainerClassID GetFrontierOpponentClass(u16 trainerId); +u8 GetFrontierTrainerFacilityClass(u16 trainerId); +void GetFrontierTrainerName(u8 *dst, u16 trainerId); +u16 GetRandomFrontierMonFromSet(u16 trainerId); +void FrontierSpeechToString(const u16 *words); +u8 SetFacilityPtrsGetLevel(void); +u8 GetFrontierEnemyMonLevel(u8 lvlMode); +s32 GetHighestLevelInPlayerParty(void); +u16 FacilityClassToGraphicsId(u8 facilityClass); void ShowBattleFrontierCaughtBannedSpecies(void); #endif // GUARD_FRONTIER_UTIL_H diff --git a/src/apprentice.c b/src/apprentice.c index 585b4eed43..aed2e2adb8 100644 --- a/src/apprentice.c +++ b/src/apprentice.c @@ -5,6 +5,7 @@ #include "data.h" #include "event_data.h" #include "event_object_movement.h" +#include "frontier_util.h" #include "field_player_avatar.h" #include "international_string_util.h" #include "item.h" diff --git a/src/battle_controller_link_opponent.c b/src/battle_controller_link_opponent.c index 3418293530..f95d11b569 100644 --- a/src/battle_controller_link_opponent.c +++ b/src/battle_controller_link_opponent.c @@ -10,6 +10,7 @@ #include "battle_tv.h" #include "bg.h" #include "data.h" +#include "frontier_util.h" #include "link.h" #include "main.h" #include "m4a.h" diff --git a/src/battle_controller_link_partner.c b/src/battle_controller_link_partner.c index 408cc629e1..8653ec5a28 100644 --- a/src/battle_controller_link_partner.c +++ b/src/battle_controller_link_partner.c @@ -10,6 +10,7 @@ #include "battle_tv.h" #include "bg.h" #include "data.h" +#include "frontier_util.h" #include "link.h" #include "main.h" #include "m4a.h" diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 3adbad1780..1347f91a7e 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -10,7 +10,7 @@ #include "battle_message.h" #include "battle_interface.h" #include "battle_setup.h" -#include "battle_tower.h" +#include "battle_special.h" #include "battle_tv.h" #include "battle_z_move.h" #include "bg.h" diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index 37af6497df..aa704ffa8d 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -11,6 +11,7 @@ #include "battle_z_move.h" #include "bg.h" #include "data.h" +#include "frontier_util.h" #include "item_use.h" #include "link.h" #include "main.h" diff --git a/src/battle_controller_recorded_opponent.c b/src/battle_controller_recorded_opponent.c index 954fdc4bc3..200fc1753b 100644 --- a/src/battle_controller_recorded_opponent.c +++ b/src/battle_controller_recorded_opponent.c @@ -10,6 +10,7 @@ #include "battle_tv.h" #include "bg.h" #include "data.h" +#include "frontier_util.h" #include "item_menu.h" #include "item_use.h" #include "link.h" diff --git a/src/battle_controller_recorded_partner.c b/src/battle_controller_recorded_partner.c index d135329635..d7cfb9a579 100644 --- a/src/battle_controller_recorded_partner.c +++ b/src/battle_controller_recorded_partner.c @@ -11,6 +11,7 @@ #include "battle_z_move.h" #include "bg.h" #include "data.h" +#include "frontier_util.h" #include "item_use.h" #include "link.h" #include "main.h" diff --git a/src/battle_factory.c b/src/battle_factory.c index 50434bd09c..58dbaa4c30 100644 --- a/src/battle_factory.c +++ b/src/battle_factory.c @@ -848,3 +848,81 @@ void SetMonMoveAvoidReturn(struct Pokemon *mon, u16 moveArg, u8 moveSlot) move = MOVE_FRUSTRATION; SetMonMoveSlot(mon, move, moveSlot); } + +static void FillFactoryFrontierTrainerParty(u16 trainerId, u8 firstMonId) +{ + u8 i; + u8 level; + u8 fixedIV; + u32 otID; + + if (trainerId < FRONTIER_TRAINERS_COUNT) + { + // By mistake Battle Tower's Level 50 challenge number is used to determine the IVs for Battle Factory. + #ifdef BUGFIX + u8 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; + u8 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); + u8 challengeNum = gSaveBlock2Ptr->frontier.factoryWinStreaks[battleMode][lvlMode] / FRONTIER_STAGES_PER_CHALLENGE; + #else + u8 UNUSED lvlMode = gSaveBlock2Ptr->frontier.lvlMode; + u8 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); + u8 challengeNum = gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][FRONTIER_LVL_50] / FRONTIER_STAGES_PER_CHALLENGE; + #endif + if (gSaveBlock2Ptr->frontier.curChallengeBattleNum < FRONTIER_STAGES_PER_CHALLENGE - 1) + fixedIV = GetFactoryMonFixedIV(challengeNum, FALSE); + else + fixedIV = GetFactoryMonFixedIV(challengeNum, TRUE); // Last trainer in challenge uses higher IVs + } + else if (trainerId == TRAINER_EREADER) + { + #if FREE_BATTLE_TOWER_E_READER == FALSE + for (i = firstMonId; i < firstMonId + FRONTIER_PARTY_SIZE; i++) + CreateBattleTowerMon(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.ereaderTrainer.party[i - firstMonId]); + #endif //FREE_BATTLE_TOWER_E_READER + return; + } + else if (trainerId == TRAINER_FRONTIER_BRAIN) + { + FillFactoryBrainParty(); + return; + } + else + { + fixedIV = MAX_PER_STAT_IVS; + } + + level = SetFacilityPtrsGetLevel(); + otID = T1_READ_32(gSaveBlock2Ptr->playerTrainerId); + for (i = 0; i < FRONTIER_PARTY_SIZE; i++) + { + u16 monId = gFrontierTempParty[i]; + CreateFacilityMon(&gFacilityTrainerMons[monId], + level, fixedIV, otID, FLAG_FRONTIER_MON_FACTORY, + &gEnemyParty[firstMonId + i]); + } +} + +static void FillFactoryTentTrainerParty(u16 trainerId, u8 firstMonId) +{ + u8 i; + u8 level = TENT_MIN_LEVEL; + u8 fixedIV = 0; + u32 otID = T1_READ_32(gSaveBlock2Ptr->playerTrainerId); + + for (i = 0; i < FRONTIER_PARTY_SIZE; i++) + { + u16 monId = gFrontierTempParty[i]; + CreateFacilityMon(&gFacilityTrainerMons[monId], + level, fixedIV, otID, 0, + &gEnemyParty[firstMonId + i]); + } +} + +void FillFactoryTrainerParty(void) +{ + ZeroEnemyPartyMons(); + if (gSaveBlock2Ptr->frontier.lvlMode != FRONTIER_LVL_TENT) + FillFactoryFrontierTrainerParty(TRAINER_BATTLE_PARAM.opponentA, 0); + else + FillFactoryTentTrainerParty(TRAINER_BATTLE_PARAM.opponentA, 0); +} diff --git a/src/battle_frontier.c b/src/battle_frontier.c new file mode 100644 index 0000000000..ee1df264e6 --- /dev/null +++ b/src/battle_frontier.c @@ -0,0 +1,395 @@ +#include "global.h" +#include "main.h" +#include "battle.h" +#include "battle_main.h" +#include "battle_frontier.h" +#include "battle_setup.h" +#include "battle_dome.h" +#include "battle_factory.h" +#include "battle_partner.h" +#include "battle_tower.h" +#include "battle_transition.h" +#include "event_data.h" +#include "frontier_util.h" +#include "overworld.h" +#include "script.h" +#include "string_util.h" +#include "task.h" +#include "text.h" +#include "constants/abilities.h" +#include "constants/battle_frontier.h" +#include "constants/battle_frontier_mons.h" + +static void FillTrainerParty(u16 trainerId, u8 firstMonId, u8 monCount); + +// EWRAM vars. +EWRAM_DATA const struct BattleFrontierTrainer *gFacilityTrainers = NULL; +EWRAM_DATA const struct TrainerMon *gFacilityTrainerMons = NULL; + +// IWRAM common +COMMON_DATA u16 gFrontierTempParty[MAX_FRONTIER_PARTY_SIZE] = {0}; + +static void HandleFacilityTrainerBattleEnd(void) +{ + u8 facility = gBattleScripting.specialTrainerBattleType; + switch (facility) + { + case FACILITY_BATTLE_TOWER: + case FACILITY_BATTLE_DOME: + case FACILITY_BATTLE_PALACE: + case FACILITY_BATTLE_ARENA: + case FACILITY_BATTLE_FACTORY: + case FACILITY_BATTLE_PIKE_SINGLE: + case FACILITY_BATTLE_PIKE_DOUBLE: + case FACILITY_BATTLE_PYRAMID: + if (gSaveBlock2Ptr->frontier.battlesCount < 0xFFFFFF) + { + gSaveBlock2Ptr->frontier.battlesCount++; + if (gSaveBlock2Ptr->frontier.battlesCount % 20 == 0) + UpdateGymLeaderRematch(); + } + else + { + gSaveBlock2Ptr->frontier.battlesCount = 0xFFFFFF; + } + break; + case FACILITY_BATTLE_TRAINER_HILL: + default: + break; + } + + SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); +} + +static void Task_StartBattleAfterTransition(u8 taskId) +{ + if (IsBattleTransitionDone() == TRUE) + { + gMain.savedCallback = HandleFacilityTrainerBattleEnd; + SetMainCallback2(CB2_InitBattle); + DestroyTask(taskId); + } +} + +static void DoFacilityTrainerBattleInternal(u8 facility) +{ + gBattleScripting.specialTrainerBattleType = facility; + + switch (facility) + { + case FACILITY_BATTLE_TOWER: + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_BATTLE_TOWER; + switch (VarGet(VAR_FRONTIER_BATTLE_MODE)) + { + case FRONTIER_MODE_SINGLES: + FillFrontierTrainerParty(FRONTIER_PARTY_SIZE); + break; + case FRONTIER_MODE_DOUBLES: + FillFrontierTrainerParty(FRONTIER_DOUBLES_PARTY_SIZE); + gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; + break; + case FRONTIER_MODE_MULTIS: + FillFrontierTrainersParties(FRONTIER_MULTI_PARTY_SIZE); + gPartnerTrainerId = gSaveBlock2Ptr->frontier.trainerIds[17]; + FillPartnerParty(gPartnerTrainerId); + gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_MULTI | BATTLE_TYPE_TWO_OPPONENTS; + break; + case FRONTIER_MODE_LINK_MULTIS: + gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_LINK | BATTLE_TYPE_MULTI | BATTLE_TYPE_TOWER_LINK_MULTI; + FillFrontierTrainersParties(FRONTIER_MULTI_PARTY_SIZE); + break; + } + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_TOWER)); + break; + case FACILITY_BATTLE_DOME: + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOME; + if (VarGet(VAR_FRONTIER_BATTLE_MODE) == FRONTIER_MODE_DOUBLES) + gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; + if (TRAINER_BATTLE_PARAM.opponentA == TRAINER_FRONTIER_BRAIN) + FillFrontierTrainerParty(DOME_BATTLE_PARTY_SIZE); + CreateTask(Task_StartBattleAfterTransition, 1); + CreateTask_PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_DOME)); + break; + case FACILITY_BATTLE_PALACE: + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_PALACE; + if (VarGet(VAR_FRONTIER_BATTLE_MODE) == FRONTIER_MODE_DOUBLES) + gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; + if (gSaveBlock2Ptr->frontier.lvlMode != FRONTIER_LVL_TENT) + FillFrontierTrainerParty(FRONTIER_PARTY_SIZE); + else + FillTentTrainerParty(FRONTIER_PARTY_SIZE); + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_PALACE)); + break; + case FACILITY_BATTLE_ARENA: + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_ARENA; + if (gSaveBlock2Ptr->frontier.lvlMode != FRONTIER_LVL_TENT) + FillFrontierTrainerParty(FRONTIER_PARTY_SIZE); + else + FillTentTrainerParty(FRONTIER_PARTY_SIZE); + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_ARENA)); + break; + case FACILITY_BATTLE_FACTORY: + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_FACTORY; + if (VarGet(VAR_FRONTIER_BATTLE_MODE) == FRONTIER_MODE_DOUBLES) + gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; + FillFactoryTrainerParty(); + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_FACTORY)); + break; + case FACILITY_BATTLE_PIKE_SINGLE: + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_BATTLE_TOWER; + FillFrontierTrainerParty(FRONTIER_PARTY_SIZE); + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_PIKE)); + break; + case FACILITY_BATTLE_PIKE_DOUBLE: + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_BATTLE_TOWER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS; + FillFrontierTrainersParties(1); + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_PIKE)); + break; + case FACILITY_BATTLE_PYRAMID: + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_PYRAMID; + FillFrontierTrainerParty(FRONTIER_PARTY_SIZE); + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_PYRAMID)); + break; + case FACILITY_BATTLE_TRAINER_HILL: + default: + break; + } +} + +void DoFacilityTrainerBattle(struct ScriptContext *ctx) +{ + u8 facility = ScriptReadByte(ctx); + + DoFacilityTrainerBattleInternal(facility); +} + +void FacilityTrainerBattle(struct ScriptContext *ctx) +{ + InitTrainerBattleParameter(); + + u8 facility = ScriptReadByte(ctx); + ctx->scriptPtr = BattleSetup_ConfigureFacilityTrainerBattle(facility, ctx->scriptPtr); +} + +void FillFrontierTrainerParty(u8 monsCount) +{ + ZeroEnemyPartyMons(); + FillTrainerParty(TRAINER_BATTLE_PARAM.opponentA, 0, monsCount); +} + +void FillFrontierTrainersParties(u8 monsCount) +{ + ZeroEnemyPartyMons(); + FillTrainerParty(TRAINER_BATTLE_PARAM.opponentA, 0, monsCount); + FillTrainerParty(TRAINER_BATTLE_PARAM.opponentB, 3, monsCount); +} + +static void FillTrainerParty(u16 trainerId, u8 firstMonId, u8 monCount) +{ + s32 i, j; + u16 chosenMonIndices[MAX_FRONTIER_PARTY_SIZE]; + u8 level = SetFacilityPtrsGetLevel(); + u8 fixedIV = 0; + u8 bfMonCount; + const u16 *monSet = NULL; + u32 otID = 0; + + if (trainerId < FRONTIER_TRAINERS_COUNT) + { + // Normal battle frontier trainer. + fixedIV = GetFrontierTrainerFixedIvs(trainerId); + monSet = gFacilityTrainers[TRAINER_BATTLE_PARAM.opponentA].monSet; + } + else if (trainerId == TRAINER_EREADER) + { + #if FREE_BATTLE_TOWER_E_READER == FALSE + for (i = firstMonId; i < firstMonId + FRONTIER_PARTY_SIZE; i++) + CreateBattleTowerMon(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.ereaderTrainer.party[i - firstMonId]); + #endif //FREE_BATTLE_TOWER_E_READER + return; + } + else if (trainerId == TRAINER_FRONTIER_BRAIN) + { + CreateFrontierBrainPokemon(); + return; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + // Record mixed player. + for (j = 0, i = firstMonId; i < firstMonId + monCount; j++, i++) + { + if (gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[j].species != SPECIES_NONE + && gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[j].level <= level) + { + CreateBattleTowerMon_HandleLevel(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[j], FALSE); + } + } + return; + } + else + { + // Apprentice. + for (i = firstMonId; i < firstMonId + FRONTIER_PARTY_SIZE; i++) + CreateApprenticeMon(&gEnemyParty[i], &gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE], i - firstMonId); + return; + } + + // Regular battle frontier trainer. + // Attempt to fill the trainer's party with random Pokémon until 3 have been + // successfully chosen. The trainer's party may not have duplicate Pokémon species + // or duplicate held items. + for (bfMonCount = 0; monSet[bfMonCount] != 0xFFFF; bfMonCount++) + ; + i = 0; + otID = Random32(); + while (i != monCount) + { + u16 monId = monSet[Random() % bfMonCount]; + + // "High tier" Pokémon are only allowed on open level mode + // 20 is not a possible value for level here + if ((level == FRONTIER_MAX_LEVEL_50 || level == 20) && monId > FRONTIER_MONS_HIGH_TIER) + continue; + + // Ensure this Pokémon species isn't a duplicate. + for (j = 0; j < i + firstMonId; j++) + { + if (GetMonData(&gEnemyParty[j], MON_DATA_SPECIES, NULL) == gFacilityTrainerMons[monId].species) + break; + } + if (j != i + firstMonId) + continue; + + // Ensure this Pokemon's held item isn't a duplicate. + for (j = 0; j < i + firstMonId; j++) + { + if (GetMonData(&gEnemyParty[j], MON_DATA_HELD_ITEM, NULL) != ITEM_NONE + && GetMonData(&gEnemyParty[j], MON_DATA_HELD_ITEM, NULL) == gFacilityTrainerMons[monId].heldItem) + break; + } + if (j != i + firstMonId) + continue; + + // Ensure this exact Pokémon index isn't a duplicate. This check doesn't seem necessary + // because the species and held items were already checked directly above. + for (j = 0; j < i; j++) + { + if (chosenMonIndices[j] == monId) + break; + } + if (j != i) + continue; + + chosenMonIndices[i] = monId; + + // Place the chosen Pokémon into the trainer's party. + CreateFacilityMon(&gFacilityTrainerMons[monId], level, fixedIV, otID, 0, &gEnemyParty[i + firstMonId]); + + // The Pokémon was successfully added to the trainer's party, so it's safe to move on to + // the next party slot. + i++; + } +} + +void CreateFacilityMon(const struct TrainerMon *fmon, u16 level, u8 fixedIV, u32 otID, u32 flags, struct Pokemon *dst) +{ + u8 ball = (fmon->ball == 0xFF) ? Random() % POKEBALL_COUNT : fmon->ball; + u16 move; + u32 personality = 0, ability, friendship, j; + + if (fmon->gender == TRAINER_MON_MALE) + { + personality = GeneratePersonalityForGender(MON_MALE, fmon->species); + } + else if (fmon->gender == TRAINER_MON_FEMALE) + { + personality = GeneratePersonalityForGender(MON_FEMALE, fmon->species); + } + + ModifyPersonalityForNature(&personality, fmon->nature); + CreateMon(dst, fmon->species, level, fixedIV, TRUE, personality, otID, OT_ID_PRESET); + + friendship = MAX_FRIENDSHIP; + // Give the chosen Pokémon its specified moves. + for (j = 0; j < MAX_MON_MOVES; j++) + { + move = fmon->moves[j]; + if (flags & FLAG_FRONTIER_MON_FACTORY && move == MOVE_RETURN) + move = MOVE_FRUSTRATION; + + SetMonMoveSlot(dst, move, j); + if (GetMoveEffect(move) == EFFECT_FRUSTRATION) + friendship = 0; // Frustration is more powerful the lower the pokemon's friendship is. + } + + SetMonData(dst, MON_DATA_FRIENDSHIP, &friendship); + SetMonData(dst, MON_DATA_HELD_ITEM, &fmon->heldItem); + + // try to set ability. Otherwise, random of non-hidden as per vanilla + if (fmon->ability != ABILITY_NONE) + { + const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[fmon->species]; + u32 maxAbilities = ARRAY_COUNT(speciesInfo->abilities); + for (ability = 0; ability < maxAbilities; ++ability) + { + if (speciesInfo->abilities[ability] == fmon->ability) + break; + } + if (ability >= maxAbilities) + ability = 0; + SetMonData(dst, MON_DATA_ABILITY_NUM, &ability); + } + + if (fmon->ev != NULL) + { + SetMonData(dst, MON_DATA_HP_EV, &(fmon->ev[0])); + SetMonData(dst, MON_DATA_ATK_EV, &(fmon->ev[1])); + SetMonData(dst, MON_DATA_DEF_EV, &(fmon->ev[2])); + SetMonData(dst, MON_DATA_SPATK_EV, &(fmon->ev[3])); + SetMonData(dst, MON_DATA_SPDEF_EV, &(fmon->ev[4])); + SetMonData(dst, MON_DATA_SPEED_EV, &(fmon->ev[5])); + } + + if (fmon->iv) + SetMonData(dst, MON_DATA_IVS, &(fmon->iv)); + + if (fmon->isShiny) + { + u32 data = TRUE; + SetMonData(dst, MON_DATA_IS_SHINY, &data); + } + if (fmon->dynamaxLevel > 0) + { + u32 data = fmon->dynamaxLevel; + SetMonData(dst, MON_DATA_DYNAMAX_LEVEL, &data); + } + if (fmon->gigantamaxFactor) + { + u32 data = fmon->gigantamaxFactor; + SetMonData(dst, MON_DATA_GIGANTAMAX_FACTOR, &data); + } + if (fmon->teraType) + { + u32 data = fmon->teraType; + SetMonData(dst, MON_DATA_TERA_TYPE, &data); + } + + + SetMonData(dst, MON_DATA_POKEBALL, &ball); + CalculateMonStats(dst); +} diff --git a/src/battle_main.c b/src/battle_main.c index 1d14ae29f2..7903f7de0d 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -25,6 +25,7 @@ #include "dma3.h" #include "event_data.h" #include "evolution_scene.h" +#include "frontier_util.h" #include "field_weather.h" #include "follower_npc.h" #include "graphics.h" diff --git a/src/battle_message.c b/src/battle_message.c index 6fb4f79a03..7e5ebf2738 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -4,7 +4,7 @@ #include "battle_controllers.h" #include "battle_message.h" #include "battle_setup.h" -#include "battle_tower.h" +#include "battle_special.h" #include "battle_z_move.h" #include "data.h" #include "event_data.h" diff --git a/src/battle_partner.c b/src/battle_partner.c new file mode 100644 index 0000000000..ea517d075e --- /dev/null +++ b/src/battle_partner.c @@ -0,0 +1,178 @@ +#include "global.h" +#include "main.h" +#include "battle.h" +#include "battle_partner.h" +#include "battle_frontier.h" +#include "data.h" +#include "frontier_util.h" +#include "difficulty.h" +#include "string_util.h" +#include "text.h" + +#include "constants/abilities.h" +#include "constants/battle_ai.h" + +#include "data/partner_parties.h" +const struct Trainer gBattlePartners[DIFFICULTY_COUNT][PARTNER_COUNT] = +{ +#include "data/battle_partners.h" +}; + +#define STEVEN_OTID 61226 + +void FillPartnerParty(u16 trainerId) +{ + s32 i, j, k; + u32 firstIdPart = 0, secondIdPart = 0, thirdIdPart = 0; + u32 ivs, level, personality; + u16 monId; + u32 otID; + u8 trainerName[(PLAYER_NAME_LENGTH * 3) + 1]; + s32 ball = -1; + enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(trainerId); + SetFacilityPtrsGetLevel(); + + if (trainerId > TRAINER_PARTNER(PARTNER_NONE)) + { + for (i = 0; i < 3; i++) + ZeroMonData(&gPlayerParty[i + 3]); + + for (i = 0; i < 3 && i < gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].partySize; i++) + { + const struct TrainerMon *partyData = gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].party; + const u8 *partnerName = gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName; + + for (k = 0; partnerName[k] != EOS && k < 3; k++) + { + if (k == 0) + { + firstIdPart = partnerName[k]; + secondIdPart = partnerName[k]; + thirdIdPart = partnerName[k]; + } + else if (k == 1) + { + secondIdPart = partnerName[k]; + thirdIdPart = partnerName[k]; + } + else if (k == 2) + { + thirdIdPart = partnerName[k]; + } + } + if (trainerId == TRAINER_PARTNER(PARTNER_STEVEN)) + otID = STEVEN_OTID; + else + otID = ((firstIdPart % 72) * 1000) + ((secondIdPart % 23) * 10) + (thirdIdPart % 37) % 65536; + + personality = Random32(); + if (partyData[i].gender == TRAINER_MON_MALE) + personality = (personality & 0xFFFFFF00) | GeneratePersonalityForGender(MON_MALE, partyData[i].species); + else if (partyData[i].gender == TRAINER_MON_FEMALE) + personality = (personality & 0xFFFFFF00) | GeneratePersonalityForGender(MON_FEMALE, partyData[i].species); + ModifyPersonalityForNature(&personality, partyData[i].nature); + CreateMon(&gPlayerParty[i + 3], partyData[i].species, partyData[i].lvl, 0, TRUE, personality, OT_ID_PRESET, otID); + j = partyData[i].isShiny; + SetMonData(&gPlayerParty[i + 3], MON_DATA_IS_SHINY, &j); + SetMonData(&gPlayerParty[i + 3], MON_DATA_HELD_ITEM, &partyData[i].heldItem); + CustomTrainerPartyAssignMoves(&gPlayerParty[i + 3], &partyData[i]); + + SetMonData(&gPlayerParty[i + 3], MON_DATA_IVS, &(partyData[i].iv)); + if (partyData[i].ev != NULL) + { + SetMonData(&gPlayerParty[i + 3], MON_DATA_HP_EV, &(partyData[i].ev[0])); + SetMonData(&gPlayerParty[i + 3], MON_DATA_ATK_EV, &(partyData[i].ev[1])); + SetMonData(&gPlayerParty[i + 3], MON_DATA_DEF_EV, &(partyData[i].ev[2])); + SetMonData(&gPlayerParty[i + 3], MON_DATA_SPATK_EV, &(partyData[i].ev[3])); + SetMonData(&gPlayerParty[i + 3], MON_DATA_SPDEF_EV, &(partyData[i].ev[4])); + SetMonData(&gPlayerParty[i + 3], MON_DATA_SPEED_EV, &(partyData[i].ev[5])); + } + if (partyData[i].ability != ABILITY_NONE) + { + const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[partyData[i].species]; + u32 maxAbilities = ARRAY_COUNT(speciesInfo->abilities); + for (j = 0; j < maxAbilities; j++) + { + if (speciesInfo->abilities[j] == partyData[i].ability) + break; + } + if (j < maxAbilities) + SetMonData(&gPlayerParty[i + 3], MON_DATA_ABILITY_NUM, &j); + } + SetMonData(&gPlayerParty[i + 3], MON_DATA_FRIENDSHIP, &(partyData[i].friendship)); + if (partyData[i].ball != ITEM_NONE) + { + ball = partyData[i].ball; + SetMonData(&gPlayerParty[i + 3], MON_DATA_POKEBALL, &ball); + } + if (partyData[i].nickname != NULL) + { + SetMonData(&gPlayerParty[i + 3], MON_DATA_NICKNAME, partyData[i].nickname); + } + CalculateMonStats(&gPlayerParty[i + 3]); + + StringCopy(trainerName, gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName); + SetMonData(&gPlayerParty[i + 3], MON_DATA_OT_NAME, trainerName); + j = gBattlePartners[difficulty][SanitizeTrainerId(trainerId - TRAINER_PARTNER(PARTNER_NONE))].encounterMusic_gender >> 7; + SetMonData(&gPlayerParty[i + 3], MON_DATA_OT_GENDER, &j); + } + } + else if (trainerId == TRAINER_EREADER) + { + // Scrapped, lol. + trainerName[0] = gGameLanguage; + } + else if (trainerId < FRONTIER_TRAINERS_COUNT) + { + level = SetFacilityPtrsGetLevel(); + ivs = GetFrontierTrainerFixedIvs(trainerId); + otID = Random32(); + for (i = 0; i < FRONTIER_MULTI_PARTY_SIZE; i++) + { + monId = gSaveBlock2Ptr->frontier.trainerIds[i + 18]; + CreateFacilityMon(&gFacilityTrainerMons[monId], level, ivs, otID, 0, &gPlayerParty[MULTI_PARTY_SIZE + i]); + for (j = 0; j < PLAYER_NAME_LENGTH + 1; j++) + trainerName[j] = gFacilityTrainers[trainerId].trainerName[j]; + SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_OT_NAME, &trainerName); + j = IsFrontierTrainerFemale(trainerId); + SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_OT_GENDER, &j); + } + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + trainerId -= TRAINER_RECORD_MIXING_FRIEND; + for (i = 0; i < FRONTIER_MULTI_PARTY_SIZE; i++) + { + struct EmeraldBattleTowerRecord *record = &gSaveBlock2Ptr->frontier.towerRecords[trainerId]; + struct BattleTowerPokemon monData = record->party[gSaveBlock2Ptr->frontier.trainerIds[18 + i]]; + StringCopy(trainerName, record->name); + if (record->language == LANGUAGE_JAPANESE) + { + if (monData.nickname[0] != EXT_CTRL_CODE_BEGIN || monData.nickname[1] != EXT_CTRL_CODE_JPN) + { + monData.nickname[5] = EOS; + ConvertInternationalString(monData.nickname, LANGUAGE_JAPANESE); + } + } + else + { + if (monData.nickname[0] == EXT_CTRL_CODE_BEGIN && monData.nickname[1] == EXT_CTRL_CODE_JPN) + trainerName[5] = EOS; + } + CreateBattleTowerMon_HandleLevel(&gPlayerParty[MULTI_PARTY_SIZE + i], &monData, TRUE); + SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_OT_NAME, trainerName); + j = IsFrontierTrainerFemale(trainerId + TRAINER_RECORD_MIXING_FRIEND); + SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_OT_GENDER, &j); + } + } + else + { + trainerId -= TRAINER_RECORD_MIXING_APPRENTICE; + for (i = 0; i < FRONTIER_MULTI_PARTY_SIZE; i++) + { + CreateApprenticeMon(&gPlayerParty[MULTI_PARTY_SIZE + i], &gSaveBlock2Ptr->apprentices[trainerId], gSaveBlock2Ptr->frontier.trainerIds[18 + i]); + j = IsFrontierTrainerFemale(trainerId + TRAINER_RECORD_MIXING_APPRENTICE); + SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_OT_GENDER, &j); + } + } +} diff --git a/src/battle_setup.c b/src/battle_setup.c index 1ad284d05e..44f09ec40f 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -32,6 +32,7 @@ #include "field_weather.h" #include "battle_tower.h" #include "gym_leader_rematch.h" +#include "battle_frontier.h" #include "battle_pike.h" #include "battle_pyramid.h" #include "fldeff.h" @@ -42,6 +43,7 @@ #include "data.h" #include "vs_seeker.h" #include "item.h" +#include "script.h" #include "field_name_box.h" #include "constants/battle_frontier.h" #include "constants/battle_setup.h" @@ -958,7 +960,7 @@ void ResetTrainerOpponentIds(void) TRAINER_BATTLE_PARAM.opponentB = 0; } -static void InitTrainerBattleVariables(void) +void InitTrainerBattleParameter(void) { memset(gTrainerBattleParameter.data, 0, sizeof(TrainerBattleParameter)); sTrainerBattleEndScript = NULL; @@ -966,7 +968,7 @@ static void InitTrainerBattleVariables(void) void TrainerBattleLoadArgs(const u8 *data) { - InitTrainerBattleVariables(); + InitTrainerBattleParameter(); memcpy(gTrainerBattleParameter.data, data, sizeof(TrainerBattleParameter)); sTrainerBattleEndScript = (u8*)data + sizeof(TrainerBattleParameter); } @@ -1059,30 +1061,6 @@ const u8 *BattleSetup_ConfigureTrainerBattle(const u8 *data) 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 @@ -1096,6 +1074,39 @@ const u8 *BattleSetup_ConfigureTrainerBattle(const u8 *data) } } +const u8* BattleSetup_ConfigureFacilityTrainerBattle(u8 facility, const u8* scriptEndPtr) +{ + sTrainerBattleEndScript = (u8*)scriptEndPtr; + + switch (facility) + { + case FACILITY_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 FACILITY_BATTLE_TRAINER_HILL: + if (gApproachingTrainerId == 0) + { + SetMapVarsToTrainerA(); + TRAINER_BATTLE_PARAM.opponentA = LocalIdToHillTrainerId(gSpecialVar_LastTalked); + } + else + { + TRAINER_BATTLE_PARAM.opponentB = LocalIdToHillTrainerId(gSpecialVar_LastTalked); + } + return EventScript_TryDoNormalTrainerBattle; + default: + return sTrainerBattleEndScript; + } +} + void ConfigureAndSetUpOneTrainerBattle(u8 trainerObjEventId, const u8 *trainerScript) { gSelectedObjectEvent = trainerObjEventId; @@ -1960,3 +1971,15 @@ u16 CountBattledRematchTeams(u16 trainerId) return i; } + +void SetMultiTrainerBattle(struct ScriptContext *ctx) +{ + InitTrainerBattleParameter(); + + TRAINER_BATTLE_PARAM.opponentA = ScriptReadHalfword(ctx); + TRAINER_BATTLE_PARAM.defeatTextA = (u8*)ScriptReadWord(ctx); + TRAINER_BATTLE_PARAM.opponentB = ScriptReadHalfword(ctx); + TRAINER_BATTLE_PARAM.defeatTextB = (u8*)ScriptReadWord(ctx); + gPartnerTrainerId = TRAINER_PARTNER(ScriptReadHalfword(ctx)); +}; + diff --git a/src/battle_special.c b/src/battle_special.c new file mode 100644 index 0000000000..176366eba8 --- /dev/null +++ b/src/battle_special.c @@ -0,0 +1,277 @@ +#include "global.h" +#include "main.h" +#include "battle_special.h" +#include "battle.h" +#include "battle_frontier.h" +#include "battle_partner.h" +#include "battle_setup.h" +#include "battle_tower.h" +#include "battle_transition.h" +#include "event_data.h" +#include "frontier_util.h" +#include "new_game.h" +#include "overworld.h" +#include "recorded_battle.h" +#include "string_util.h" +#include "task.h" +#include "text.h" +#include "constants/battle_frontier.h" +#include "constants/battle_special.h" + +static void HandleSpecialTrainerBattleEnd(void); +static void Task_StartBattleAfterTransition(u8 taskId); +static void UNUSED FillEReaderTrainerWithPlayerData(void); +static void CopyEReaderTrainerFarewellMessage(void); + +#if FREE_BATTLE_TOWER_E_READER == FALSE +static void SetEReaderTrainerChecksum(struct BattleTowerEReaderTrainer *ereaderTrainer); +#endif //FREE_BATTLE_TOWER_E_READER + +static void HandleSpecialTrainerBattleEnd(void) +{ + s32 i; + + RecordedBattle_SaveBattleOutcome(); + switch (gBattleScripting.specialTrainerBattleType) + { + case SPECIAL_BATTLE_SECRET_BASE: + for (i = 0; i < PARTY_SIZE; i++) + { + u16 itemBefore = GetMonData(&gSaveBlock1Ptr->playerParty[i], MON_DATA_HELD_ITEM); + SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &itemBefore); + } + break; + case SPECIAL_BATTLE_EREADER: + CopyEReaderTrainerFarewellMessage(); + break; + case SPECIAL_BATTLE_MULTI: + for (i = 0; i < 3; i++) + { + if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES)) + gSaveBlock1Ptr->playerParty[i] = gPlayerParty[i]; + } + break; + } + + SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); +} + +static void Task_StartBattleAfterTransition(u8 taskId) +{ + if (IsBattleTransitionDone() == TRUE) + { + gMain.savedCallback = HandleSpecialTrainerBattleEnd; + SetMainCallback2(CB2_InitBattle); + DestroyTask(taskId); + } +} + +void DoSpecialTrainerBattle(void) +{ + s32 i; + + gBattleScripting.specialTrainerBattleType = gSpecialVar_0x8004; + switch (gSpecialVar_0x8004) + { + case SPECIAL_BATTLE_SECRET_BASE: + for (i = 0; i < PARTY_SIZE; i++) + { + u16 itemBefore = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM); + SetMonData(&gSaveBlock1Ptr->playerParty[i], MON_DATA_HELD_ITEM, &itemBefore); + } + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_SECRET_BASE)); + break; + case SPECIAL_BATTLE_EREADER: + #if FREE_BATTLE_TOWER_E_READER == FALSE + ZeroEnemyPartyMons(); + for (i = 0; i < (int)ARRAY_COUNT(gSaveBlock2Ptr->frontier.ereaderTrainer.party); i++) + CreateBattleTowerMon(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.ereaderTrainer.party[i]); + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_EREADER_TRAINER; + TRAINER_BATTLE_PARAM.opponentA = 0; + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_E_READER)); + #endif //FREE_BATTLE_TOWER_E_READER + break; + case SPECIAL_BATTLE_MULTI: + if (gSpecialVar_0x8005 & MULTI_BATTLE_2_VS_WILD) // Player + AI against wild mon + { + gBattleTypeFlags = BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER; + } + else if (gSpecialVar_0x8005 & MULTI_BATTLE_2_VS_1) // Player + AI against one trainer + { + TRAINER_BATTLE_PARAM.opponentB = 0xFFFF; + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER; + } + else // MULTI_BATTLE_2_VS_2 + { + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER; + } + + FillPartnerParty(gPartnerTrainerId); + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + if (gSpecialVar_0x8005 & MULTI_BATTLE_2_VS_WILD) + BattleTransition_StartOnField(GetWildBattleTransition()); + else + BattleTransition_StartOnField(GetTrainerBattleTransition()); + + if (gSpecialVar_0x8005 & MULTI_BATTLE_CHOOSE_MONS) // Skip mons restoring(done in the script) + gBattleScripting.specialTrainerBattleType = 0xFF; + break; + } +} + +void SetEReaderTrainerGfxId(void) +{ + SetBattleFacilityTrainerGfxId(TRAINER_EREADER, 0); +} + +// This is a leftover debugging function that is used to populate the E-Reader +// trainer with the player's current data. +static void UNUSED FillEReaderTrainerWithPlayerData(void) +{ +#if FREE_BATTLE_TOWER_E_READER == FALSE + struct BattleTowerEReaderTrainer *ereaderTrainer = &gSaveBlock2Ptr->frontier.ereaderTrainer; + s32 i, j; + + if (gSaveBlock2Ptr->playerGender != MALE) + { + ereaderTrainer->facilityClass = gTowerFemaleFacilityClasses[(gSaveBlock2Ptr->playerTrainerId[0] + gSaveBlock2Ptr->playerTrainerId[1] + + gSaveBlock2Ptr->playerTrainerId[2] + gSaveBlock2Ptr->playerTrainerId[3]) % ARRAY_COUNT(gTowerFemaleFacilityClasses)]; + } + else + { + ereaderTrainer->facilityClass = gTowerMaleFacilityClasses[(gSaveBlock2Ptr->playerTrainerId[0] + gSaveBlock2Ptr->playerTrainerId[1] + + gSaveBlock2Ptr->playerTrainerId[2] + gSaveBlock2Ptr->playerTrainerId[3]) % ARRAY_COUNT(gTowerMaleFacilityClasses)]; + } + + CopyTrainerId(ereaderTrainer->trainerId, gSaveBlock2Ptr->playerTrainerId); + StringCopy_PlayerName(ereaderTrainer->name, gSaveBlock2Ptr->playerName); + + ereaderTrainer->winStreak = 1; + + j = 7; + for (i = 0; i < EASY_CHAT_BATTLE_WORDS_COUNT; i++) + { + ereaderTrainer->greeting[i] = gSaveBlock1Ptr->easyChatBattleStart[i]; + ereaderTrainer->farewellPlayerLost[i] = j; + ereaderTrainer->farewellPlayerWon[i] = j + 6; + j++; + } + + for (i = 0; i < (int)ARRAY_COUNT(ereaderTrainer->party); i++) + ConvertPokemonToBattleTowerPokemon(&gPlayerParty[i], &ereaderTrainer->party[i]); + + SetEReaderTrainerChecksum(ereaderTrainer); +#endif //FREE_BATTLE_TOWER_E_READER +} + +u8 GetEreaderTrainerFrontSpriteId(void) +{ +#if FREE_BATTLE_TOWER_E_READER == FALSE + return gFacilityClassToPicIndex[gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass]; +#else + return 0; +#endif //FREE_BATTLE_TOWER_E_READER +} + +enum TrainerClassID GetEreaderTrainerClassId(void) +{ +#if FREE_BATTLE_TOWER_E_READER == FALSE + return gFacilityClassToTrainerClass[gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass]; +#else + return 0; +#endif //FREE_BATTLE_TOWER_E_READER +} + +void GetEreaderTrainerName(u8 *dst) +{ +#if FREE_BATTLE_TOWER_E_READER == FALSE + s32 i; + + for (i = 0; i < 5; i++) + dst[i] = gSaveBlock2Ptr->frontier.ereaderTrainer.name[i]; + + dst[i] = EOS; +#else + dst[0] = EOS; +#endif //FREE_BATTLE_TOWER_E_READER +} + +// Checks if the saved E-Reader trainer is valid. +void ValidateEReaderTrainer(void) +{ +#if FREE_BATTLE_TOWER_E_READER == FALSE + u32 i; + u32 checksum; + struct BattleTowerEReaderTrainer *ereaderTrainer; + + gSpecialVar_Result = FALSE; + ereaderTrainer = &gSaveBlock2Ptr->frontier.ereaderTrainer; + + checksum = 0; + for (i = 0; i < (sizeof(struct BattleTowerEReaderTrainer) - 4) / 4; i++) // - 4, because of the last field being the checksum itself. + checksum |= ((u32 *)ereaderTrainer)[i]; + + if (checksum == 0) + { + gSpecialVar_Result = TRUE; + return; + } + + checksum = 0; + for (i = 0; i < (sizeof(struct BattleTowerEReaderTrainer) - 4) / 4; i++) // - 4, because of the last field being the checksum itself. + checksum += ((u32 *)ereaderTrainer)[i]; + + if (gSaveBlock2Ptr->frontier.ereaderTrainer.checksum != checksum) + { + ClearEReaderTrainer(&gSaveBlock2Ptr->frontier.ereaderTrainer); + gSpecialVar_Result = TRUE; + } +#else + gSpecialVar_Result = FALSE; +#endif //FREE_BATTLE_TOWER_E_READER +} + +#if FREE_BATTLE_TOWER_E_READER == FALSE +static void SetEReaderTrainerChecksum(struct BattleTowerEReaderTrainer *ereaderTrainer) +{ + s32 i; + + ereaderTrainer->checksum = 0; + for (i = 0; i < (sizeof(struct BattleTowerEReaderTrainer) - 4) / 4; i++) // - 4, because of the last field being the checksum itself. + ereaderTrainer->checksum += ((u32 *)ereaderTrainer)[i]; +} +#endif //FREE_BATTLE_TOWER_E_READER + +void ClearEReaderTrainer(struct BattleTowerEReaderTrainer *ereaderTrainer) +{ +#if FREE_BATTLE_TOWER_E_READER == FALSE + u32 i; + + for (i = 0; i < (sizeof(struct BattleTowerEReaderTrainer)) / 4; i++) + ((u32 *)ereaderTrainer)[i] = 0; +#endif //FREE_BATTLE_TOWER_E_READER +} + +void CopyEReaderTrainerGreeting(void) +{ +#if FREE_BATTLE_TOWER_E_READER == FALSE + FrontierSpeechToString(gSaveBlock2Ptr->frontier.ereaderTrainer.greeting); +#endif //FREE_BATTLE_TOWER_E_READER +} + +static void CopyEReaderTrainerFarewellMessage(void) +{ +#if FREE_BATTLE_TOWER_E_READER == FALSE + if (gBattleOutcome == B_OUTCOME_DREW) + gStringVar4[0] = EOS; + else if (gBattleOutcome == B_OUTCOME_WON) + FrontierSpeechToString(gSaveBlock2Ptr->frontier.ereaderTrainer.farewellPlayerWon); + else + FrontierSpeechToString(gSaveBlock2Ptr->frontier.ereaderTrainer.farewellPlayerLost); +#endif //FREE_BATTLE_TOWER_E_READER +} diff --git a/src/battle_tower.c b/src/battle_tower.c index d589cb757a..a1a3cb33de 100644 --- a/src/battle_tower.c +++ b/src/battle_tower.c @@ -3,12 +3,14 @@ #include "apprentice.h" #include "event_data.h" #include "battle_setup.h" +#include "battle_special.h" #include "overworld.h" #include "random.h" #include "text.h" #include "main.h" #include "international_string_util.h" #include "battle.h" +#include "battle_partner.h" #include "frontier_util.h" #include "strings.h" #include "recorded_battle.h" @@ -43,18 +45,11 @@ #include "test/battle.h" #include "test/test_runner_battle.h" -// EWRAM vars. -EWRAM_DATA const struct BattleFrontierTrainer *gFacilityTrainers = NULL; -EWRAM_DATA const struct TrainerMon *gFacilityTrainerMons = NULL; - -// IWRAM common -COMMON_DATA u16 gFrontierTempParty[MAX_FRONTIER_PARTY_SIZE] = {0}; - // This file's functions. static void InitTowerChallenge(void); static void GetTowerData(void); static void SetTowerData(void); -static void SetNextFacilityOpponent(void); +static void SetNextTowerOpponent(void); static void SetTowerBattleWon(void); static void AwardBattleTowerRibbons(void); static void SaveTowerChallenge(void); @@ -71,17 +66,8 @@ static void ValidateBattleTowerRecordChecksums(void); static void SaveCurrentWinStreak(void); static void ValidateApprenticesChecksums(void); static void SetNextBattleTentOpponent(void); -static void CopyEReaderTrainerFarewellMessage(void); static void ClearBattleTowerRecord(struct EmeraldBattleTowerRecord *record); -static void FillTrainerParty(u16 trainerId, u8 firstMonId, u8 monCount); static void FillTentTrainerParty_(u16 trainerId, u8 firstMonId, u8 monCount); -static void FillFactoryFrontierTrainerParty(u16 trainerId, u8 firstMonId); -static void FillFactoryTentTrainerParty(u16 trainerId, u8 firstMonId); -static u8 GetFrontierTrainerFixedIvs(u16 trainerId); -#if FREE_BATTLE_TOWER_E_READER == FALSE -static void SetEReaderTrainerChecksum(struct BattleTowerEReaderTrainer *ereaderTrainer); -#endif //FREE_BATTLE_TOWER_E_READER -static u8 SetTentPtrsGetLevel(void); #include "data/battle_frontier/battle_frontier_trainer_mons.h" #include "data/battle_frontier/battle_frontier_trainers.h" @@ -700,18 +686,12 @@ static const u8 *const *const sPartnerApprenticeTextTables[NUM_APPRENTICES] = #include "data/battle_frontier/battle_tent.h" -#include "data/partner_parties.h" -const struct Trainer gBattlePartners[DIFFICULTY_COUNT][PARTNER_COUNT] = -{ -#include "data/battle_partners.h" -}; - -static void (*const sBattleTowerFuncs[])(void) = +static void (* const sBattleTowerFuncs[])(void) = { [BATTLE_TOWER_FUNC_INIT] = InitTowerChallenge, [BATTLE_TOWER_FUNC_GET_DATA] = GetTowerData, [BATTLE_TOWER_FUNC_SET_DATA] = SetTowerData, - [BATTLE_TOWER_FUNC_SET_OPPONENT] = SetNextFacilityOpponent, + [BATTLE_TOWER_FUNC_SET_OPPONENT] = SetNextTowerOpponent, [BATTLE_TOWER_FUNC_SET_BATTLE_WON] = SetTowerBattleWon, [BATTLE_TOWER_FUNC_GIVE_RIBBONS] = AwardBattleTowerRibbons, [BATTLE_TOWER_FUNC_SAVE] = SaveTowerChallenge, @@ -757,32 +737,6 @@ static const u8 sBattleTowerPartySizes2[] = [FRONTIER_MODE_LINK_MULTIS] = FRONTIER_MULTI_PARTY_SIZE, }; -// Trainer ID ranges for possible frontier trainers to encounter on particular challenges -// Trainers are scaled by difficulty, so higher trainer IDs have better teams -static const u16 sFrontierTrainerIdRanges[][2] = -{ - {FRONTIER_TRAINER_BRADY, FRONTIER_TRAINER_JILL}, // 0 - 99 - {FRONTIER_TRAINER_TREVIN, FRONTIER_TRAINER_CHLOE}, // 80 - 119 - {FRONTIER_TRAINER_ERIK, FRONTIER_TRAINER_SOFIA}, // 100 - 139 - {FRONTIER_TRAINER_NORTON, FRONTIER_TRAINER_JAZLYN}, // 120 - 159 - {FRONTIER_TRAINER_BRADEN, FRONTIER_TRAINER_ALISON}, // 140 - 179 - {FRONTIER_TRAINER_ZACHERY, FRONTIER_TRAINER_LAMAR}, // 160 - 199 - {FRONTIER_TRAINER_HANK, FRONTIER_TRAINER_TESS}, // 180 - 219 - {FRONTIER_TRAINER_JAXON, FRONTIER_TRAINER_GRETEL}, // 200 - 299 -}; - -static const u16 sFrontierTrainerIdRangesHard[][2] = -{ - {FRONTIER_TRAINER_ERIK, FRONTIER_TRAINER_CHLOE}, // 100 - 119 - {FRONTIER_TRAINER_NORTON, FRONTIER_TRAINER_SOFIA}, // 120 - 139 - {FRONTIER_TRAINER_BRADEN, FRONTIER_TRAINER_JAZLYN}, // 140 - 159 - {FRONTIER_TRAINER_ZACHERY, FRONTIER_TRAINER_ALISON}, // 160 - 179 - {FRONTIER_TRAINER_HANK, FRONTIER_TRAINER_LAMAR}, // 180 - 199 - {FRONTIER_TRAINER_JAXON, FRONTIER_TRAINER_TESS}, // 200 - 219 - {FRONTIER_TRAINER_LEON, FRONTIER_TRAINER_RAUL}, // 220 - 239 - {FRONTIER_TRAINER_JAXON, FRONTIER_TRAINER_GRETEL}, // 200 - 299 -}; - // Unknown, unused data static const u16 sUnused[] = { 179, 141, 200, 183 }; @@ -957,7 +911,7 @@ static bool8 ChooseSpecialBattleTowerTrainer(void) } } -static void SetNextFacilityOpponent(void) +static void SetNextTowerOpponent(void) { u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; if (lvlMode == FRONTIER_LVL_TENT) @@ -1010,220 +964,7 @@ static void SetNextFacilityOpponent(void) } } -u16 GetRandomScaledFrontierTrainerId(u8 challengeNum, u8 battleNum) -{ - u16 trainerId; - if (challengeNum <= 7) - { - if (battleNum == FRONTIER_STAGES_PER_CHALLENGE - 1) - { - // The last battle in each challenge has a jump in difficulty, pulls from a table with higher ranges - trainerId = (sFrontierTrainerIdRangesHard[challengeNum][1] - sFrontierTrainerIdRangesHard[challengeNum][0]) + 1; - trainerId = sFrontierTrainerIdRangesHard[challengeNum][0] + (Random() % trainerId); - } - else - { - trainerId = (sFrontierTrainerIdRanges[challengeNum][1] - sFrontierTrainerIdRanges[challengeNum][0]) + 1; - trainerId = sFrontierTrainerIdRanges[challengeNum][0] + (Random() % trainerId); - } - } - else - { - // After challenge 7, trainer IDs always come from the last, hardest range, which is the same for both trainer ID tables - trainerId = (sFrontierTrainerIdRanges[7][1] - sFrontierTrainerIdRanges[7][0]) + 1; - trainerId = sFrontierTrainerIdRanges[7][0] + (Random() % trainerId); - } - - return trainerId; -} - -static void UNUSED GetRandomScaledFrontierTrainerIdRange(u8 challengeNum, u8 battleNum, u16 *trainerIdPtr, u8 *rangePtr) -{ - u16 trainerId, range; - - if (challengeNum <= 7) - { - if (battleNum == FRONTIER_STAGES_PER_CHALLENGE - 1) - { - // The last battle in each challenge has a jump in difficulty, pulls from a table with higher ranges - range = (sFrontierTrainerIdRangesHard[challengeNum][1] - sFrontierTrainerIdRangesHard[challengeNum][0]) + 1; - trainerId = sFrontierTrainerIdRangesHard[challengeNum][0]; - } - else - { - range = (sFrontierTrainerIdRanges[challengeNum][1] - sFrontierTrainerIdRanges[challengeNum][0]) + 1; - trainerId = sFrontierTrainerIdRanges[challengeNum][0]; - } - } - else - { - // After challenge 7, trainer IDs always come from the last, hardest range, which is the same for both trainer ID tables - range = (sFrontierTrainerIdRanges[7][1] - sFrontierTrainerIdRanges[7][0]) + 1; - trainerId = sFrontierTrainerIdRanges[7][0]; - } - - *trainerIdPtr = trainerId; - *rangePtr = range; -} - -void SetBattleFacilityTrainerGfxId(u16 trainerId, u8 tempVarId) -{ - u32 i; - u8 facilityClass; - u8 trainerObjectGfxId; - - SetFacilityPtrsGetLevel(); -#if FREE_BATTLE_TOWER_E_READER == FALSE - if (trainerId == TRAINER_EREADER) - { - facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass; - } - else if (trainerId == TRAINER_FRONTIER_BRAIN) -#else - if (trainerId == TRAINER_FRONTIER_BRAIN) -#endif //FREE_BATTLE_TOWER_E_READER - { - SetFrontierBrainObjEventGfx_2(); - return; - } - else if (trainerId < FRONTIER_TRAINERS_COUNT) - { - facilityClass = gFacilityTrainers[trainerId].facilityClass; - } - else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) - { - facilityClass = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass; - } - else - { - facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass; - } - - // Search male classes. - for (i = 0; i < ARRAY_COUNT(gTowerMaleFacilityClasses); i++) - { - if (gTowerMaleFacilityClasses[i] == facilityClass) - break; - } - if (i != ARRAY_COUNT(gTowerMaleFacilityClasses)) - { - trainerObjectGfxId = gTowerMaleTrainerGfxIds[i]; - switch (tempVarId) - { - case 0: - default: - VarSet(VAR_OBJ_GFX_ID_0, trainerObjectGfxId); - return; - case 1: - VarSet(VAR_OBJ_GFX_ID_1, trainerObjectGfxId); - return; - case 15: - VarSet(VAR_OBJ_GFX_ID_E, trainerObjectGfxId); - return; - } - } - - // Search female classes. - for (i = 0; i < ARRAY_COUNT(gTowerFemaleFacilityClasses); i++) - { - if (gTowerFemaleFacilityClasses[i] == facilityClass) - break; - } - if (i != ARRAY_COUNT(gTowerFemaleFacilityClasses)) - { - trainerObjectGfxId = gTowerFemaleTrainerGfxIds[i]; - switch (tempVarId) - { - case 0: - default: - VarSet(VAR_OBJ_GFX_ID_0, trainerObjectGfxId); - return; - case 1: - VarSet(VAR_OBJ_GFX_ID_1, trainerObjectGfxId); - return; - case 15: - VarSet(VAR_OBJ_GFX_ID_E, trainerObjectGfxId); - return; - } - } - - switch (tempVarId) - { - case 0: - default: - VarSet(VAR_OBJ_GFX_ID_0, OBJ_EVENT_GFX_BOY_1); - return; - case 1: - VarSet(VAR_OBJ_GFX_ID_1, OBJ_EVENT_GFX_BOY_1); - return; - case 15: - VarSet(VAR_OBJ_GFX_ID_E, OBJ_EVENT_GFX_BOY_1); - return; - } -} - -void SetEReaderTrainerGfxId(void) -{ - SetBattleFacilityTrainerGfxId(TRAINER_EREADER, 0); -} - -u16 GetBattleFacilityTrainerGfxId(u16 trainerId) -{ - u32 i; - u8 facilityClass; - u16 trainerObjectGfxId; - - SetFacilityPtrsGetLevel(); -#if FREE_BATTLE_TOWER_E_READER == FALSE - if (trainerId == TRAINER_EREADER) - { - facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass; - } - else if (trainerId < FRONTIER_TRAINERS_COUNT) -#else - if (trainerId < FRONTIER_TRAINERS_COUNT) -#endif //FREE_BATTLE_TOWER_E_READER - { - facilityClass = gFacilityTrainers[trainerId].facilityClass; - } - else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) - { - facilityClass = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass; - } - else - { - facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass; - } - - // Search male classes. - for (i = 0; i < ARRAY_COUNT(gTowerMaleFacilityClasses); i++) - { - if (gTowerMaleFacilityClasses[i] == facilityClass) - break; - } - if (i != ARRAY_COUNT(gTowerMaleFacilityClasses)) - { - trainerObjectGfxId = gTowerMaleTrainerGfxIds[i]; - return trainerObjectGfxId; - } - - // Search female classes. - for (i = 0; i < ARRAY_COUNT(gTowerFemaleFacilityClasses); i++) - { - if (gTowerFemaleFacilityClasses[i] == facilityClass) - break; - } - if (i != ARRAY_COUNT(gTowerFemaleFacilityClasses)) - { - trainerObjectGfxId = gTowerFemaleTrainerGfxIds[i]; - return trainerObjectGfxId; - } - else - { - return OBJ_EVENT_GFX_BOY_1; - } -} void PutNewBattleTowerRecord(struct EmeraldBattleTowerRecord *newRecordEm) { @@ -1318,571 +1059,12 @@ void PutNewBattleTowerRecord(struct EmeraldBattleTowerRecord *newRecordEm) gSaveBlock2Ptr->frontier.towerRecords[slotIds[i]] = *newRecord; } -u8 GetFrontierTrainerFrontSpriteId(u16 trainerId) -{ - SetFacilityPtrsGetLevel(); - -#if FREE_BATTLE_TOWER_E_READER == FALSE - if (trainerId == TRAINER_EREADER) - { - return gFacilityClassToPicIndex[gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass]; - } - else if (trainerId == TRAINER_FRONTIER_BRAIN) -#else - if (trainerId == TRAINER_FRONTIER_BRAIN) -#endif //FREE_BATTLE_TOWER_E_READER - { - return GetFrontierBrainTrainerPicIndex(); - } - else if (trainerId < FRONTIER_TRAINERS_COUNT) - { - return gFacilityClassToPicIndex[gFacilityTrainers[trainerId].facilityClass]; - } - else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) - { - if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) - return gFacilityClassToPicIndex[GetRecordedBattleRecordMixFriendClass()]; - else - return gFacilityClassToPicIndex[gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass]; - } - else - { - if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) - return gFacilityClassToPicIndex[gApprentices[GetRecordedBattleApprenticeId()].facilityClass]; - else - return gFacilityClassToPicIndex[gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass]; - } -} - -enum TrainerClassID GetFrontierOpponentClass(u16 trainerId) -{ - enum TrainerClassID trainerClass = 0; - enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(trainerId); - SetFacilityPtrsGetLevel(); - -#if FREE_BATTLE_TOWER_E_READER == FALSE - if (trainerId == TRAINER_EREADER) - { - trainerClass = gFacilityClassToTrainerClass[gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass]; - } - else if (trainerId == TRAINER_FRONTIER_BRAIN) -#else - if (trainerId == TRAINER_FRONTIER_BRAIN) -#endif //FREE_BATTLE_TOWER_E_READER - { - return GetFrontierBrainTrainerClass(); - } - else if (trainerId > TRAINER_PARTNER(PARTNER_NONE)) - { - trainerClass = gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerClass; - } - else if (trainerId < FRONTIER_TRAINERS_COUNT) - { - trainerClass = gFacilityClassToTrainerClass[gFacilityTrainers[trainerId].facilityClass]; - } - else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) - { - if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) - { - trainerClass = gFacilityClassToTrainerClass[GetRecordedBattleRecordMixFriendClass()]; - } - else - { - trainerClass = gFacilityClassToTrainerClass[gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass]; - } - } - else - { - if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) - { - trainerClass = gFacilityClassToTrainerClass[gApprentices[GetRecordedBattleApprenticeId()].facilityClass]; - } - else - { - trainerClass = gFacilityClassToTrainerClass[gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass]; - } - } - - return trainerClass; -} - -static u8 GetFrontierTrainerFacilityClass(u16 trainerId) -{ - u8 facilityClass; - SetFacilityPtrsGetLevel(); - - if (trainerId == TRAINER_EREADER) - { - #if FREE_BATTLE_TOWER_E_READER == FALSE - facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass; - #else - facilityClass = 0; - #endif //FREE_BATTLE_TOWER_E_READER - } - else if (trainerId < FRONTIER_TRAINERS_COUNT) - { - facilityClass = gFacilityTrainers[trainerId].facilityClass; - } - else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) - { - if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) - facilityClass = GetRecordedBattleRecordMixFriendClass(); - else - facilityClass = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass; - } - else - { - if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) - facilityClass = gApprentices[GetRecordedBattleApprenticeId()].facilityClass; - else - facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass; - } - - return facilityClass; -} - -void GetFrontierTrainerName(u8 *dst, u16 trainerId) -{ - s32 i = 0; - enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(trainerId); - SetFacilityPtrsGetLevel(); - - if (trainerId == TRAINER_EREADER) - { - #if FREE_BATTLE_TOWER_E_READER == FALSE - for (i = 0; i < PLAYER_NAME_LENGTH; i++) - dst[i] = gSaveBlock2Ptr->frontier.ereaderTrainer.name[i]; - #endif //FREE_BATTLE_TOWER_E_READER - } - else if (trainerId == TRAINER_FRONTIER_BRAIN) - { - CopyFrontierBrainTrainerName(dst); - return; - } - else if (trainerId > TRAINER_PARTNER(PARTNER_NONE)) - { - for (i = 0; gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName[i] != EOS; i++) - dst[i] = gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName[i]; - } - else if (trainerId < FRONTIER_TRAINERS_COUNT) - { - for (i = 0; i < PLAYER_NAME_LENGTH; i++) - dst[i] = gFacilityTrainers[trainerId].trainerName[i]; - } - else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) - { - if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) - { - GetRecordedBattleRecordMixFriendName(dst); - return; - } - else - { - struct EmeraldBattleTowerRecord *record = &gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND]; - TVShowConvertInternationalString(dst, record->name, record->language); - return; - } - } - else - { - u8 id, language; - - if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) - { - id = GetRecordedBattleApprenticeId(); - language = GetRecordedBattleApprenticeLanguage(); - } - else - { - struct Apprentice *apprentice = &gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE]; - id = apprentice->id; - language = apprentice->language; - } - TVShowConvertInternationalString(dst, GetApprenticeNameInLanguage(id, language), language); - return; - } - - dst[i] = EOS; -} - -static bool8 IsFrontierTrainerFemale(u16 trainerId) -{ - u32 i; - u8 facilityClass; - - SetFacilityPtrsGetLevel(); - if (trainerId == TRAINER_EREADER) - { - #if FREE_BATTLE_TOWER_E_READER == FALSE - facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass; - #else - facilityClass = 0; - #endif //FREE_BATTLE_TOWER_E_READER - } - else if (trainerId == TRAINER_FRONTIER_BRAIN) - { - return IsFrontierBrainFemale(); - } - else if (trainerId < FRONTIER_TRAINERS_COUNT) - { - facilityClass = gFacilityTrainers[trainerId].facilityClass; - } - else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) - { - facilityClass = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass; - } - else - { - facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass; - } - - // Search female classes. - for (i = 0; i < ARRAY_COUNT(gTowerFemaleFacilityClasses); i++) - { - if (gTowerFemaleFacilityClasses[i] == facilityClass) - break; - } - if (i != ARRAY_COUNT(gTowerFemaleFacilityClasses)) - return TRUE; - else - return FALSE; -} - -void FillFrontierTrainerParty(u8 monsCount) -{ - ZeroEnemyPartyMons(); - FillTrainerParty(TRAINER_BATTLE_PARAM.opponentA, 0, monsCount); -} - -void FillFrontierTrainersParties(u8 monsCount) -{ - ZeroEnemyPartyMons(); - FillTrainerParty(TRAINER_BATTLE_PARAM.opponentA, 0, monsCount); - FillTrainerParty(TRAINER_BATTLE_PARAM.opponentB, 3, monsCount); -} - -static void FillTentTrainerParty(u8 monsCount) +void FillTentTrainerParty(u8 monsCount) { ZeroEnemyPartyMons(); FillTentTrainerParty_(TRAINER_BATTLE_PARAM.opponentA, 0, monsCount); } -void CreateFacilityMon(const struct TrainerMon *fmon, u16 level, u8 fixedIV, u32 otID, u32 flags, struct Pokemon *dst) -{ - u8 ball = (fmon->ball == 0xFF) ? Random() % POKEBALL_COUNT : fmon->ball; - u16 move; - u32 personality = 0, friendship, j; - enum Ability ability; - - if (fmon->gender == TRAINER_MON_MALE) - { - personality = GeneratePersonalityForGender(MON_MALE, fmon->species); - } - else if (fmon->gender == TRAINER_MON_FEMALE) - { - personality = GeneratePersonalityForGender(MON_FEMALE, fmon->species); - } - - ModifyPersonalityForNature(&personality, fmon->nature); - CreateMon(dst, fmon->species, level, fixedIV, TRUE, personality, OT_ID_PRESET, otID); - - friendship = MAX_FRIENDSHIP; - // Give the chosen Pokémon its specified moves. - for (j = 0; j < MAX_MON_MOVES; j++) - { - move = fmon->moves[j]; - if (flags & FLAG_FRONTIER_MON_FACTORY && move == MOVE_RETURN) - move = MOVE_FRUSTRATION; - - SetMonMoveSlot(dst, move, j); - if (GetMoveEffect(move) == EFFECT_FRUSTRATION) - friendship = 0; // Frustration is more powerful the lower the pokemon's friendship is. - } - - SetMonData(dst, MON_DATA_FRIENDSHIP, &friendship); - SetMonData(dst, MON_DATA_HELD_ITEM, &fmon->heldItem); - - // try to set ability. Otherwise, random of non-hidden as per vanilla - if (fmon->ability != ABILITY_NONE) - { - const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[fmon->species]; - u32 maxAbilities = ARRAY_COUNT(speciesInfo->abilities); - for (ability = 0; ability < maxAbilities; ++ability) - { - if (speciesInfo->abilities[ability] == fmon->ability) - break; - } - if (ability >= maxAbilities) - ability = 0; - SetMonData(dst, MON_DATA_ABILITY_NUM, &ability); - } - - if (fmon->ev != NULL) - { - SetMonData(dst, MON_DATA_HP_EV, &(fmon->ev[0])); - SetMonData(dst, MON_DATA_ATK_EV, &(fmon->ev[1])); - SetMonData(dst, MON_DATA_DEF_EV, &(fmon->ev[2])); - SetMonData(dst, MON_DATA_SPATK_EV, &(fmon->ev[3])); - SetMonData(dst, MON_DATA_SPDEF_EV, &(fmon->ev[4])); - SetMonData(dst, MON_DATA_SPEED_EV, &(fmon->ev[5])); - } - - if (fmon->iv) - SetMonData(dst, MON_DATA_IVS, &(fmon->iv)); - - if (fmon->isShiny) - { - bool32 data = TRUE; - SetMonData(dst, MON_DATA_IS_SHINY, &data); - } - if (fmon->dynamaxLevel > 0) - { - u32 data = fmon->dynamaxLevel; - SetMonData(dst, MON_DATA_DYNAMAX_LEVEL, &data); - } - if (fmon->gigantamaxFactor) - { - u32 data = fmon->gigantamaxFactor; - SetMonData(dst, MON_DATA_GIGANTAMAX_FACTOR, &data); - } - if (fmon->teraType) - { - enum Type data = fmon->teraType; - SetMonData(dst, MON_DATA_TERA_TYPE, &data); - } - - if (ball != BALL_STRANGE) - SetMonData(dst, MON_DATA_POKEBALL, &ball); - CalculateMonStats(dst); -} - -static void FillTrainerParty(u16 trainerId, u8 firstMonId, u8 monCount) -{ - s32 i, j; - u16 chosenMonIndices[MAX_FRONTIER_PARTY_SIZE]; - u8 level = SetFacilityPtrsGetLevel(); - u8 fixedIV = 0; - u8 bfMonCount; - const u16 *monSet = NULL; - u32 otID = 0; - - if (trainerId < FRONTIER_TRAINERS_COUNT) - { - // Normal battle frontier trainer. - fixedIV = GetFrontierTrainerFixedIvs(trainerId); - monSet = gFacilityTrainers[TRAINER_BATTLE_PARAM.opponentA].monSet; - } - else if (trainerId == TRAINER_EREADER) - { - #if FREE_BATTLE_TOWER_E_READER == FALSE - for (i = firstMonId; i < firstMonId + FRONTIER_PARTY_SIZE; i++) - CreateBattleTowerMon(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.ereaderTrainer.party[i - firstMonId]); - #endif //FREE_BATTLE_TOWER_E_READER - return; - } - else if (trainerId == TRAINER_FRONTIER_BRAIN) - { - CreateFrontierBrainPokemon(); - return; - } - else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) - { - // Record mixed player. - for (j = 0, i = firstMonId; i < firstMonId + monCount; j++, i++) - { - if (gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[j].species != SPECIES_NONE - && gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[j].level <= level) - { - CreateBattleTowerMon_HandleLevel(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[j], FALSE); - } - } - return; - } - else - { - // Apprentice. - for (i = firstMonId; i < firstMonId + FRONTIER_PARTY_SIZE; i++) - CreateApprenticeMon(&gEnemyParty[i], &gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE], i - firstMonId); - return; - } - - // Regular battle frontier trainer. - // Attempt to fill the trainer's party with random Pokémon until 3 have been - // successfully chosen. The trainer's party may not have duplicate Pokémon species - // or duplicate held items. - for (bfMonCount = 0; monSet[bfMonCount] != 0xFFFF; bfMonCount++) - ; - i = 0; - otID = Random32(); - while (i != monCount) - { - u16 monId = monSet[Random() % bfMonCount]; - - // "High tier" Pokémon are only allowed on open level mode - // 20 is not a possible value for level here - if ((level == FRONTIER_MAX_LEVEL_50 || level == 20) && monId > FRONTIER_MONS_HIGH_TIER) - continue; - - // Ensure this Pokémon species isn't a duplicate. - for (j = 0; j < i + firstMonId; j++) - { - if (GetMonData(&gEnemyParty[j], MON_DATA_SPECIES, NULL) == gFacilityTrainerMons[monId].species) - break; - } - if (j != i + firstMonId) - continue; - - // Ensure this Pokemon's held item isn't a duplicate. - for (j = 0; j < i + firstMonId; j++) - { - if (GetMonData(&gEnemyParty[j], MON_DATA_HELD_ITEM, NULL) != ITEM_NONE - && GetMonData(&gEnemyParty[j], MON_DATA_HELD_ITEM, NULL) == gFacilityTrainerMons[monId].heldItem) - break; - } - if (j != i + firstMonId) - continue; - - // Ensure this exact Pokémon index isn't a duplicate. This check doesn't seem necessary - // because the species and held items were already checked directly above. - for (j = 0; j < i; j++) - { - if (chosenMonIndices[j] == monId) - break; - } - if (j != i) - continue; - - chosenMonIndices[i] = monId; - - // Place the chosen Pokémon into the trainer's party. - CreateFacilityMon(&gFacilityTrainerMons[monId], level, fixedIV, otID, 0, &gEnemyParty[i + firstMonId]); - - // The Pokémon was successfully added to the trainer's party, so it's safe to move on to - // the next party slot. - i++; - } -} - -u16 GetRandomFrontierMonFromSet(u16 trainerId) -{ - u8 level = SetFacilityPtrsGetLevel(); - const u16 *monSet = gFacilityTrainers[trainerId].monSet; - u8 numMons = 0; - u32 monId = monSet[numMons]; - - while (monId != 0xFFFF) - { - numMons++; - monId = monSet[numMons]; - if (monId == 0xFFFF) - break; - } - - do - { - // "High tier" Pokémon are only allowed on open level mode - // 20 is not a possible value for level here - monId = monSet[Random() % numMons]; - } while((level == FRONTIER_MAX_LEVEL_50 || level == 20) && monId > FRONTIER_MONS_HIGH_TIER); - - return monId; -} - -static void FillFactoryTrainerParty(void) -{ - ZeroEnemyPartyMons(); - if (gSaveBlock2Ptr->frontier.lvlMode != FRONTIER_LVL_TENT) - FillFactoryFrontierTrainerParty(TRAINER_BATTLE_PARAM.opponentA, 0); - else - FillFactoryTentTrainerParty(TRAINER_BATTLE_PARAM.opponentA, 0); -} - -static void FillFactoryFrontierTrainerParty(u16 trainerId, u8 firstMonId) -{ - u8 i; - u8 level; - u8 fixedIV; - u32 otID; - - if (trainerId < FRONTIER_TRAINERS_COUNT) - { - // By mistake Battle Tower's Level 50 challenge number is used to determine the IVs for Battle Factory. - #ifdef BUGFIX - u8 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; - u8 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); - u8 challengeNum = gSaveBlock2Ptr->frontier.factoryWinStreaks[battleMode][lvlMode] / FRONTIER_STAGES_PER_CHALLENGE; - #else - u8 UNUSED lvlMode = gSaveBlock2Ptr->frontier.lvlMode; - u8 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE); - u8 challengeNum = gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][FRONTIER_LVL_50] / FRONTIER_STAGES_PER_CHALLENGE; - #endif - if (gSaveBlock2Ptr->frontier.curChallengeBattleNum < FRONTIER_STAGES_PER_CHALLENGE - 1) - fixedIV = GetFactoryMonFixedIV(challengeNum, FALSE); - else - fixedIV = GetFactoryMonFixedIV(challengeNum, TRUE); // Last trainer in challenge uses higher IVs - } - else if (trainerId == TRAINER_EREADER) - { - #if FREE_BATTLE_TOWER_E_READER == FALSE - for (i = firstMonId; i < firstMonId + FRONTIER_PARTY_SIZE; i++) - CreateBattleTowerMon(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.ereaderTrainer.party[i - firstMonId]); - #endif //FREE_BATTLE_TOWER_E_READER - return; - } - else if (trainerId == TRAINER_FRONTIER_BRAIN) - { - FillFactoryBrainParty(); - return; - } - else - { - fixedIV = MAX_PER_STAT_IVS; - } - - level = SetFacilityPtrsGetLevel(); - otID = T1_READ_32(gSaveBlock2Ptr->playerTrainerId); - for (i = 0; i < FRONTIER_PARTY_SIZE; i++) - { - u16 monId = gFrontierTempParty[i]; - CreateFacilityMon(&gFacilityTrainerMons[monId], - level, fixedIV, otID, FLAG_FRONTIER_MON_FACTORY, - &gEnemyParty[firstMonId + i]); - } -} - -static void FillFactoryTentTrainerParty(u16 trainerId, u8 firstMonId) -{ - u8 i; - u8 level = TENT_MIN_LEVEL; - u8 fixedIV = 0; - u32 otID = T1_READ_32(gSaveBlock2Ptr->playerTrainerId); - - for (i = 0; i < FRONTIER_PARTY_SIZE; i++) - { - u16 monId = gFrontierTempParty[i]; - CreateFacilityMon(&gFacilityTrainerMons[monId], - level, fixedIV, otID, 0, - &gEnemyParty[firstMonId + i]); - } -} - -void FrontierSpeechToString(const u16 *words) -{ - ConvertEasyChatWordsToString(gStringVar4, words, 3, 2); - if (GetStringWidth(FONT_NORMAL, gStringVar4, -1) > 204u) - { - s32 i = 0; - - ConvertEasyChatWordsToString(gStringVar4, words, 2, 3); - while (gStringVar4[i++] != CHAR_NEWLINE) - ; - while (gStringVar4[i] != CHAR_NEWLINE) - i++; - - gStringVar4[i] = CHAR_PROMPT_SCROLL; - } -} - static void GetOpponentIntroSpeech(void) { u16 trainerId; @@ -1907,211 +1089,6 @@ static void GetOpponentIntroSpeech(void) BufferApprenticeChallengeText(trainerId - TRAINER_RECORD_MIXING_APPRENTICE); } -static void HandleSpecialTrainerBattleEnd(void) -{ - s32 i; - - RecordedBattle_SaveBattleOutcome(); - switch (gBattleScripting.specialTrainerBattleType) - { - case SPECIAL_BATTLE_TOWER: - case SPECIAL_BATTLE_DOME: - case SPECIAL_BATTLE_PALACE: - case SPECIAL_BATTLE_ARENA: - case SPECIAL_BATTLE_FACTORY: - case SPECIAL_BATTLE_PIKE_SINGLE: - case SPECIAL_BATTLE_PIKE_DOUBLE: - case SPECIAL_BATTLE_PYRAMID: - if (gSaveBlock2Ptr->frontier.battlesCount < 0xFFFFFF) - { - gSaveBlock2Ptr->frontier.battlesCount++; - if (gSaveBlock2Ptr->frontier.battlesCount % 20 == 0) - UpdateGymLeaderRematch(); - } - else - { - gSaveBlock2Ptr->frontier.battlesCount = 0xFFFFFF; - } - break; - case SPECIAL_BATTLE_SECRET_BASE: - for (i = 0; i < PARTY_SIZE; i++) - { - u16 itemBefore = GetMonData(GetSavedPlayerPartyMon(i), MON_DATA_HELD_ITEM); - SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &itemBefore); - } - break; - case SPECIAL_BATTLE_EREADER: - CopyEReaderTrainerFarewellMessage(); - break; - case SPECIAL_BATTLE_MULTI: - for (i = 0; i < 3; i++) - { - if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES)) - SavePlayerPartyMon(i, &gPlayerParty[i]); - } - break; - } - - SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); -} - -static void Task_StartBattleAfterTransition(u8 taskId) -{ - if (IsBattleTransitionDone() == TRUE) - { - gMain.savedCallback = HandleSpecialTrainerBattleEnd; - SetMainCallback2(CB2_InitBattle); - DestroyTask(taskId); - } -} - -void DoSpecialTrainerBattle(void) -{ - s32 i; - - gBattleScripting.specialTrainerBattleType = gSpecialVar_0x8004; - switch (gSpecialVar_0x8004) - { - case SPECIAL_BATTLE_TOWER: - gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_BATTLE_TOWER; - switch (VarGet(VAR_FRONTIER_BATTLE_MODE)) - { - case FRONTIER_MODE_SINGLES: - FillFrontierTrainerParty(FRONTIER_PARTY_SIZE); - break; - case FRONTIER_MODE_DOUBLES: - FillFrontierTrainerParty(FRONTIER_DOUBLES_PARTY_SIZE); - gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; - break; - case FRONTIER_MODE_MULTIS: - FillFrontierTrainersParties(FRONTIER_MULTI_PARTY_SIZE); - gPartnerTrainerId = gSaveBlock2Ptr->frontier.trainerIds[17]; - FillPartnerParty(gPartnerTrainerId); - gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_MULTI | BATTLE_TYPE_TWO_OPPONENTS; - break; - case FRONTIER_MODE_LINK_MULTIS: - gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_LINK | BATTLE_TYPE_MULTI | BATTLE_TYPE_TOWER_LINK_MULTI | BATTLE_TYPE_TWO_OPPONENTS; - FillFrontierTrainersParties(FRONTIER_MULTI_PARTY_SIZE); - break; - } - CreateTask(Task_StartBattleAfterTransition, 1); - PlayMapChosenOrBattleBGM(0); - BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_TOWER)); - break; - case SPECIAL_BATTLE_SECRET_BASE: - for (i = 0; i < PARTY_SIZE; i++) - { - u16 itemBefore = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM); - SetMonData(GetSavedPlayerPartyMon(i), MON_DATA_HELD_ITEM, &itemBefore); - } - CreateTask(Task_StartBattleAfterTransition, 1); - PlayMapChosenOrBattleBGM(0); - BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_SECRET_BASE)); - break; - case SPECIAL_BATTLE_EREADER: - #if FREE_BATTLE_TOWER_E_READER == FALSE - ZeroEnemyPartyMons(); - for (i = 0; i < (int)ARRAY_COUNT(gSaveBlock2Ptr->frontier.ereaderTrainer.party); i++) - CreateBattleTowerMon(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.ereaderTrainer.party[i]); - gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_EREADER_TRAINER; - TRAINER_BATTLE_PARAM.opponentA = 0; - CreateTask(Task_StartBattleAfterTransition, 1); - PlayMapChosenOrBattleBGM(0); - BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_E_READER)); - #endif //FREE_BATTLE_TOWER_E_READER - break; - case SPECIAL_BATTLE_DOME: - gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOME; - if (VarGet(VAR_FRONTIER_BATTLE_MODE) == FRONTIER_MODE_DOUBLES) - gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; - if (TRAINER_BATTLE_PARAM.opponentA == TRAINER_FRONTIER_BRAIN) - FillFrontierTrainerParty(DOME_BATTLE_PARTY_SIZE); - CreateTask(Task_StartBattleAfterTransition, 1); - CreateTask_PlayMapChosenOrBattleBGM(0); - BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_DOME)); - break; - case SPECIAL_BATTLE_PALACE: - gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_PALACE; - if (VarGet(VAR_FRONTIER_BATTLE_MODE) == FRONTIER_MODE_DOUBLES) - gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; - if (gSaveBlock2Ptr->frontier.lvlMode != FRONTIER_LVL_TENT) - FillFrontierTrainerParty(FRONTIER_PARTY_SIZE); - else - FillTentTrainerParty(FRONTIER_PARTY_SIZE); - CreateTask(Task_StartBattleAfterTransition, 1); - PlayMapChosenOrBattleBGM(0); - BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_PALACE)); - break; - case SPECIAL_BATTLE_ARENA: - gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_ARENA; - if (gSaveBlock2Ptr->frontier.lvlMode != FRONTIER_LVL_TENT) - FillFrontierTrainerParty(FRONTIER_PARTY_SIZE); - else - FillTentTrainerParty(FRONTIER_PARTY_SIZE); - CreateTask(Task_StartBattleAfterTransition, 1); - PlayMapChosenOrBattleBGM(0); - BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_ARENA)); - break; - case SPECIAL_BATTLE_FACTORY: - gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_FACTORY; - if (VarGet(VAR_FRONTIER_BATTLE_MODE) == FRONTIER_MODE_DOUBLES) - gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; - FillFactoryTrainerParty(); - CreateTask(Task_StartBattleAfterTransition, 1); - PlayMapChosenOrBattleBGM(0); - BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_FACTORY)); - break; - case SPECIAL_BATTLE_PIKE_SINGLE: - gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_BATTLE_TOWER; - FillFrontierTrainerParty(FRONTIER_PARTY_SIZE); - CreateTask(Task_StartBattleAfterTransition, 1); - PlayMapChosenOrBattleBGM(0); - BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_PIKE)); - break; - case SPECIAL_BATTLE_PYRAMID: - gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_PYRAMID; - FillFrontierTrainerParty(FRONTIER_PARTY_SIZE); - CreateTask(Task_StartBattleAfterTransition, 1); - PlayMapChosenOrBattleBGM(0); - BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_PYRAMID)); - break; - case SPECIAL_BATTLE_PIKE_DOUBLE: - gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_BATTLE_TOWER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS; - FillFrontierTrainersParties(1); - CreateTask(Task_StartBattleAfterTransition, 1); - PlayMapChosenOrBattleBGM(0); - BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_PIKE)); - break; - case SPECIAL_BATTLE_MULTI: - if (gSpecialVar_0x8005 & MULTI_BATTLE_2_VS_WILD) // Player + AI against wild mon - { - gBattleTypeFlags = BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER; - } - else if (gSpecialVar_0x8005 & MULTI_BATTLE_2_VS_1) // Player + AI against one trainer - { - TRAINER_BATTLE_PARAM.opponentB = 0xFFFF; - gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER; - } - else // MULTI_BATTLE_2_VS_2 - { - gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER; - } - - gPartnerTrainerId = VarGet(gSpecialVar_0x8006) + TRAINER_PARTNER(PARTNER_NONE); - FillPartnerParty(gPartnerTrainerId); - CreateTask(Task_StartBattleAfterTransition, 1); - PlayMapChosenOrBattleBGM(0); - if (gSpecialVar_0x8005 & MULTI_BATTLE_2_VS_WILD) - BattleTransition_StartOnField(GetWildBattleTransition()); - else - BattleTransition_StartOnField(GetTrainerBattleTransition()); - - if (gSpecialVar_0x8005 & MULTI_BATTLE_CHOOSE_MONS) // Skip mons restoring(done in the script) - gBattleScripting.specialTrainerBattleType = 0xFF; - break; - } -} - static void SaveCurrentWinStreak(void) { u8 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; @@ -2801,152 +1778,7 @@ static void AwardBattleTowerRibbons(void) } } -// This is a leftover debugging function that is used to populate the E-Reader -// trainer with the player's current data. -static void UNUSED FillEReaderTrainerWithPlayerData(void) -{ -#if FREE_BATTLE_TOWER_E_READER == FALSE - struct BattleTowerEReaderTrainer *ereaderTrainer = &gSaveBlock2Ptr->frontier.ereaderTrainer; - s32 i, j; - if (gSaveBlock2Ptr->playerGender != MALE) - { - ereaderTrainer->facilityClass = gTowerFemaleFacilityClasses[(gSaveBlock2Ptr->playerTrainerId[0] + gSaveBlock2Ptr->playerTrainerId[1] - + gSaveBlock2Ptr->playerTrainerId[2] + gSaveBlock2Ptr->playerTrainerId[3]) % ARRAY_COUNT(gTowerFemaleFacilityClasses)]; - } - else - { - ereaderTrainer->facilityClass = gTowerMaleFacilityClasses[(gSaveBlock2Ptr->playerTrainerId[0] + gSaveBlock2Ptr->playerTrainerId[1] - + gSaveBlock2Ptr->playerTrainerId[2] + gSaveBlock2Ptr->playerTrainerId[3]) % ARRAY_COUNT(gTowerMaleFacilityClasses)]; - } - - CopyTrainerId(ereaderTrainer->trainerId, gSaveBlock2Ptr->playerTrainerId); - StringCopy_PlayerName(ereaderTrainer->name, gSaveBlock2Ptr->playerName); - - ereaderTrainer->winStreak = 1; - - j = 7; - for (i = 0; i < EASY_CHAT_BATTLE_WORDS_COUNT; i++) - { - ereaderTrainer->greeting[i] = gSaveBlock1Ptr->easyChatBattleStart[i]; - ereaderTrainer->farewellPlayerLost[i] = j; - ereaderTrainer->farewellPlayerWon[i] = j + 6; - j++; - } - - for (i = 0; i < (int)ARRAY_COUNT(ereaderTrainer->party); i++) - ConvertPokemonToBattleTowerPokemon(&gPlayerParty[i], &ereaderTrainer->party[i]); - - SetEReaderTrainerChecksum(ereaderTrainer); -#endif //FREE_BATTLE_TOWER_E_READER -} - -u8 GetEreaderTrainerFrontSpriteId(void) -{ -#if FREE_BATTLE_TOWER_E_READER == FALSE - return gFacilityClassToPicIndex[gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass]; -#else - return 0; -#endif //FREE_BATTLE_TOWER_E_READER -} - -enum TrainerClassID GetEreaderTrainerClassId(void) -{ -#if FREE_BATTLE_TOWER_E_READER == FALSE - return gFacilityClassToTrainerClass[gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass]; -#else - return 0; -#endif //FREE_BATTLE_TOWER_E_READER -} - -void GetEreaderTrainerName(u8 *dst) -{ -#if FREE_BATTLE_TOWER_E_READER == FALSE - s32 i; - - for (i = 0; i < 5; i++) - dst[i] = gSaveBlock2Ptr->frontier.ereaderTrainer.name[i]; - - dst[i] = EOS; -#else - dst[0] = EOS; -#endif //FREE_BATTLE_TOWER_E_READER -} - -// Checks if the saved E-Reader trainer is valid. -void ValidateEReaderTrainer(void) -{ -#if FREE_BATTLE_TOWER_E_READER == FALSE - u32 i; - u32 checksum; - struct BattleTowerEReaderTrainer *ereaderTrainer; - - gSpecialVar_Result = FALSE; - ereaderTrainer = &gSaveBlock2Ptr->frontier.ereaderTrainer; - - checksum = 0; - for (i = 0; i < (sizeof(struct BattleTowerEReaderTrainer) - 4) / 4; i++) // - 4, because of the last field being the checksum itself. - checksum |= ((u32 *)ereaderTrainer)[i]; - - if (checksum == 0) - { - gSpecialVar_Result = TRUE; - return; - } - - checksum = 0; - for (i = 0; i < (sizeof(struct BattleTowerEReaderTrainer) - 4) / 4; i++) // - 4, because of the last field being the checksum itself. - checksum += ((u32 *)ereaderTrainer)[i]; - - if (gSaveBlock2Ptr->frontier.ereaderTrainer.checksum != checksum) - { - ClearEReaderTrainer(&gSaveBlock2Ptr->frontier.ereaderTrainer); - gSpecialVar_Result = TRUE; - } -#else - gSpecialVar_Result = FALSE; -#endif //FREE_BATTLE_TOWER_E_READER -} - -#if FREE_BATTLE_TOWER_E_READER == FALSE -static void SetEReaderTrainerChecksum(struct BattleTowerEReaderTrainer *ereaderTrainer) -{ - s32 i; - - ereaderTrainer->checksum = 0; - for (i = 0; i < (sizeof(struct BattleTowerEReaderTrainer) - 4) / 4; i++) // - 4, because of the last field being the checksum itself. - ereaderTrainer->checksum += ((u32 *)ereaderTrainer)[i]; -} -#endif //FREE_BATTLE_TOWER_E_READER - -void ClearEReaderTrainer(struct BattleTowerEReaderTrainer *ereaderTrainer) -{ -#if FREE_BATTLE_TOWER_E_READER == FALSE - u32 i; - - for (i = 0; i < (sizeof(struct BattleTowerEReaderTrainer)) / 4; i++) - ((u32 *)ereaderTrainer)[i] = 0; -#endif //FREE_BATTLE_TOWER_E_READER -} - -void CopyEReaderTrainerGreeting(void) -{ -#if FREE_BATTLE_TOWER_E_READER == FALSE - FrontierSpeechToString(gSaveBlock2Ptr->frontier.ereaderTrainer.greeting); -#endif //FREE_BATTLE_TOWER_E_READER -} - -static void CopyEReaderTrainerFarewellMessage(void) -{ -#if FREE_BATTLE_TOWER_E_READER == FALSE - if (gBattleOutcome == B_OUTCOME_DREW) - gStringVar4[0] = EOS; - else if (gBattleOutcome == B_OUTCOME_WON) - FrontierSpeechToString(gSaveBlock2Ptr->frontier.ereaderTrainer.farewellPlayerWon); - else - FrontierSpeechToString(gSaveBlock2Ptr->frontier.ereaderTrainer.farewellPlayerLost); -#endif //FREE_BATTLE_TOWER_E_READER -} void TryHideBattleTowerReporter(void) { @@ -2959,165 +1791,6 @@ void TryHideBattleTowerReporter(void) } } -#define STEVEN_OTID 61226 - -void FillPartnerParty(u16 trainerId) -{ - s32 i, j, k; - u32 firstIdPart = 0, secondIdPart = 0, thirdIdPart = 0; - u32 ivs, level, personality; - u16 monId; - u32 otID; - u8 trainerName[(PLAYER_NAME_LENGTH * 3) + 1]; - s32 ball = -1; - enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(trainerId); - SetFacilityPtrsGetLevel(); - - if (trainerId > TRAINER_PARTNER(PARTNER_NONE)) - { - for (i = 0; i < 3; i++) - ZeroMonData(&gPlayerParty[i + 3]); - - for (i = 0; i < 3 && i < gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].partySize; i++) - { - const struct TrainerMon *partyData = gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].party; - const u8 *partnerName = gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName; - - for (k = 0; partnerName[k] != EOS && k < 3; k++) - { - if (k == 0) - { - firstIdPart = partnerName[k]; - secondIdPart = partnerName[k]; - thirdIdPart = partnerName[k]; - } - else if (k == 1) - { - secondIdPart = partnerName[k]; - thirdIdPart = partnerName[k]; - } - else if (k == 2) - { - thirdIdPart = partnerName[k]; - } - } - if (trainerId == TRAINER_PARTNER(PARTNER_STEVEN)) - otID = STEVEN_OTID; - else - otID = ((firstIdPart % 72) * 1000) + ((secondIdPart % 23) * 10) + (thirdIdPart % 37) % 65536; - - personality = Random32(); - if (partyData[i].gender == TRAINER_MON_MALE) - personality = (personality & 0xFFFFFF00) | GeneratePersonalityForGender(MON_MALE, partyData[i].species); - else if (partyData[i].gender == TRAINER_MON_FEMALE) - personality = (personality & 0xFFFFFF00) | GeneratePersonalityForGender(MON_FEMALE, partyData[i].species); - ModifyPersonalityForNature(&personality, partyData[i].nature); - CreateMon(&gPlayerParty[i + 3], partyData[i].species, partyData[i].lvl, 0, TRUE, personality, OT_ID_PRESET, otID); - j = partyData[i].isShiny; - SetMonData(&gPlayerParty[i + 3], MON_DATA_IS_SHINY, &j); - SetMonData(&gPlayerParty[i + 3], MON_DATA_HELD_ITEM, &partyData[i].heldItem); - CustomTrainerPartyAssignMoves(&gPlayerParty[i + 3], &partyData[i]); - - SetMonData(&gPlayerParty[i + 3], MON_DATA_IVS, &(partyData[i].iv)); - if (partyData[i].ev != NULL) - { - SetMonData(&gPlayerParty[i + 3], MON_DATA_HP_EV, &(partyData[i].ev[0])); - SetMonData(&gPlayerParty[i + 3], MON_DATA_ATK_EV, &(partyData[i].ev[1])); - SetMonData(&gPlayerParty[i + 3], MON_DATA_DEF_EV, &(partyData[i].ev[2])); - SetMonData(&gPlayerParty[i + 3], MON_DATA_SPATK_EV, &(partyData[i].ev[3])); - SetMonData(&gPlayerParty[i + 3], MON_DATA_SPDEF_EV, &(partyData[i].ev[4])); - SetMonData(&gPlayerParty[i + 3], MON_DATA_SPEED_EV, &(partyData[i].ev[5])); - } - if (partyData[i].ability != ABILITY_NONE) - { - const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[partyData[i].species]; - u32 maxAbilities = ARRAY_COUNT(speciesInfo->abilities); - for (j = 0; j < maxAbilities; j++) - { - if (speciesInfo->abilities[j] == partyData[i].ability) - break; - } - if (j < maxAbilities) - SetMonData(&gPlayerParty[i + 3], MON_DATA_ABILITY_NUM, &j); - } - SetMonData(&gPlayerParty[i + 3], MON_DATA_FRIENDSHIP, &(partyData[i].friendship)); - if (partyData[i].ball != ITEM_NONE) - { - ball = partyData[i].ball; - SetMonData(&gPlayerParty[i + 3], MON_DATA_POKEBALL, &ball); - } - if (partyData[i].nickname != NULL) - { - SetMonData(&gPlayerParty[i + 3], MON_DATA_NICKNAME, partyData[i].nickname); - } - CalculateMonStats(&gPlayerParty[i + 3]); - - StringCopy(trainerName, gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName); - SetMonData(&gPlayerParty[i + 3], MON_DATA_OT_NAME, trainerName); - j = gBattlePartners[difficulty][SanitizeTrainerId(trainerId - TRAINER_PARTNER(PARTNER_NONE))].encounterMusic_gender >> 7; - SetMonData(&gPlayerParty[i + 3], MON_DATA_OT_GENDER, &j); - } - } - else if (trainerId == TRAINER_EREADER) - { - // Scrapped, lol. - trainerName[0] = gGameLanguage; - } - else if (trainerId < FRONTIER_TRAINERS_COUNT) - { - level = SetFacilityPtrsGetLevel(); - ivs = GetFrontierTrainerFixedIvs(trainerId); - otID = Random32(); - for (i = 0; i < FRONTIER_MULTI_PARTY_SIZE; i++) - { - monId = gSaveBlock2Ptr->frontier.trainerIds[i + 18]; - CreateFacilityMon(&gFacilityTrainerMons[monId], level, ivs, otID, 0, &gPlayerParty[MULTI_PARTY_SIZE + i]); - for (j = 0; j < PLAYER_NAME_LENGTH + 1; j++) - trainerName[j] = gFacilityTrainers[trainerId].trainerName[j]; - SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_OT_NAME, &trainerName); - j = IsFrontierTrainerFemale(trainerId); - SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_OT_GENDER, &j); - } - } - else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) - { - trainerId -= TRAINER_RECORD_MIXING_FRIEND; - for (i = 0; i < FRONTIER_MULTI_PARTY_SIZE; i++) - { - struct EmeraldBattleTowerRecord *record = &gSaveBlock2Ptr->frontier.towerRecords[trainerId]; - struct BattleTowerPokemon monData = record->party[gSaveBlock2Ptr->frontier.trainerIds[18 + i]]; - StringCopy(trainerName, record->name); - if (record->language == LANGUAGE_JAPANESE) - { - if (monData.nickname[0] != EXT_CTRL_CODE_BEGIN || monData.nickname[1] != EXT_CTRL_CODE_JPN) - { - monData.nickname[5] = EOS; - ConvertInternationalString(monData.nickname, LANGUAGE_JAPANESE); - } - } - else - { - if (monData.nickname[0] == EXT_CTRL_CODE_BEGIN && monData.nickname[1] == EXT_CTRL_CODE_JPN) - trainerName[5] = EOS; - } - CreateBattleTowerMon_HandleLevel(&gPlayerParty[MULTI_PARTY_SIZE + i], &monData, TRUE); - SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_OT_NAME, trainerName); - j = IsFrontierTrainerFemale(trainerId + TRAINER_RECORD_MIXING_FRIEND); - SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_OT_GENDER, &j); - } - } - else - { - trainerId -= TRAINER_RECORD_MIXING_APPRENTICE; - for (i = 0; i < FRONTIER_MULTI_PARTY_SIZE; i++) - { - CreateApprenticeMon(&gPlayerParty[MULTI_PARTY_SIZE + i], &gSaveBlock2Ptr->apprentices[trainerId], gSaveBlock2Ptr->frontier.trainerIds[18 + i]); - j = IsFrontierTrainerFemale(trainerId + TRAINER_RECORD_MIXING_APPRENTICE); - SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_OT_GENDER, &j); - } - } -} - bool32 RubyBattleTowerRecordToEmerald(struct RSBattleTowerRecord *src, struct EmeraldBattleTowerRecord *dst) { s32 i, validMons = 0; @@ -3278,85 +1951,6 @@ void GetBattleTowerTrainerLanguage(u8 *dst, u16 trainerId) } } -u8 SetFacilityPtrsGetLevel(void) -{ - if (gSaveBlock2Ptr->frontier.lvlMode == FRONTIER_LVL_TENT) - { - return SetTentPtrsGetLevel(); - } - else - { - gFacilityTrainers = gBattleFrontierTrainers; - gFacilityTrainerMons = gBattleFrontierMons; - return GetFrontierEnemyMonLevel(gSaveBlock2Ptr->frontier.lvlMode); - } -} - -u8 GetFrontierEnemyMonLevel(u8 lvlMode) -{ - u8 level; - - switch (lvlMode) - { - default: - case FRONTIER_LVL_50: - level = FRONTIER_MAX_LEVEL_50; - break; - case FRONTIER_LVL_OPEN: - level = GetHighestLevelInPlayerParty(); - if (level < FRONTIER_MIN_LEVEL_OPEN) - level = FRONTIER_MIN_LEVEL_OPEN; - break; - } - - return level; -} - -s32 GetHighestLevelInPlayerParty(void) -{ - s32 highestLevel = 0; - s32 i; - - for (i = 0; i < PARTY_SIZE; i++) - { - if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL) - && GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG, NULL) != SPECIES_EGG) - { - s32 level = GetMonData(&gPlayerParty[i], MON_DATA_LEVEL, NULL); - if (level > highestLevel) - highestLevel = level; - } - } - - return highestLevel; -} - -// Frontier Trainer parties are roughly scaled in difficulty with higher trainer IDs, so scale IVs as well -// Duplicated in Battle Dome as GetDomeTrainerMonIvs -static u8 GetFrontierTrainerFixedIvs(u16 trainerId) -{ - u8 fixedIv; - - if (trainerId <= FRONTIER_TRAINER_JILL) // 0 - 99 - fixedIv = 3; - else if (trainerId <= FRONTIER_TRAINER_CHLOE) // 100 - 119 - fixedIv = 6; - else if (trainerId <= FRONTIER_TRAINER_SOFIA) // 120 - 139 - fixedIv = 9; - else if (trainerId <= FRONTIER_TRAINER_JAZLYN) // 140 - 159 - fixedIv = 12; - else if (trainerId <= FRONTIER_TRAINER_ALISON) // 160 - 179 - fixedIv = 15; - else if (trainerId <= FRONTIER_TRAINER_LAMAR) // 180 - 199 - fixedIv = 18; - else if (trainerId <= FRONTIER_TRAINER_TESS) // 200 - 219 - fixedIv = 21; - else // 220+ (- 299) - fixedIv = MAX_PER_STAT_IVS; - - return fixedIv; -} - static u16 GetBattleTentTrainerId(void) { u32 facility = VarGet(VAR_FRONTIER_FACILITY); @@ -3373,7 +1967,7 @@ static u16 GetBattleTentTrainerId(void) return 0; } -static u8 SetTentPtrsGetLevel(void) +u8 SetTentPtrsGetLevel(void) { u8 level = TENT_MIN_LEVEL; u32 facility = VarGet(VAR_FRONTIER_FACILITY); @@ -3496,40 +2090,6 @@ static void FillTentTrainerParty_(u16 trainerId, u8 firstMonId, u8 monCount) } } -u16 FacilityClassToGraphicsId(u8 facilityClass) -{ - u16 trainerObjectGfxId; - u8 i; - - // Search male classes. - for (i = 0; i < ARRAY_COUNT(gTowerMaleFacilityClasses); i++) - { - if (gTowerMaleFacilityClasses[i] == facilityClass) - break; - } - if (i != ARRAY_COUNT(gTowerMaleFacilityClasses)) - { - trainerObjectGfxId = gTowerMaleTrainerGfxIds[i]; - return trainerObjectGfxId; - } - - // Search female classes. - for (i = 0; i < ARRAY_COUNT(gTowerFemaleFacilityClasses); i++) - { - if (gTowerFemaleFacilityClasses[i] == facilityClass) - break; - } - if (i != ARRAY_COUNT(gTowerFemaleFacilityClasses)) - { - trainerObjectGfxId = gTowerFemaleTrainerGfxIds[i]; - return trainerObjectGfxId; - } - else - { - return OBJ_EVENT_GFX_BOY_1; - } -} - bool32 ValidateBattleTowerRecord(u8 recordId) // unused { s32 i; diff --git a/src/field_specials.c b/src/field_specials.c index 511f3282b5..fc6fe1f3a8 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -2,7 +2,7 @@ #include "debug.h" #include "malloc.h" #include "battle.h" -#include "battle_tower.h" +#include "battle_special.h" #include "cable_club.h" #include "data.h" #include "decoration.h" diff --git a/src/follower_npc.c b/src/follower_npc.c index 2cca3186b3..90b7664733 100644 --- a/src/follower_npc.c +++ b/src/follower_npc.c @@ -2,6 +2,7 @@ #include "follower_npc.h" #include "follower_npc_alternate_sprites.h" #include "battle.h" +#include "battle_partner.h" #include "battle_setup.h" #include "battle_tower.h" #include "bike.h" diff --git a/src/frontier_util.c b/src/frontier_util.c index 50f0c67167..df127dabf1 100644 --- a/src/frontier_util.c +++ b/src/frontier_util.c @@ -1,9 +1,12 @@ #include "global.h" #include "frontier_util.h" +#include "easy_chat.h" #include "event_data.h" #include "battle_setup.h" #include "overworld.h" #include "random.h" +#include "battle_frontier.h" +#include "battle_special.h" #include "battle_tower.h" #include "field_specials.h" #include "battle.h" @@ -29,6 +32,7 @@ #include "load_save.h" #include "battle_dome.h" #include "constants/battle_frontier.h" +#include "constants/battle_frontier_mons.h" #include "constants/battle_move_effects.h" #include "constants/battle_pike.h" #include "constants/frontier_util.h" @@ -788,6 +792,31 @@ static const u8 *const sHallFacilityToRecordsText[] = [RANKING_HALL_TOWER_LINK] = gText_FrontierFacilityWinStreak, }; +// Trainer ID ranges for possible frontier trainers to encounter on particular challenges +// Trainers are scaled by difficulty, so higher trainer IDs have better teams +static const u16 sFrontierTrainerIdRanges[][2] = +{ + {FRONTIER_TRAINER_BRADY, FRONTIER_TRAINER_JILL}, // 0 - 99 + {FRONTIER_TRAINER_TREVIN, FRONTIER_TRAINER_CHLOE}, // 80 - 119 + {FRONTIER_TRAINER_ERIK, FRONTIER_TRAINER_SOFIA}, // 100 - 139 + {FRONTIER_TRAINER_NORTON, FRONTIER_TRAINER_JAZLYN}, // 120 - 159 + {FRONTIER_TRAINER_BRADEN, FRONTIER_TRAINER_ALISON}, // 140 - 179 + {FRONTIER_TRAINER_ZACHERY, FRONTIER_TRAINER_LAMAR}, // 160 - 199 + {FRONTIER_TRAINER_HANK, FRONTIER_TRAINER_TESS}, // 180 - 219 + {FRONTIER_TRAINER_JAXON, FRONTIER_TRAINER_GRETEL}, // 200 - 299 +}; + +static const u16 sFrontierTrainerIdRangesHard[][2] = +{ + {FRONTIER_TRAINER_ERIK, FRONTIER_TRAINER_CHLOE}, // 100 - 119 + {FRONTIER_TRAINER_NORTON, FRONTIER_TRAINER_SOFIA}, // 120 - 139 + {FRONTIER_TRAINER_BRADEN, FRONTIER_TRAINER_JAZLYN}, // 140 - 159 + {FRONTIER_TRAINER_ZACHERY, FRONTIER_TRAINER_ALISON}, // 160 - 179 + {FRONTIER_TRAINER_HANK, FRONTIER_TRAINER_LAMAR}, // 180 - 199 + {FRONTIER_TRAINER_JAXON, FRONTIER_TRAINER_TESS}, // 200 - 219 + {FRONTIER_TRAINER_LEON, FRONTIER_TRAINER_RAUL}, // 220 - 239 + {FRONTIER_TRAINER_JAXON, FRONTIER_TRAINER_GRETEL}, // 200 - 299 +}; #define BANNED_SPECIES_SHOWN 6 @@ -2715,6 +2744,602 @@ void ClearEnemyPartyAfterChallenge() } } +bool8 IsFrontierTrainerFemale(u16 trainerId) +{ + u32 i; + u8 facilityClass; + + SetFacilityPtrsGetLevel(); + if (trainerId == TRAINER_EREADER) + { + #if FREE_BATTLE_TOWER_E_READER == FALSE + facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass; + #else + facilityClass = 0; + #endif //FREE_BATTLE_TOWER_E_READER + } + else if (trainerId == TRAINER_FRONTIER_BRAIN) + { + return IsFrontierBrainFemale(); + } + else if (trainerId < FRONTIER_TRAINERS_COUNT) + { + facilityClass = gFacilityTrainers[trainerId].facilityClass; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + facilityClass = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass; + } + else + { + facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass; + } + + // Search female classes. + for (i = 0; i < ARRAY_COUNT(gTowerFemaleFacilityClasses); i++) + { + if (gTowerFemaleFacilityClasses[i] == facilityClass) + break; + } + if (i != ARRAY_COUNT(gTowerFemaleFacilityClasses)) + return TRUE; + else + return FALSE; +} + +// Frontier Trainer parties are roughly scaled in difficulty with higher trainer IDs, so scale IVs as well +// Duplicated in Battle Dome as GetDomeTrainerMonIvs +u8 GetFrontierTrainerFixedIvs(u16 trainerId) +{ + u8 fixedIv; + + if (trainerId <= FRONTIER_TRAINER_JILL) // 0 - 99 + fixedIv = 3; + else if (trainerId <= FRONTIER_TRAINER_CHLOE) // 100 - 119 + fixedIv = 6; + else if (trainerId <= FRONTIER_TRAINER_SOFIA) // 120 - 139 + fixedIv = 9; + else if (trainerId <= FRONTIER_TRAINER_JAZLYN) // 140 - 159 + fixedIv = 12; + else if (trainerId <= FRONTIER_TRAINER_ALISON) // 160 - 179 + fixedIv = 15; + else if (trainerId <= FRONTIER_TRAINER_LAMAR) // 180 - 199 + fixedIv = 18; + else if (trainerId <= FRONTIER_TRAINER_TESS) // 200 - 219 + fixedIv = 21; + else // 220+ (- 299) + fixedIv = MAX_PER_STAT_IVS; + + return fixedIv; +} + + +u16 GetRandomScaledFrontierTrainerId(u8 challengeNum, u8 battleNum) +{ + u16 trainerId; + + if (challengeNum <= 7) + { + if (battleNum == FRONTIER_STAGES_PER_CHALLENGE - 1) + { + // The last battle in each challenge has a jump in difficulty, pulls from a table with higher ranges + trainerId = (sFrontierTrainerIdRangesHard[challengeNum][1] - sFrontierTrainerIdRangesHard[challengeNum][0]) + 1; + trainerId = sFrontierTrainerIdRangesHard[challengeNum][0] + (Random() % trainerId); + } + else + { + trainerId = (sFrontierTrainerIdRanges[challengeNum][1] - sFrontierTrainerIdRanges[challengeNum][0]) + 1; + trainerId = sFrontierTrainerIdRanges[challengeNum][0] + (Random() % trainerId); + } + } + else + { + // After challenge 7, trainer IDs always come from the last, hardest range, which is the same for both trainer ID tables + trainerId = (sFrontierTrainerIdRanges[7][1] - sFrontierTrainerIdRanges[7][0]) + 1; + trainerId = sFrontierTrainerIdRanges[7][0] + (Random() % trainerId); + } + + return trainerId; +} + +static void UNUSED GetRandomScaledFrontierTrainerIdRange(u8 challengeNum, u8 battleNum, u16 *trainerIdPtr, u8 *rangePtr) +{ + u16 trainerId, range; + + if (challengeNum <= 7) + { + if (battleNum == FRONTIER_STAGES_PER_CHALLENGE - 1) + { + // The last battle in each challenge has a jump in difficulty, pulls from a table with higher ranges + range = (sFrontierTrainerIdRangesHard[challengeNum][1] - sFrontierTrainerIdRangesHard[challengeNum][0]) + 1; + trainerId = sFrontierTrainerIdRangesHard[challengeNum][0]; + } + else + { + range = (sFrontierTrainerIdRanges[challengeNum][1] - sFrontierTrainerIdRanges[challengeNum][0]) + 1; + trainerId = sFrontierTrainerIdRanges[challengeNum][0]; + } + } + else + { + // After challenge 7, trainer IDs always come from the last, hardest range, which is the same for both trainer ID tables + range = (sFrontierTrainerIdRanges[7][1] - sFrontierTrainerIdRanges[7][0]) + 1; + trainerId = sFrontierTrainerIdRanges[7][0]; + } + + *trainerIdPtr = trainerId; + *rangePtr = range; +} + +void SetBattleFacilityTrainerGfxId(u16 trainerId, u8 tempVarId) +{ + u32 i; + u8 facilityClass; + u8 trainerObjectGfxId; + + SetFacilityPtrsGetLevel(); +#if FREE_BATTLE_TOWER_E_READER == FALSE + if (trainerId == TRAINER_EREADER) + { + facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass; + } + else if (trainerId == TRAINER_FRONTIER_BRAIN) +#else + if (trainerId == TRAINER_FRONTIER_BRAIN) +#endif //FREE_BATTLE_TOWER_E_READER + { + SetFrontierBrainObjEventGfx_2(); + return; + } + else if (trainerId < FRONTIER_TRAINERS_COUNT) + { + facilityClass = gFacilityTrainers[trainerId].facilityClass; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + facilityClass = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass; + } + else + { + facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass; + } + + // Search male classes. + for (i = 0; i < ARRAY_COUNT(gTowerMaleFacilityClasses); i++) + { + if (gTowerMaleFacilityClasses[i] == facilityClass) + break; + } + if (i != ARRAY_COUNT(gTowerMaleFacilityClasses)) + { + trainerObjectGfxId = gTowerMaleTrainerGfxIds[i]; + switch (tempVarId) + { + case 0: + default: + VarSet(VAR_OBJ_GFX_ID_0, trainerObjectGfxId); + return; + case 1: + VarSet(VAR_OBJ_GFX_ID_1, trainerObjectGfxId); + return; + case 15: + VarSet(VAR_OBJ_GFX_ID_E, trainerObjectGfxId); + return; + } + } + + // Search female classes. + for (i = 0; i < ARRAY_COUNT(gTowerFemaleFacilityClasses); i++) + { + if (gTowerFemaleFacilityClasses[i] == facilityClass) + break; + } + if (i != ARRAY_COUNT(gTowerFemaleFacilityClasses)) + { + trainerObjectGfxId = gTowerFemaleTrainerGfxIds[i]; + switch (tempVarId) + { + case 0: + default: + VarSet(VAR_OBJ_GFX_ID_0, trainerObjectGfxId); + return; + case 1: + VarSet(VAR_OBJ_GFX_ID_1, trainerObjectGfxId); + return; + case 15: + VarSet(VAR_OBJ_GFX_ID_E, trainerObjectGfxId); + return; + } + } + + switch (tempVarId) + { + case 0: + default: + VarSet(VAR_OBJ_GFX_ID_0, OBJ_EVENT_GFX_BOY_1); + return; + case 1: + VarSet(VAR_OBJ_GFX_ID_1, OBJ_EVENT_GFX_BOY_1); + return; + case 15: + VarSet(VAR_OBJ_GFX_ID_E, OBJ_EVENT_GFX_BOY_1); + return; + } +} + +u16 GetBattleFacilityTrainerGfxId(u16 trainerId) +{ + u32 i; + u8 facilityClass; + u16 trainerObjectGfxId; + + SetFacilityPtrsGetLevel(); +#if FREE_BATTLE_TOWER_E_READER == FALSE + if (trainerId == TRAINER_EREADER) + { + facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass; + } + else if (trainerId < FRONTIER_TRAINERS_COUNT) +#else + if (trainerId < FRONTIER_TRAINERS_COUNT) +#endif //FREE_BATTLE_TOWER_E_READER + { + facilityClass = gFacilityTrainers[trainerId].facilityClass; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + facilityClass = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass; + } + else + { + facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass; + } + + // Search male classes. + for (i = 0; i < ARRAY_COUNT(gTowerMaleFacilityClasses); i++) + { + if (gTowerMaleFacilityClasses[i] == facilityClass) + break; + } + if (i != ARRAY_COUNT(gTowerMaleFacilityClasses)) + { + trainerObjectGfxId = gTowerMaleTrainerGfxIds[i]; + return trainerObjectGfxId; + } + + // Search female classes. + for (i = 0; i < ARRAY_COUNT(gTowerFemaleFacilityClasses); i++) + { + if (gTowerFemaleFacilityClasses[i] == facilityClass) + break; + } + if (i != ARRAY_COUNT(gTowerFemaleFacilityClasses)) + { + trainerObjectGfxId = gTowerFemaleTrainerGfxIds[i]; + return trainerObjectGfxId; + } + else + { + return OBJ_EVENT_GFX_BOY_1; + } +} + +u8 GetFrontierTrainerFrontSpriteId(u16 trainerId) +{ + SetFacilityPtrsGetLevel(); + +#if FREE_BATTLE_TOWER_E_READER == FALSE + if (trainerId == TRAINER_EREADER) + { + return gFacilityClassToPicIndex[gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass]; + } + else if (trainerId == TRAINER_FRONTIER_BRAIN) +#else + if (trainerId == TRAINER_FRONTIER_BRAIN) +#endif //FREE_BATTLE_TOWER_E_READER + { + return GetFrontierBrainTrainerPicIndex(); + } + else if (trainerId < FRONTIER_TRAINERS_COUNT) + { + return gFacilityClassToPicIndex[gFacilityTrainers[trainerId].facilityClass]; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + return gFacilityClassToPicIndex[GetRecordedBattleRecordMixFriendClass()]; + else + return gFacilityClassToPicIndex[gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass]; + } + else + { + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + return gFacilityClassToPicIndex[gApprentices[GetRecordedBattleApprenticeId()].facilityClass]; + else + return gFacilityClassToPicIndex[gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass]; + } +} + +enum TrainerClassID GetFrontierOpponentClass(u16 trainerId) +{ + u8 trainerClass = 0; + enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(trainerId); + SetFacilityPtrsGetLevel(); + +#if FREE_BATTLE_TOWER_E_READER == FALSE + if (trainerId == TRAINER_EREADER) + { + trainerClass = gFacilityClassToTrainerClass[gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass]; + } + else if (trainerId == TRAINER_FRONTIER_BRAIN) +#else + if (trainerId == TRAINER_FRONTIER_BRAIN) +#endif //FREE_BATTLE_TOWER_E_READER + { + return GetFrontierBrainTrainerClass(); + } + else if (trainerId > TRAINER_PARTNER(PARTNER_NONE)) + { + trainerClass = gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerClass; + } + else if (trainerId < FRONTIER_TRAINERS_COUNT) + { + trainerClass = gFacilityClassToTrainerClass[gFacilityTrainers[trainerId].facilityClass]; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + { + trainerClass = gFacilityClassToTrainerClass[GetRecordedBattleRecordMixFriendClass()]; + } + else + { + trainerClass = gFacilityClassToTrainerClass[gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass]; + } + } + else + { + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + { + trainerClass = gFacilityClassToTrainerClass[gApprentices[GetRecordedBattleApprenticeId()].facilityClass]; + } + else + { + trainerClass = gFacilityClassToTrainerClass[gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass]; + } + } + + return trainerClass; +} + +u8 GetFrontierTrainerFacilityClass(u16 trainerId) +{ + u8 facilityClass; + SetFacilityPtrsGetLevel(); + + if (trainerId == TRAINER_EREADER) + { + #if FREE_BATTLE_TOWER_E_READER == FALSE + facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass; + #else + facilityClass = 0; + #endif //FREE_BATTLE_TOWER_E_READER + } + else if (trainerId < FRONTIER_TRAINERS_COUNT) + { + facilityClass = gFacilityTrainers[trainerId].facilityClass; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + facilityClass = GetRecordedBattleRecordMixFriendClass(); + else + facilityClass = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass; + } + else + { + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + facilityClass = gApprentices[GetRecordedBattleApprenticeId()].facilityClass; + else + facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass; + } + + return facilityClass; +} + +void GetFrontierTrainerName(u8 *dst, u16 trainerId) +{ + s32 i = 0; + enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(trainerId); + SetFacilityPtrsGetLevel(); + + if (trainerId == TRAINER_EREADER) + { + #if FREE_BATTLE_TOWER_E_READER == FALSE + for (i = 0; i < PLAYER_NAME_LENGTH; i++) + dst[i] = gSaveBlock2Ptr->frontier.ereaderTrainer.name[i]; + #endif //FREE_BATTLE_TOWER_E_READER + } + else if (trainerId == TRAINER_FRONTIER_BRAIN) + { + CopyFrontierBrainTrainerName(dst); + return; + } + else if (trainerId > TRAINER_PARTNER(PARTNER_NONE)) + { + for (i = 0; gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName[i] != EOS; i++) + dst[i] = gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName[i]; + } + else if (trainerId < FRONTIER_TRAINERS_COUNT) + { + for (i = 0; i < PLAYER_NAME_LENGTH; i++) + dst[i] = gFacilityTrainers[trainerId].trainerName[i]; + } + else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE) + { + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + { + GetRecordedBattleRecordMixFriendName(dst); + return; + } + else + { + struct EmeraldBattleTowerRecord *record = &gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND]; + TVShowConvertInternationalString(dst, record->name, record->language); + return; + } + } + else + { + u8 id, language; + + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + { + id = GetRecordedBattleApprenticeId(); + language = GetRecordedBattleApprenticeLanguage(); + } + else + { + struct Apprentice *apprentice = &gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE]; + id = apprentice->id; + language = apprentice->language; + } + TVShowConvertInternationalString(dst, GetApprenticeNameInLanguage(id, language), language); + return; + } + + dst[i] = EOS; +} + +u16 GetRandomFrontierMonFromSet(u16 trainerId) +{ + u8 level = SetFacilityPtrsGetLevel(); + const u16 *monSet = gFacilityTrainers[trainerId].monSet; + u8 numMons = 0; + u32 monId = monSet[numMons]; + + while (monId != 0xFFFF) + { + numMons++; + monId = monSet[numMons]; + if (monId == 0xFFFF) + break; + } + + do + { + // "High tier" Pokémon are only allowed on open level mode + // 20 is not a possible value for level here + monId = monSet[Random() % numMons]; + } while((level == FRONTIER_MAX_LEVEL_50 || level == 20) && monId > FRONTIER_MONS_HIGH_TIER); + + return monId; +} + +void FrontierSpeechToString(const u16 *words) +{ + ConvertEasyChatWordsToString(gStringVar4, words, 3, 2); + if (GetStringWidth(FONT_NORMAL, gStringVar4, -1) > 204u) + { + s32 i = 0; + + ConvertEasyChatWordsToString(gStringVar4, words, 2, 3); + while (gStringVar4[i++] != CHAR_NEWLINE) + ; + while (gStringVar4[i] != CHAR_NEWLINE) + i++; + + gStringVar4[i] = CHAR_PROMPT_SCROLL; + } +} + +u8 SetFacilityPtrsGetLevel(void) +{ + if (gSaveBlock2Ptr->frontier.lvlMode == FRONTIER_LVL_TENT) + { + return SetTentPtrsGetLevel(); + } + else + { + gFacilityTrainers = gBattleFrontierTrainers; + gFacilityTrainerMons = gBattleFrontierMons; + return GetFrontierEnemyMonLevel(gSaveBlock2Ptr->frontier.lvlMode); + } +} + +u8 GetFrontierEnemyMonLevel(u8 lvlMode) +{ + u8 level; + + switch (lvlMode) + { + default: + case FRONTIER_LVL_50: + level = FRONTIER_MAX_LEVEL_50; + break; + case FRONTIER_LVL_OPEN: + level = GetHighestLevelInPlayerParty(); + if (level < FRONTIER_MIN_LEVEL_OPEN) + level = FRONTIER_MIN_LEVEL_OPEN; + break; + } + + return level; +} + +s32 GetHighestLevelInPlayerParty(void) +{ + s32 highestLevel = 0; + s32 i; + + for (i = 0; i < PARTY_SIZE; i++) + { + if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL) + && GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG, NULL) != SPECIES_EGG) + { + s32 level = GetMonData(&gPlayerParty[i], MON_DATA_LEVEL, NULL); + if (level > highestLevel) + highestLevel = level; + } + } + + return highestLevel; +} + +u16 FacilityClassToGraphicsId(u8 facilityClass) +{ + u16 trainerObjectGfxId; + u8 i; + + // Search male classes. + for (i = 0; i < ARRAY_COUNT(gTowerMaleFacilityClasses); i++) + { + if (gTowerMaleFacilityClasses[i] == facilityClass) + break; + } + if (i != ARRAY_COUNT(gTowerMaleFacilityClasses)) + { + trainerObjectGfxId = gTowerMaleTrainerGfxIds[i]; + return trainerObjectGfxId; + } + + // Search female classes. + for (i = 0; i < ARRAY_COUNT(gTowerFemaleFacilityClasses); i++) + { + if (gTowerFemaleFacilityClasses[i] == facilityClass) + break; + } + if (i != ARRAY_COUNT(gTowerFemaleFacilityClasses)) + { + trainerObjectGfxId = gTowerFemaleTrainerGfxIds[i]; + return trainerObjectGfxId; + } + else + { + return OBJ_EVENT_GFX_BOY_1; + } +} + #define tWindowId data[0] #define tMenuTaskId data[1] #define tArrowTaskId data[2] diff --git a/src/mystery_event_script.c b/src/mystery_event_script.c index c50ef5f963..13f5169a0c 100644 --- a/src/mystery_event_script.c +++ b/src/mystery_event_script.c @@ -1,6 +1,6 @@ #include "global.h" #include "berry.h" -#include "battle_tower.h" +#include "battle_special.h" #include "easy_chat.h" #include "event_data.h" #include "mail.h" diff --git a/src/mystery_gift.c b/src/mystery_gift.c index 1c1d871d66..11ff12b77f 100755 --- a/src/mystery_gift.c +++ b/src/mystery_gift.c @@ -4,7 +4,7 @@ #include "event_data.h" #include "easy_chat.h" #include "script.h" -#include "battle_tower.h" +#include "battle_special.h" #include "wonder_news.h" #include "string_util.h" #include "new_game.h" diff --git a/src/mystery_gift_client.c b/src/mystery_gift_client.c index 2d2ccec545..36a8c10ac2 100644 --- a/src/mystery_gift_client.c +++ b/src/mystery_gift_client.c @@ -3,7 +3,7 @@ #include "decompress.h" #include "overworld.h" #include "script.h" -#include "battle_tower.h" +#include "battle_special.h" #include "mystery_gift.h" #include "mystery_event_script.h" #include "mystery_gift_client.h" diff --git a/src/pokemon.c b/src/pokemon.c index 9e8ee13a42..867d06cafc 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -12,6 +12,7 @@ #include "battle_tower.h" #include "battle_z_move.h" #include "data.h" +#include "frontier_util.h" #include "daycare.h" #include "dexnav.h" #include "event_data.h" diff --git a/src/trainer_hill.c b/src/trainer_hill.c index 3acf983632..ac23189727 100644 --- a/src/trainer_hill.c +++ b/src/trainer_hill.c @@ -6,6 +6,7 @@ #include "ereader_helpers.h" #include "event_data.h" #include "event_scripts.h" +#include "frontier_util.h" #include "fieldmap.h" #include "field_message_box.h" #include "international_string_util.h" diff --git a/src/trainer_see.c b/src/trainer_see.c index bf66f66e2e..b12c077d60 100644 --- a/src/trainer_see.c +++ b/src/trainer_see.c @@ -15,6 +15,7 @@ #include "trainer_hill.h" #include "util.h" #include "battle_pyramid.h" +#include "constants/battle_frontier.h" #include "constants/battle_setup.h" #include "constants/event_objects.h" #include "constants/event_object_movement.h" @@ -420,6 +421,33 @@ bool8 CheckForTrainersWantingBattle(void) break; } + + if (InBattlePyramid_() || InTrainerHillChallenge()) + { + u8 facility = InBattlePyramid_() ? FACILITY_BATTLE_PYRAMID : FACILITY_BATTLE_TRAINER_HILL; + + if (gNoOfApproachingTrainers > 0) + { + ResetTrainerOpponentIds(); + InitTrainerBattleParameter(); + + gSelectedObjectEvent = gApproachingTrainers[0].objectEventId; + gSpecialVar_LastTalked = gObjectEvents[gApproachingTrainers[0].objectEventId].localId; + BattleSetup_ConfigureFacilityTrainerBattle(facility, gApproachingTrainers[0].trainerScriptPtr + 2); + if (gNoOfApproachingTrainers > 1) + { + gApproachingTrainerId++; + gSelectedObjectEvent = gApproachingTrainers[1].objectEventId; + gSpecialVar_LastTalked = gObjectEvents[gApproachingTrainers[1].objectEventId].localId; + BattleSetup_ConfigureFacilityTrainerBattle(facility, gApproachingTrainers[0].trainerScriptPtr + 2); + gApproachingTrainerId = 0; + } + ScriptContext_SetupScript(EventScript_StartTrainerApproach); + LockPlayerFieldControls(); + return TRUE; + } + } + if (gNoOfApproachingTrainers == 1) { ResetTrainerOpponentIds();