Trainerbattle Type Differentiation (#6424)

Co-authored-by: CallmeEchoo <callmeechoo@github.com>
Co-authored-by: Your Name <you@example.com>
This commit is contained in:
Salem 2025-12-01 19:28:09 +01:00 committed by GitHub
parent 25623fbade
commit 29f83ea5e0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
49 changed files with 1806 additions and 1595 deletions

View File

@ -98,14 +98,13 @@
waitstate waitstate
.endm .endm
.macro multi_do type:req, partnerId:req .macro multi_do type:req
special ReducePlayerPartyToSelectedMons special ReducePlayerPartyToSelectedMons
setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SET_DATA setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SET_DATA
setvar VAR_0x8005, FRONTIER_DATA_SELECTED_MON_ORDER setvar VAR_0x8005, FRONTIER_DATA_SELECTED_MON_ORDER
special CallFrontierUtilFunc @ saves the mon order, so the non-selected mons get restored afterwards special CallFrontierUtilFunc @ saves the mon order, so the non-selected mons get restored afterwards
setvar VAR_0x8004, SPECIAL_BATTLE_MULTI setvar VAR_0x8004, SPECIAL_BATTLE_MULTI
setvar VAR_0x8005, \type | MULTI_BATTLE_CHOOSE_MONS setvar VAR_0x8005, \type | MULTI_BATTLE_CHOOSE_MONS
setvar VAR_0x8006, \partnerId
special DoSpecialTrainerBattle special DoSpecialTrainerBattle
waitstate waitstate
setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SAVE_PARTY 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 .macro multi_2_vs_2 trainer1Id:req, trainer1LoseText:req, trainer2Id:req, trainer2LoseText:req, partnerId:req
special SavePlayerParty 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 setmultitrainerbattle \trainer1Id, \trainer1LoseText, \trainer2Id, \trainer2LoseText, \partnerId
multi_do MULTI_BATTLE_2_VS_2, \partnerId multi_do MULTI_BATTLE_2_VS_2
.endm .endm
.macro multi_2_vs_1 trainer1Id:req, trainer1LoseText:req, partnerId:req .macro multi_2_vs_1 trainer1Id:req, trainer1LoseText:req, partnerId:req
special SavePlayerParty 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 setmultitrainerbattle \trainer1Id, \trainer1LoseText, TRAINER_NONE, NULL, \partnerId
multi_do MULTI_BATTLE_2_VS_1, \partnerId multi_do MULTI_BATTLE_2_VS_1
.endm .endm
@ Wild mons need to be assigned to gEnemyParty 0 and 3 slots, other slots need to be cleared out. @ Wild mons need to be assigned to gEnemyParty 0 and 3 slots, other slots need to be cleared out.
.macro multi_wild partnerId:req .macro multi_wild partnerId:req
special SavePlayerParty 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 .endm
.macro multi_do_fixed type:req, partnerId:req .macro multi_do_fixed type:req
setvar VAR_0x8004, SPECIAL_BATTLE_MULTI setvar VAR_0x8004, SPECIAL_BATTLE_MULTI
setvar VAR_0x8005, \type setvar VAR_0x8005, \type
setvar VAR_0x8006, \partnerId
special DoSpecialTrainerBattle special DoSpecialTrainerBattle
waitstate waitstate
setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SAVE_PARTY 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 .macro multi_fixed_2_vs_2 trainer1Id:req, trainer1LoseText:req, trainer2Id:req, trainer2LoseText:req, partnerId:req
special SavePlayerParty 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 setmultitrainerbattle \trainer1Id, \trainer1LoseText, \trainer2Id, \trainer2LoseText, \partnerId
multi_do_fixed MULTI_BATTLE_2_VS_2, \partnerId multi_do_fixed MULTI_BATTLE_2_VS_2
.endm .endm
.macro multi_fixed_2_vs_1 trainer1Id:req, trainer1LoseText:req, partnerId:req .macro multi_fixed_2_vs_1 trainer1Id:req, trainer1LoseText:req, partnerId:req
special SavePlayerParty 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 setmultitrainerbattle \trainer1Id, \trainer1LoseText, TRAINER_NONE, NULL, \partnerId
multi_do_fixed MULTI_BATTLE_2_VS_1, \partnerId multi_do_fixed MULTI_BATTLE_2_VS_1
.endm .endm
@ Wild mons need to be assigned to gEnemyParty 0 and 3 slots, other slots need to be cleared out. @ 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 .macro multi_fixed_wild partnerId:req
special SavePlayerParty 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 .endm

View File

@ -2599,6 +2599,27 @@
cant_see_if 5 cant_see_if 5
.endm .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 @ Follower NPCs
@ Sets an existing NPC up to follow the player. @ Sets an existing NPC up to follow the player.

View File

@ -13,6 +13,7 @@
#include "constants/battle_pike.h" #include "constants/battle_pike.h"
#include "constants/battle_pyramid.h" #include "constants/battle_pyramid.h"
#include "constants/battle_setup.h" #include "constants/battle_setup.h"
#include "constants/battle_special.h"
#include "constants/battle_tent.h" #include "constants/battle_tent.h"
#include "constants/battle_tower.h" #include "constants/battle_tower.h"
#include "constants/berry.h" #include "constants/berry.h"

View File

@ -364,9 +364,7 @@ BattleFrontier_BattleArenaBattleRoom_EventScript_DoArenaBattle::
setvar VAR_TEMP_2, 0 setvar VAR_TEMP_2, 0
frontier_set FRONTIER_DATA_RECORD_DISABLED, FALSE frontier_set FRONTIER_DATA_RECORD_DISABLED, FALSE
special HealPlayerParty special HealPlayerParty
setvar VAR_0x8004, SPECIAL_BATTLE_ARENA dofacilitytrainerbattle FACILITY_BATTLE_ARENA
setvar VAR_0x8005, 0
special DoSpecialTrainerBattle
waitstate waitstate
frontier_restorehelditems frontier_restorehelditems
special HealPlayerParty special HealPlayerParty

View File

@ -445,10 +445,8 @@ BattleFrontier_BattleDomeBattleRoom_EventScript_TuckerDraw::
BattleFrontier_BattleDomeBattleRoom_EventScript_DoDomeBattle:: BattleFrontier_BattleDomeBattleRoom_EventScript_DoDomeBattle::
frontier_set FRONTIER_DATA_RECORD_DISABLED, FALSE frontier_set FRONTIER_DATA_RECORD_DISABLED, FALSE
special HealPlayerParty special HealPlayerParty
setvar VAR_0x8004, SPECIAL_BATTLE_DOME
setvar VAR_0x8005, 0
setvar VAR_TEMP_9, 1 setvar VAR_TEMP_9, 1
special DoSpecialTrainerBattle dofacilitytrainerbattle FACILITY_BATTLE_DOME
waitstate waitstate
setvar VAR_TEMP_9, 0 setvar VAR_TEMP_9, 0
dome_restorehelditems dome_restorehelditems

View File

@ -80,9 +80,7 @@ BattleFrontier_BattleFactoryBattleRoom_EventScript_BattleOpponent::
closemessage closemessage
frontier_set FRONTIER_DATA_RECORD_DISABLED, FALSE frontier_set FRONTIER_DATA_RECORD_DISABLED, FALSE
special HealPlayerParty special HealPlayerParty
setvar VAR_0x8004, SPECIAL_BATTLE_FACTORY dofacilitytrainerbattle FRONTIER_FACILITY_FACTORY
setvar VAR_0x8005, 0
special DoSpecialTrainerBattle
waitstate waitstate
switch VAR_RESULT switch VAR_RESULT
case 1, BattleFrontier_BattleFactoryBattleRoom_EventScript_DefeatedOpponent case 1, BattleFrontier_BattleFactoryBattleRoom_EventScript_DefeatedOpponent
@ -171,9 +169,7 @@ BattleFrontier_BattleFactoryBattleRoom_EventScript_DoNolandBattle::
closemessage closemessage
frontier_set FRONTIER_DATA_RECORD_DISABLED, FALSE frontier_set FRONTIER_DATA_RECORD_DISABLED, FALSE
special HealPlayerParty special HealPlayerParty
setvar VAR_0x8004, SPECIAL_BATTLE_FACTORY dofacilitytrainerbattle FACILITY_BATTLE_FACTORY
setvar VAR_0x8005, 0
special DoSpecialTrainerBattle
waitstate waitstate
return return

View File

@ -274,9 +274,7 @@ BattleFrontier_BattlePalaceBattleRoom_EventScript_DoPalaceBattle::
setvar VAR_TEMP_2, 0 setvar VAR_TEMP_2, 0
frontier_set FRONTIER_DATA_RECORD_DISABLED, FALSE frontier_set FRONTIER_DATA_RECORD_DISABLED, FALSE
special HealPlayerParty special HealPlayerParty
setvar VAR_0x8004, SPECIAL_BATTLE_PALACE dofacilitytrainerbattle FACILITY_BATTLE_PALACE
setvar VAR_0x8005, 0
special DoSpecialTrainerBattle
waitstate waitstate
frontier_restorehelditems frontier_restorehelditems
special HealPlayerParty special HealPlayerParty

View File

@ -31,9 +31,7 @@ BattleFrontier_BattlePikeRoomNormal_EventScript_EnterSingleBattleRoom::
waitmessage waitmessage
closemessage closemessage
releaseall releaseall
setvar VAR_0x8004, SPECIAL_BATTLE_PIKE_SINGLE dofacilitytrainerbattle FACILITY_BATTLE_PIKE_SINGLE
setvar VAR_0x8005, 0
special DoSpecialTrainerBattle
waitstate waitstate
switch VAR_RESULT switch VAR_RESULT
case 1, BattleFrontier_BattlePikeRoomNormal_EventScript_WonSingleBattle case 1, BattleFrontier_BattlePikeRoomNormal_EventScript_WonSingleBattle
@ -61,9 +59,7 @@ BattleFrontier_BattlePikeRoomNormal_EventScript_EnterHardBattleRoom::
waitmessage waitmessage
closemessage closemessage
releaseall releaseall
setvar VAR_0x8004, SPECIAL_BATTLE_PIKE_SINGLE dofacilitytrainerbattle FACILITY_BATTLE_PIKE_SINGLE
setvar VAR_0x8005, 0
special DoSpecialTrainerBattle
waitstate waitstate
switch VAR_RESULT switch VAR_RESULT
case 1, BattleFrontier_BattlePikeRoomNormal_EventScript_WonHardBattle case 1, BattleFrontier_BattlePikeRoomNormal_EventScript_WonHardBattle
@ -252,9 +248,7 @@ BattleFrontier_BattlePikeRoomNormal_EventScript_EnterDoubleBattleRoom::
pike_gettrainerintro 1 pike_gettrainerintro 1
msgbox gStringVar4, MSGBOX_DEFAULT msgbox gStringVar4, MSGBOX_DEFAULT
closemessage closemessage
setvar VAR_0x8004, SPECIAL_BATTLE_PIKE_DOUBLE dofacilitytrainerbattle FACILITY_BATTLE_PIKE_DOUBLE
setvar VAR_0x8005, 0
special DoSpecialTrainerBattle
waitstate waitstate
switch VAR_RESULT switch VAR_RESULT
case 1, BattleFrontier_BattlePikeRoomNormal_EventScript_WonDoubleBattle case 1, BattleFrontier_BattlePikeRoomNormal_EventScript_WonDoubleBattle
@ -380,9 +374,7 @@ BattleFrontier_BattlePikeRoomNormal_EventScript_DoPikeQueenBattle::
closemessage closemessage
applymovement LOCALID_PLAYER, BattleFrontier_BattlePikeRoomNormal_Movement_PlayerWalkUp2 applymovement LOCALID_PLAYER, BattleFrontier_BattlePikeRoomNormal_Movement_PlayerWalkUp2
waitmovement 0 waitmovement 0
setvar VAR_0x8004, SPECIAL_BATTLE_PIKE_SINGLE dofacilitytrainerbattle FACILITY_BATTLE_PIKE_SINGLE
setvar VAR_0x8005, 0
special DoSpecialTrainerBattle
waitstate waitstate
return return

View File

@ -105,7 +105,7 @@ BattlePyramid_WarpToTop::
@ TRAINER_PHILLIP is used as a placeholder @ TRAINER_PHILLIP is used as a placeholder
BattlePyramid_TrainerBattle:: 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 pyramid_showhint
waitmessage waitmessage
waitbuttonpress waitbuttonpress

View File

@ -180,9 +180,7 @@ BattleFrontier_BattlePyramidTop_EventScript_DefeatedBrandonGold::
BattleFrontier_BattlePyramidTop_EventScript_DoBrandonBattle:: BattleFrontier_BattlePyramidTop_EventScript_DoBrandonBattle::
closemessage closemessage
setvar VAR_0x8004, SPECIAL_BATTLE_PYRAMID dofacilitytrainerbattle FACILITY_BATTLE_PYRAMID
setvar VAR_0x8005, 0
special DoSpecialTrainerBattle
waitstate waitstate
return return

View File

@ -296,9 +296,7 @@ BattleFrontier_BattleTowerBattleRoom_EventScript_DoTowerBattle::
setvar VAR_TEMP_2, 0 setvar VAR_TEMP_2, 0
frontier_set FRONTIER_DATA_RECORD_DISABLED, FALSE frontier_set FRONTIER_DATA_RECORD_DISABLED, FALSE
special HealPlayerParty special HealPlayerParty
setvar VAR_0x8004, SPECIAL_BATTLE_TOWER dofacilitytrainerbattle FACILITY_BATTLE_TOWER
setvar VAR_0x8005, 0
special DoSpecialTrainerBattle
waitstate waitstate
copyvar VAR_0x8004, VAR_FRONTIER_BATTLE_MODE copyvar VAR_0x8004, VAR_FRONTIER_BATTLE_MODE
goto_if_eq VAR_0x8004, FRONTIER_MODE_LINK_MULTIS, BattleFrontier_BattleTowerBattleRoom_EventScript_EndTowerBattle goto_if_eq VAR_0x8004, FRONTIER_MODE_LINK_MULTIS, BattleFrontier_BattleTowerBattleRoom_EventScript_EndTowerBattle

View File

@ -57,9 +57,7 @@ SlateportCity_BattleTentBattleRoom_EventScript_EnterRoom::
waitmessage waitmessage
closemessage closemessage
special HealPlayerParty special HealPlayerParty
setvar VAR_0x8004, SPECIAL_BATTLE_FACTORY dofacilitytrainerbattle FACILITY_BATTLE_PYRAMID
setvar VAR_0x8005, 0
special DoSpecialTrainerBattle
waitstate waitstate
switch VAR_RESULT switch VAR_RESULT
case 1, SlateportCity_BattleTentBattleRoom_EventScript_DefeatedOpponent case 1, SlateportCity_BattleTentBattleRoom_EventScript_DefeatedOpponent

View File

@ -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 @ TRAINER_PHILLIP is an actual Trainer on the SS Tidal, but is used as a placeholder here
TrainerHill_EventScript_TrainerBattle:: 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 trainerhill_postbattletext
waitmessage waitmessage
waitbuttonpress waitbuttonpress

View File

@ -1,6 +1,8 @@
#ifndef GUARD_BATTLE_DOME_H #ifndef GUARD_BATTLE_DOME_H
#define GUARD_BATTLE_DOME_H #define GUARD_BATTLE_DOME_H
#include "constants/battle_dome.h"
int GetDomeTrainerSelectedMons(u16 tournamentTrainerId); int GetDomeTrainerSelectedMons(u16 tournamentTrainerId);
int TrainerIdToDomeTournamentId(u16 trainerId); int TrainerIdToDomeTournamentId(u16 trainerId);

View File

@ -8,5 +8,6 @@ void FillFactoryBrainParty(void);
u8 GetNumPastRentalsRank(u8 battleMode, u8 lvlMode); u8 GetNumPastRentalsRank(u8 battleMode, u8 lvlMode);
u64 GetAiScriptsInBattleFactory(void); u64 GetAiScriptsInBattleFactory(void);
void SetMonMoveAvoidReturn(struct Pokemon *mon, u16 moveArg, u8 moveSlot); void SetMonMoveAvoidReturn(struct Pokemon *mon, u16 moveArg, u8 moveSlot);
void FillFactoryTrainerParty(void);
#endif // GUARD_BATTLE_FACTORY_H #endif // GUARD_BATTLE_FACTORY_H

32
include/battle_frontier.h Normal file
View File

@ -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

11
include/battle_partner.h Normal file
View File

@ -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

View File

@ -42,7 +42,6 @@ typedef union PACKED TrainerBattleParameter
u8 data[sizeof(struct _TrainerBattleParameter)]; u8 data[sizeof(struct _TrainerBattleParameter)];
} TrainerBattleParameter; } TrainerBattleParameter;
extern const struct RematchTrainer gRematchTable[REMATCH_TABLE_ENTRIES]; extern const struct RematchTrainer gRematchTable[REMATCH_TABLE_ENTRIES];
extern TrainerBattleParameter gTrainerBattleParameter; extern TrainerBattleParameter gTrainerBattleParameter;
@ -70,6 +69,7 @@ void ResetTrainerOpponentIds(void);
void SetMapVarsToTrainerA(void); void SetMapVarsToTrainerA(void);
void SetMapVarsToTrainerB(void); void SetMapVarsToTrainerB(void);
const u8 *BattleSetup_ConfigureTrainerBattle(const u8 *data); const u8 *BattleSetup_ConfigureTrainerBattle(const u8 *data);
const u8* BattleSetup_ConfigureFacilityTrainerBattle(u8 facility, const u8* scriptEndPtr);
void ConfigureAndSetUpOneTrainerBattle(u8 trainerObjEventId, const u8 *trainerScript); void ConfigureAndSetUpOneTrainerBattle(u8 trainerObjEventId, const u8 *trainerScript);
void ConfigureTwoTrainersBattle(u8 trainerObjEventId, const u8 *trainerScript); void ConfigureTwoTrainersBattle(u8 trainerObjEventId, const u8 *trainerScript);
void SetUpTwoTrainersBattle(void); void SetUpTwoTrainersBattle(void);
@ -107,6 +107,7 @@ void TrainerBattleLoadArgs(const u8 *data);
void TrainerBattleLoadArgsTrainerA(const u8 *data); void TrainerBattleLoadArgsTrainerA(const u8 *data);
void TrainerBattleLoadArgsTrainerB(const u8 *data); void TrainerBattleLoadArgsTrainerB(const u8 *data);
void TrainerBattleLoadArgsSecondTrainer(const u8 *data); void TrainerBattleLoadArgsSecondTrainer(const u8 *data);
void InitTrainerBattleParameter(void);
void DoStandardWildBattle_Debug(void); void DoStandardWildBattle_Debug(void);
void BattleSetup_StartTrainerBattle_Debug(void); void BattleSetup_StartTrainerBattle_Debug(void);

13
include/battle_special.h Normal file
View File

@ -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

View File

@ -2,6 +2,7 @@
#define GUARD_BATTLE_TOWER_H #define GUARD_BATTLE_TOWER_H
#include "data.h" #include "data.h"
#include "battle_frontier.h"
struct RSBattleTowerRecord struct RSBattleTowerRecord
{ {
@ -15,68 +16,27 @@ struct RSBattleTowerRecord
/*0xA0*/ u32 checksum; /*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 u8 gTowerMaleFacilityClasses[30];
extern const u16 gTowerMaleTrainerGfxIds[30]; extern const u16 gTowerMaleTrainerGfxIds[30];
extern const u8 gTowerFemaleFacilityClasses[20]; extern const u8 gTowerFemaleFacilityClasses[20];
extern const u16 gTowerFemaleTrainerGfxIds[20]; extern const u16 gTowerFemaleTrainerGfxIds[20];
extern const struct TrainerMon gBattleFrontierMons[];
extern const struct BattleFrontierTrainer gBattleFrontierTrainers[];
extern const struct TrainerMon gSlateportBattleTentMons[]; extern const struct TrainerMon gSlateportBattleTentMons[];
extern const struct BattleFrontierTrainer gSlateportBattleTentTrainers[]; 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); 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); 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 CalcEmeraldBattleTowerChecksum(struct EmeraldBattleTowerRecord *record);
void CalcRubyBattleTowerChecksum(struct RSBattleTowerRecord *record); void CalcRubyBattleTowerChecksum(struct RSBattleTowerRecord *record);
u16 GetCurrentBattleTowerWinStreak(u8 lvlMode, u8 battleMode); 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); void TryHideBattleTowerReporter(void);
bool32 RubyBattleTowerRecordToEmerald(struct RSBattleTowerRecord *src, struct EmeraldBattleTowerRecord *dst); bool32 RubyBattleTowerRecordToEmerald(struct RSBattleTowerRecord *src, struct EmeraldBattleTowerRecord *dst);
bool32 EmeraldBattleTowerRecordToRuby(struct EmeraldBattleTowerRecord *src, struct RSBattleTowerRecord *dst); bool32 EmeraldBattleTowerRecordToRuby(struct EmeraldBattleTowerRecord *src, struct RSBattleTowerRecord *dst);
void CalcApprenticeChecksum(struct Apprentice *apprentice); void CalcApprenticeChecksum(struct Apprentice *apprentice);
void GetBattleTowerTrainerLanguage(u8 *dst, u16 trainerId); void GetBattleTowerTrainerLanguage(u8 *dst, u16 trainerId);
u8 SetFacilityPtrsGetLevel(void); u8 SetTentPtrsGetLevel(void);
u8 GetFrontierEnemyMonLevel(u8 lvlMode);
s32 GetHighestLevelInPlayerParty(void);
u16 FacilityClassToGraphicsId(u8 facilityClass);
bool32 ValidateBattleTowerRecord(u8 recordId); // unused bool32 ValidateBattleTowerRecord(u8 recordId); // unused
void TrySetLinkBattleTowerEnemyPartyLevel(void); void TrySetLinkBattleTowerEnemyPartyLevel(void);
void CreateFacilityMon(const struct TrainerMon *fmon, u16 level, u8 fixedIV, u32 otID, u32 flags, struct Pokemon *dst); void FillTentTrainerParty(u8 monsCount);
void FillPartnerParty(u16 trainerId);
#endif //GUARD_BATTLE_TOWER_H #endif //GUARD_BATTLE_TOWER_H

View File

@ -33,17 +33,20 @@
#define CHALLENGE_STATUS_LOST 4 #define CHALLENGE_STATUS_LOST 4
// Special trainer battles. // Special trainer battles.
#define SPECIAL_BATTLE_TOWER 0
#define SPECIAL_BATTLE_SECRET_BASE 1 #define SPECIAL_BATTLE_SECRET_BASE 1
#define SPECIAL_BATTLE_EREADER 2 #define SPECIAL_BATTLE_EREADER 2
#define SPECIAL_BATTLE_DOME 3 #define SPECIAL_BATTLE_MULTI 3
#define SPECIAL_BATTLE_PALACE 4
#define SPECIAL_BATTLE_ARENA 5 // Facility trainer Battles
#define SPECIAL_BATTLE_FACTORY 6 #define FACILITY_BATTLE_TOWER 0
#define SPECIAL_BATTLE_PIKE_SINGLE 7 #define FACILITY_BATTLE_DOME 1
#define SPECIAL_BATTLE_PIKE_DOUBLE 8 #define FACILITY_BATTLE_PALACE 2
#define SPECIAL_BATTLE_PYRAMID 9 #define FACILITY_BATTLE_ARENA 3
#define SPECIAL_BATTLE_MULTI 10 #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_BATTLE_FRONTIER_POINTS 9999
#define MAX_STREAK 9999 #define MAX_STREAK 9999

View File

@ -10,9 +10,6 @@
#define TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE 6 #define TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE 6
#define TRAINER_BATTLE_REMATCH_DOUBLE 7 #define TRAINER_BATTLE_REMATCH_DOUBLE 7
#define TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE_NO_MUSIC 8 #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 #define TRAINER_BATTLE_TWO_TRAINERS_NO_INTRO 13
#endif // GUARD_CONSTANTS_BATTLE_SETUP_H #endif // GUARD_CONSTANTS_BATTLE_SETUP_H

View File

@ -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

View File

@ -38,10 +38,4 @@
#define BATTLE_TOWER_LINKSTAT_MEMBER_RETIRE 2 #define BATTLE_TOWER_LINKSTAT_MEMBER_RETIRE 2
#define BATTLE_TOWER_LINKSTAT_LEADER_RETIRE 3 #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 #endif //GUARD_CONSTANTS_BATTLE_TOWER_H

View File

@ -25,6 +25,21 @@ u8 GetFrontierBrainMonNature(u8 monId);
u8 GetFrontierBrainMonEvs(u8 monId, u8 evStatId); u8 GetFrontierBrainMonEvs(u8 monId, u8 evStatId);
s32 GetFronterBrainSymbol(void); s32 GetFronterBrainSymbol(void);
void ClearEnemyPartyAfterChallenge(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); void ShowBattleFrontierCaughtBannedSpecies(void);
#endif // GUARD_FRONTIER_UTIL_H #endif // GUARD_FRONTIER_UTIL_H

View File

@ -5,6 +5,7 @@
#include "data.h" #include "data.h"
#include "event_data.h" #include "event_data.h"
#include "event_object_movement.h" #include "event_object_movement.h"
#include "frontier_util.h"
#include "field_player_avatar.h" #include "field_player_avatar.h"
#include "international_string_util.h" #include "international_string_util.h"
#include "item.h" #include "item.h"

View File

@ -10,6 +10,7 @@
#include "battle_tv.h" #include "battle_tv.h"
#include "bg.h" #include "bg.h"
#include "data.h" #include "data.h"
#include "frontier_util.h"
#include "link.h" #include "link.h"
#include "main.h" #include "main.h"
#include "m4a.h" #include "m4a.h"

View File

@ -10,6 +10,7 @@
#include "battle_tv.h" #include "battle_tv.h"
#include "bg.h" #include "bg.h"
#include "data.h" #include "data.h"
#include "frontier_util.h"
#include "link.h" #include "link.h"
#include "main.h" #include "main.h"
#include "m4a.h" #include "m4a.h"

View File

@ -10,7 +10,7 @@
#include "battle_message.h" #include "battle_message.h"
#include "battle_interface.h" #include "battle_interface.h"
#include "battle_setup.h" #include "battle_setup.h"
#include "battle_tower.h" #include "battle_special.h"
#include "battle_tv.h" #include "battle_tv.h"
#include "battle_z_move.h" #include "battle_z_move.h"
#include "bg.h" #include "bg.h"

View File

@ -11,6 +11,7 @@
#include "battle_z_move.h" #include "battle_z_move.h"
#include "bg.h" #include "bg.h"
#include "data.h" #include "data.h"
#include "frontier_util.h"
#include "item_use.h" #include "item_use.h"
#include "link.h" #include "link.h"
#include "main.h" #include "main.h"

View File

@ -10,6 +10,7 @@
#include "battle_tv.h" #include "battle_tv.h"
#include "bg.h" #include "bg.h"
#include "data.h" #include "data.h"
#include "frontier_util.h"
#include "item_menu.h" #include "item_menu.h"
#include "item_use.h" #include "item_use.h"
#include "link.h" #include "link.h"

View File

@ -11,6 +11,7 @@
#include "battle_z_move.h" #include "battle_z_move.h"
#include "bg.h" #include "bg.h"
#include "data.h" #include "data.h"
#include "frontier_util.h"
#include "item_use.h" #include "item_use.h"
#include "link.h" #include "link.h"
#include "main.h" #include "main.h"

View File

@ -848,3 +848,81 @@ void SetMonMoveAvoidReturn(struct Pokemon *mon, u16 moveArg, u8 moveSlot)
move = MOVE_FRUSTRATION; move = MOVE_FRUSTRATION;
SetMonMoveSlot(mon, move, moveSlot); 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);
}

395
src/battle_frontier.c Normal file
View File

@ -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);
}

View File

@ -25,6 +25,7 @@
#include "dma3.h" #include "dma3.h"
#include "event_data.h" #include "event_data.h"
#include "evolution_scene.h" #include "evolution_scene.h"
#include "frontier_util.h"
#include "field_weather.h" #include "field_weather.h"
#include "follower_npc.h" #include "follower_npc.h"
#include "graphics.h" #include "graphics.h"

View File

@ -4,7 +4,7 @@
#include "battle_controllers.h" #include "battle_controllers.h"
#include "battle_message.h" #include "battle_message.h"
#include "battle_setup.h" #include "battle_setup.h"
#include "battle_tower.h" #include "battle_special.h"
#include "battle_z_move.h" #include "battle_z_move.h"
#include "data.h" #include "data.h"
#include "event_data.h" #include "event_data.h"

178
src/battle_partner.c Normal file
View File

@ -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);
}
}
}

View File

@ -32,6 +32,7 @@
#include "field_weather.h" #include "field_weather.h"
#include "battle_tower.h" #include "battle_tower.h"
#include "gym_leader_rematch.h" #include "gym_leader_rematch.h"
#include "battle_frontier.h"
#include "battle_pike.h" #include "battle_pike.h"
#include "battle_pyramid.h" #include "battle_pyramid.h"
#include "fldeff.h" #include "fldeff.h"
@ -42,6 +43,7 @@
#include "data.h" #include "data.h"
#include "vs_seeker.h" #include "vs_seeker.h"
#include "item.h" #include "item.h"
#include "script.h"
#include "field_name_box.h" #include "field_name_box.h"
#include "constants/battle_frontier.h" #include "constants/battle_frontier.h"
#include "constants/battle_setup.h" #include "constants/battle_setup.h"
@ -958,7 +960,7 @@ void ResetTrainerOpponentIds(void)
TRAINER_BATTLE_PARAM.opponentB = 0; TRAINER_BATTLE_PARAM.opponentB = 0;
} }
static void InitTrainerBattleVariables(void) void InitTrainerBattleParameter(void)
{ {
memset(gTrainerBattleParameter.data, 0, sizeof(TrainerBattleParameter)); memset(gTrainerBattleParameter.data, 0, sizeof(TrainerBattleParameter));
sTrainerBattleEndScript = NULL; sTrainerBattleEndScript = NULL;
@ -966,7 +968,7 @@ static void InitTrainerBattleVariables(void)
void TrainerBattleLoadArgs(const u8 *data) void TrainerBattleLoadArgs(const u8 *data)
{ {
InitTrainerBattleVariables(); InitTrainerBattleParameter();
memcpy(gTrainerBattleParameter.data, data, sizeof(TrainerBattleParameter)); memcpy(gTrainerBattleParameter.data, data, sizeof(TrainerBattleParameter));
sTrainerBattleEndScript = (u8*)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); TRAINER_BATTLE_PARAM.opponentA = GetRematchTrainerId(TRAINER_BATTLE_PARAM.opponentA);
return EventScript_TryDoRematchBattle; return EventScript_TryDoRematchBattle;
#endif //FREE_MATCH_CALL #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: case TRAINER_BATTLE_TWO_TRAINERS_NO_INTRO:
gNoOfApproachingTrainers = 2; // set TWO_OPPONENTS gBattleTypeFlags gNoOfApproachingTrainers = 2; // set TWO_OPPONENTS gBattleTypeFlags
gApproachingTrainerId = 1; // prevent trainer approach 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) void ConfigureAndSetUpOneTrainerBattle(u8 trainerObjEventId, const u8 *trainerScript)
{ {
gSelectedObjectEvent = trainerObjEventId; gSelectedObjectEvent = trainerObjEventId;
@ -1960,3 +1971,15 @@ u16 CountBattledRematchTeams(u16 trainerId)
return i; 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));
};

277
src/battle_special.c Normal file
View File

@ -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
}

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
#include "debug.h" #include "debug.h"
#include "malloc.h" #include "malloc.h"
#include "battle.h" #include "battle.h"
#include "battle_tower.h" #include "battle_special.h"
#include "cable_club.h" #include "cable_club.h"
#include "data.h" #include "data.h"
#include "decoration.h" #include "decoration.h"

View File

@ -2,6 +2,7 @@
#include "follower_npc.h" #include "follower_npc.h"
#include "follower_npc_alternate_sprites.h" #include "follower_npc_alternate_sprites.h"
#include "battle.h" #include "battle.h"
#include "battle_partner.h"
#include "battle_setup.h" #include "battle_setup.h"
#include "battle_tower.h" #include "battle_tower.h"
#include "bike.h" #include "bike.h"

View File

@ -1,9 +1,12 @@
#include "global.h" #include "global.h"
#include "frontier_util.h" #include "frontier_util.h"
#include "easy_chat.h"
#include "event_data.h" #include "event_data.h"
#include "battle_setup.h" #include "battle_setup.h"
#include "overworld.h" #include "overworld.h"
#include "random.h" #include "random.h"
#include "battle_frontier.h"
#include "battle_special.h"
#include "battle_tower.h" #include "battle_tower.h"
#include "field_specials.h" #include "field_specials.h"
#include "battle.h" #include "battle.h"
@ -29,6 +32,7 @@
#include "load_save.h" #include "load_save.h"
#include "battle_dome.h" #include "battle_dome.h"
#include "constants/battle_frontier.h" #include "constants/battle_frontier.h"
#include "constants/battle_frontier_mons.h"
#include "constants/battle_move_effects.h" #include "constants/battle_move_effects.h"
#include "constants/battle_pike.h" #include "constants/battle_pike.h"
#include "constants/frontier_util.h" #include "constants/frontier_util.h"
@ -788,6 +792,31 @@ static const u8 *const sHallFacilityToRecordsText[] =
[RANKING_HALL_TOWER_LINK] = gText_FrontierFacilityWinStreak, [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 #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 tWindowId data[0]
#define tMenuTaskId data[1] #define tMenuTaskId data[1]
#define tArrowTaskId data[2] #define tArrowTaskId data[2]

View File

@ -1,6 +1,6 @@
#include "global.h" #include "global.h"
#include "berry.h" #include "berry.h"
#include "battle_tower.h" #include "battle_special.h"
#include "easy_chat.h" #include "easy_chat.h"
#include "event_data.h" #include "event_data.h"
#include "mail.h" #include "mail.h"

View File

@ -4,7 +4,7 @@
#include "event_data.h" #include "event_data.h"
#include "easy_chat.h" #include "easy_chat.h"
#include "script.h" #include "script.h"
#include "battle_tower.h" #include "battle_special.h"
#include "wonder_news.h" #include "wonder_news.h"
#include "string_util.h" #include "string_util.h"
#include "new_game.h" #include "new_game.h"

View File

@ -3,7 +3,7 @@
#include "decompress.h" #include "decompress.h"
#include "overworld.h" #include "overworld.h"
#include "script.h" #include "script.h"
#include "battle_tower.h" #include "battle_special.h"
#include "mystery_gift.h" #include "mystery_gift.h"
#include "mystery_event_script.h" #include "mystery_event_script.h"
#include "mystery_gift_client.h" #include "mystery_gift_client.h"

View File

@ -12,6 +12,7 @@
#include "battle_tower.h" #include "battle_tower.h"
#include "battle_z_move.h" #include "battle_z_move.h"
#include "data.h" #include "data.h"
#include "frontier_util.h"
#include "daycare.h" #include "daycare.h"
#include "dexnav.h" #include "dexnav.h"
#include "event_data.h" #include "event_data.h"

View File

@ -6,6 +6,7 @@
#include "ereader_helpers.h" #include "ereader_helpers.h"
#include "event_data.h" #include "event_data.h"
#include "event_scripts.h" #include "event_scripts.h"
#include "frontier_util.h"
#include "fieldmap.h" #include "fieldmap.h"
#include "field_message_box.h" #include "field_message_box.h"
#include "international_string_util.h" #include "international_string_util.h"

View File

@ -15,6 +15,7 @@
#include "trainer_hill.h" #include "trainer_hill.h"
#include "util.h" #include "util.h"
#include "battle_pyramid.h" #include "battle_pyramid.h"
#include "constants/battle_frontier.h"
#include "constants/battle_setup.h" #include "constants/battle_setup.h"
#include "constants/event_objects.h" #include "constants/event_objects.h"
#include "constants/event_object_movement.h" #include "constants/event_object_movement.h"
@ -420,6 +421,33 @@ bool8 CheckForTrainersWantingBattle(void)
break; 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) if (gNoOfApproachingTrainers == 1)
{ {
ResetTrainerOpponentIds(); ResetTrainerOpponentIds();