diff --git a/asm/macros/battle_frontier/battle_tower.inc b/asm/macros/battle_frontier/battle_tower.inc index f3abc0cfd4..95ee11c91e 100644 --- a/asm/macros/battle_frontier/battle_tower.inc +++ b/asm/macros/battle_frontier/battle_tower.inc @@ -103,9 +103,8 @@ 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 - special DoSpecialTrainerBattle + callnative BattleSetup_StartMultiBattle waitstate setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SAVE_PARTY special CallFrontierUtilFunc @@ -132,9 +131,8 @@ .endm .macro multi_do_fixed type:req - setvar VAR_0x8004, SPECIAL_BATTLE_MULTI setvar VAR_0x8005, \type - special DoSpecialTrainerBattle + callnative BattleSetup_StartMultiBattle waitstate setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SAVE_PARTY special CallFrontierUtilFunc diff --git a/asm/macros/event.inc b/asm/macros/event.inc index a7f4cbf049..bec437f3a5 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -1599,6 +1599,22 @@ @ Starts a wild battle against the Pokemon generated by setwildbattle. Blocks script execution until the battle finishes. .macro dowildbattle .byte SCR_OP_DOWILDBATTLE + .byte SPECIAL_WILD_NONE + .endm + + .macro startlegendarybattle + .byte SCR_OP_DOWILDBATTLE + .byte SPECIAL_WILD_LEGENDARY + .endm + + .macro startcatchtutorial + .byte SCR_OP_DOWILDBATTLE + .byte SPECIAL_WILD_CATCH_TUTORIAL + .endm + + .macro startghostbattle + .byte SCR_OP_DOWILDBATTLE + .byte SPECIAL_WILD_GHOST .endm @ Sets a relative address to be used by the other vcommands as part of a Mystery Gift script. diff --git a/data/event_scripts.s b/data/event_scripts.s index d3988f44d9..1fa6ad049b 100644 --- a/data/event_scripts.s +++ b/data/event_scripts.s @@ -13,7 +13,6 @@ #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/AncientTomb/scripts.inc b/data/maps/AncientTomb/scripts.inc index dd22359558..6baf2dc995 100644 --- a/data/maps/AncientTomb/scripts.inc +++ b/data/maps/AncientTomb/scripts.inc @@ -63,7 +63,7 @@ AncientTomb_EventScript_Registeel:: waitmoncry setwildbattle SPECIES_REGISTEEL, 40 setflag FLAG_SYS_CTRL_OBJ_DELETE - special StartRegiBattle + startlegendarybattle waitstate clearflag FLAG_SYS_CTRL_OBJ_DELETE specialvar VAR_RESULT, GetBattleOutcome diff --git a/data/maps/BirthIsland_Exterior/scripts.inc b/data/maps/BirthIsland_Exterior/scripts.inc index defec9a46e..22b82fb8b9 100644 --- a/data/maps/BirthIsland_Exterior/scripts.inc +++ b/data/maps/BirthIsland_Exterior/scripts.inc @@ -83,7 +83,7 @@ BirthIsland_Exterior_EventScript_Deoxys:: setvar VAR_LAST_TALKED, LOCALID_BIRTH_ISLAND_DEOXYS seteventmon SPECIES_DEOXYS_NORMAL, 30 setflag FLAG_SYS_CTRL_OBJ_DELETE - special BattleSetup_StartLegendaryBattle + startlegendarybattle waitstate clearflag FLAG_SYS_CTRL_OBJ_DELETE specialvar VAR_RESULT, GetBattleOutcome diff --git a/data/maps/BirthIsland_Exterior_Frlg/scripts.inc b/data/maps/BirthIsland_Exterior_Frlg/scripts.inc index 88979724f4..29a4da86b4 100644 --- a/data/maps/BirthIsland_Exterior_Frlg/scripts.inc +++ b/data/maps/BirthIsland_Exterior_Frlg/scripts.inc @@ -80,7 +80,7 @@ BirthIsland_Exterior_Frlg_EventScript_Deoxys:: setvar VAR_LAST_TALKED, LOCALID_BIRTH_ISLAND_DEOXYS seteventmon SPECIES_DEOXYS, 30 setflag FLAG_SYS_CTRL_OBJ_DELETE - special BattleSetup_StartLegendaryBattle + startlegendarybattle waitstate clearflag FLAG_SYS_CTRL_OBJ_DELETE specialvar VAR_RESULT, GetBattleOutcome diff --git a/data/maps/CeruleanCave_B1F_Frlg/scripts.inc b/data/maps/CeruleanCave_B1F_Frlg/scripts.inc index 1f84bbe95f..945449192a 100644 --- a/data/maps/CeruleanCave_B1F_Frlg/scripts.inc +++ b/data/maps/CeruleanCave_B1F_Frlg/scripts.inc @@ -34,7 +34,7 @@ CeruleanCave_B1F_EventScript_Mewtwo:: waitbuttonpress setwildbattle SPECIES_MEWTWO, 70 setflag FLAG_SYS_CTRL_OBJ_DELETE - special BattleSetup_StartLegendaryBattle + startlegendarybattle waitstate clearflag FLAG_SYS_CTRL_OBJ_DELETE specialvar VAR_RESULT, GetBattleOutcome diff --git a/data/maps/DesertRuins/scripts.inc b/data/maps/DesertRuins/scripts.inc index 21473e87a3..b10ced6afe 100644 --- a/data/maps/DesertRuins/scripts.inc +++ b/data/maps/DesertRuins/scripts.inc @@ -63,7 +63,7 @@ DesertRuins_EventScript_Regirock:: waitmoncry setwildbattle SPECIES_REGIROCK, 40 setflag FLAG_SYS_CTRL_OBJ_DELETE - special StartRegiBattle + startlegendarybattle waitstate clearflag FLAG_SYS_CTRL_OBJ_DELETE specialvar VAR_RESULT, GetBattleOutcome diff --git a/data/maps/FarawayIsland_Interior/scripts.inc b/data/maps/FarawayIsland_Interior/scripts.inc index fe6d35df8a..e3d6631756 100644 --- a/data/maps/FarawayIsland_Interior/scripts.inc +++ b/data/maps/FarawayIsland_Interior/scripts.inc @@ -129,7 +129,7 @@ FarawayIsland_Interior_EventScript_Mew:: waitmoncry seteventmon SPECIES_MEW, 30 setflag FLAG_SYS_CTRL_OBJ_DELETE - special BattleSetup_StartLegendaryBattle + startlegendarybattle waitstate clearflag FLAG_SYS_CTRL_OBJ_DELETE specialvar VAR_RESULT, GetBattleOutcome diff --git a/data/maps/IslandCave/scripts.inc b/data/maps/IslandCave/scripts.inc index 1bfadb9f2e..e897e7d751 100644 --- a/data/maps/IslandCave/scripts.inc +++ b/data/maps/IslandCave/scripts.inc @@ -96,7 +96,7 @@ IslandCave_EventScript_Regice:: waitmoncry setwildbattle SPECIES_REGICE, 40 setflag FLAG_SYS_CTRL_OBJ_DELETE - special StartRegiBattle + startlegendarybattle waitstate clearflag FLAG_SYS_CTRL_OBJ_DELETE specialvar VAR_RESULT, GetBattleOutcome diff --git a/data/maps/MarineCave_End/scripts.inc b/data/maps/MarineCave_End/scripts.inc index c3eb109880..532e236643 100644 --- a/data/maps/MarineCave_End/scripts.inc +++ b/data/maps/MarineCave_End/scripts.inc @@ -35,7 +35,7 @@ MarineCave_End_EventScript_Kyogre:: setvar VAR_LAST_TALKED, LOCALID_MARINE_CAVE_KYOGRE setwildbattle SPECIES_KYOGRE, 70 setflag FLAG_SYS_CTRL_OBJ_DELETE - special BattleSetup_StartLegendaryBattle + startlegendarybattle waitstate clearflag FLAG_SYS_CTRL_OBJ_DELETE setvar VAR_TEMP_1, 0 diff --git a/data/maps/MtEmber_Summit_Frlg/scripts.inc b/data/maps/MtEmber_Summit_Frlg/scripts.inc index 508dd078e2..9bf5d2499a 100644 --- a/data/maps/MtEmber_Summit_Frlg/scripts.inc +++ b/data/maps/MtEmber_Summit_Frlg/scripts.inc @@ -34,7 +34,7 @@ MtEmber_Summit_EventScript_Moltres:: playbgm MUS_RG_ENCOUNTER_GYM_LEADER, 0 waitbuttonpress setflag FLAG_SYS_CTRL_OBJ_DELETE - special BattleSetup_StartLegendaryBattle + startlegendarybattle waitstate clearflag FLAG_SYS_CTRL_OBJ_DELETE specialvar VAR_RESULT, GetBattleOutcome diff --git a/data/maps/NavelRock_Base_Frlg/scripts.inc b/data/maps/NavelRock_Base_Frlg/scripts.inc index 5568a01c5a..9f31070de4 100644 --- a/data/maps/NavelRock_Base_Frlg/scripts.inc +++ b/data/maps/NavelRock_Base_Frlg/scripts.inc @@ -53,7 +53,7 @@ NavelRock_Base_Frlg_EventScript_Lugia:: delay 20 seteventmon SPECIES_LUGIA, 70 setflag FLAG_SYS_CTRL_OBJ_DELETE - special BattleSetup_StartLegendaryBattle + startlegendarybattle waitstate clearflag FLAG_SYS_CTRL_OBJ_DELETE specialvar VAR_RESULT, GetBattleOutcome diff --git a/data/maps/NavelRock_Bottom/scripts.inc b/data/maps/NavelRock_Bottom/scripts.inc index 24c06bf1a0..d09fa50140 100644 --- a/data/maps/NavelRock_Bottom/scripts.inc +++ b/data/maps/NavelRock_Bottom/scripts.inc @@ -53,7 +53,7 @@ NavelRock_Bottom_EventScript_Lugia:: delay 20 seteventmon SPECIES_LUGIA, 70 setflag FLAG_SYS_CTRL_OBJ_DELETE - special BattleSetup_StartLegendaryBattle + startlegendarybattle waitstate clearflag FLAG_SYS_CTRL_OBJ_DELETE specialvar VAR_RESULT, GetBattleOutcome diff --git a/data/maps/NavelRock_Summit_Frlg/scripts.inc b/data/maps/NavelRock_Summit_Frlg/scripts.inc index 637812dacf..b796c86b47 100644 --- a/data/maps/NavelRock_Summit_Frlg/scripts.inc +++ b/data/maps/NavelRock_Summit_Frlg/scripts.inc @@ -57,7 +57,7 @@ NavelRock_Summit_EventScript_HoOh:: special RemoveCameraObject seteventmon SPECIES_HO_OH, 70 setflag FLAG_SYS_CTRL_OBJ_DELETE - special BattleSetup_StartLegendaryBattle + startlegendarybattle waitstate clearflag FLAG_SYS_CTRL_OBJ_DELETE setvar VAR_LAST_TALKED, LOCALID_NAVEL_ROCK_HO_OH diff --git a/data/maps/NavelRock_Top/scripts.inc b/data/maps/NavelRock_Top/scripts.inc index f8c69e039d..1390ef7fee 100644 --- a/data/maps/NavelRock_Top/scripts.inc +++ b/data/maps/NavelRock_Top/scripts.inc @@ -57,7 +57,7 @@ NavelRock_Top_EventScript_HoOh:: special RemoveCameraObject seteventmon SPECIES_HO_OH, 70 setflag FLAG_SYS_CTRL_OBJ_DELETE - special BattleSetup_StartLegendaryBattle + startlegendarybattle waitstate clearflag FLAG_SYS_CTRL_OBJ_DELETE setvar VAR_LAST_TALKED, LOCALID_NAVEL_ROCK_HO_OH diff --git a/data/maps/PetalburgCity/scripts.inc b/data/maps/PetalburgCity/scripts.inc index 879e780228..888eec9448 100644 --- a/data/maps/PetalburgCity/scripts.inc +++ b/data/maps/PetalburgCity/scripts.inc @@ -37,7 +37,8 @@ PetalburgCity_EventScript_WallyTutorial:: applymovement LOCALID_PLAYER, PetalburgCity_Movement_WallyTutorialPlayer waitmovement 0 msgbox Route102_Text_WatchMeCatchPokemon, MSGBOX_DEFAULT - special StartWallyTutorialBattle + createmon 1, 0, SPECIES_RALTS, 5, gender=MON_MALE + startcatchtutorial waitstate msgbox Route102_Text_WallyIDidIt, MSGBOX_DEFAULT applymovement LOCALID_PETALBURG_WALLY, Common_Movement_WalkInPlaceFasterLeft, MAP_PETALBURG_CITY diff --git a/data/maps/PokemonTower_6F_Frlg/scripts.inc b/data/maps/PokemonTower_6F_Frlg/scripts.inc index 86db6561e6..2ece0ef2bb 100644 --- a/data/maps/PokemonTower_6F_Frlg/scripts.inc +++ b/data/maps/PokemonTower_6F_Frlg/scripts.inc @@ -5,8 +5,8 @@ PokemonTower_6F_EventScript_MarowakGhost:: lockall textcolor NPC_TEXT_COLOR_MON msgbox PokemonTower_6F_Text_BeGoneIntruders - setwildbattle SPECIES_MAROWAK, 30 - special StartMarowakBattle + createmon 1, 0, SPECIES_MAROWAK, 30, gender=MON_FEMALE, nature=NATURE_SERIOUS, hpIv=31, atkIv=31, defIv=31, speedIv=31, spAtkIv=31, spDefIv=31 + startghostbattle waitstate goto_if_eq VAR_RESULT, FALSE, PokemonTower_6F_EventScript_DefeatedMarowakGhost @ VAR_RESULT set by CB2_EndMarowakBattle applymovement LOCALID_PLAYER, PokemonTower_6F_Movement_ForcePlayerUp diff --git a/data/maps/PowerPlant_Frlg/scripts.inc b/data/maps/PowerPlant_Frlg/scripts.inc index 3a11d69d1b..acee06e485 100644 --- a/data/maps/PowerPlant_Frlg/scripts.inc +++ b/data/maps/PowerPlant_Frlg/scripts.inc @@ -45,7 +45,7 @@ PowerPlant_EventScript_Zapdos:: playbgm MUS_RG_ENCOUNTER_GYM_LEADER, 0 waitbuttonpress setflag FLAG_SYS_CTRL_OBJ_DELETE - special BattleSetup_StartLegendaryBattle + startlegendarybattle waitstate clearflag FLAG_SYS_CTRL_OBJ_DELETE specialvar VAR_RESULT, GetBattleOutcome diff --git a/data/maps/SeafoamIslands_B4F_Frlg/scripts.inc b/data/maps/SeafoamIslands_B4F_Frlg/scripts.inc index 8b92a9dea6..db39598c3c 100644 --- a/data/maps/SeafoamIslands_B4F_Frlg/scripts.inc +++ b/data/maps/SeafoamIslands_B4F_Frlg/scripts.inc @@ -164,7 +164,7 @@ SeafoamIslands_B4F_EventScript_Articuno:: playbgm MUS_RG_ENCOUNTER_GYM_LEADER, 0 waitbuttonpress setflag FLAG_SYS_CTRL_OBJ_DELETE - special BattleSetup_StartLegendaryBattle + startlegendarybattle waitstate clearflag FLAG_SYS_CTRL_OBJ_DELETE specialvar VAR_RESULT, GetBattleOutcome diff --git a/data/maps/SkyPillar_Top/scripts.inc b/data/maps/SkyPillar_Top/scripts.inc index 3975cc0512..931f60d917 100644 --- a/data/maps/SkyPillar_Top/scripts.inc +++ b/data/maps/SkyPillar_Top/scripts.inc @@ -48,7 +48,7 @@ SkyPillar_Top_EventScript_Rayquaza:: waitmoncry setwildbattle SPECIES_RAYQUAZA, 70 setflag FLAG_SYS_CTRL_OBJ_DELETE - special BattleSetup_StartLegendaryBattle + startlegendarybattle waitstate clearflag FLAG_SYS_CTRL_OBJ_DELETE specialvar VAR_RESULT, GetBattleOutcome diff --git a/data/maps/SouthernIsland_Interior/scripts.inc b/data/maps/SouthernIsland_Interior/scripts.inc index f92d3f080e..0ea8536807 100644 --- a/data/maps/SouthernIsland_Interior/scripts.inc +++ b/data/maps/SouthernIsland_Interior/scripts.inc @@ -75,7 +75,7 @@ SouthernIsland_Interior_EventScript_Lati:: call_if_eq VAR_ROAMER_POKEMON, 0, SouthernIsland_Interior_EventScript_SetLatiosBattleVars call_if_ne VAR_ROAMER_POKEMON, 0, SouthernIsland_Interior_EventScript_SetLatiasBattleVars setflag FLAG_SYS_CTRL_OBJ_DELETE - special BattleSetup_StartLatiBattle + startlegendarybattle waitstate clearflag FLAG_SYS_CTRL_OBJ_DELETE specialvar VAR_RESULT, GetBattleOutcome diff --git a/data/maps/TerraCave_End/scripts.inc b/data/maps/TerraCave_End/scripts.inc index e1e1cd684b..1fbb947356 100644 --- a/data/maps/TerraCave_End/scripts.inc +++ b/data/maps/TerraCave_End/scripts.inc @@ -35,7 +35,7 @@ TerraCave_End_EventScript_Groudon:: setvar VAR_LAST_TALKED, LOCALID_TERRA_CAVE_GROUDON setwildbattle SPECIES_GROUDON, 70 setflag FLAG_SYS_CTRL_OBJ_DELETE - special BattleSetup_StartLegendaryBattle + startlegendarybattle waitstate clearflag FLAG_SYS_CTRL_OBJ_DELETE setvar VAR_TEMP_1, 0 diff --git a/data/maps/ViridianCity_Frlg/scripts.inc b/data/maps/ViridianCity_Frlg/scripts.inc index d77e34c990..6d75697190 100644 --- a/data/maps/ViridianCity_Frlg/scripts.inc +++ b/data/maps/ViridianCity_Frlg/scripts.inc @@ -131,14 +131,6 @@ ViridianCity_EventScript_WatchToLearnBasics:: release end -@ Unused. Starts battle after the post battle text, which is odd. -ViridianCity_EventScript_TutorialUnused:: - msgbox ViridianCity_Text_ThatWasEducationalTakeThis - special StartOldManTutorialBattle - waitstate - release - end - ViridianCity_EventScript_TutorialNotReady:: msgbox ViridianCity_Text_ThisIsPrivateProperty closemessage @@ -222,7 +214,8 @@ ViridianCity_EventScript_TutorialTriggerRight:: ViridianCity_EventScript_DoTutorialBattle:: msgbox ViridianCity_Text_ShowYouHowToCatchMons closemessage - special StartOldManTutorialBattle + createmon 1, 0, SPECIES_WEEDLE, 5, gender=MON_MALE + startcatchtutorial waitstate lock faceplayer diff --git a/data/scripts/debug.inc b/data/scripts/debug.inc index 2539eb188b..8aa2734000 100644 --- a/data/scripts/debug.inc +++ b/data/scripts/debug.inc @@ -362,7 +362,8 @@ Debug_EventScript_Steven_Multi:: Debug_EventScript_WallyTutorial:: special SavePlayerParty special LoadWallyZigzagoon - special StartWallyTutorialBattle + createmon 1, 0, SPECIES_RALTS, 5, gender=MON_MALE + startcatchtutorial waitstate special LoadPlayerParty release diff --git a/data/specials.inc b/data/specials.inc index 0eecbb2d6d..41dfd3598c 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -175,7 +175,6 @@ gSpecials:: def_special StartWallClock def_special Special_ViewWallClock def_special ChooseStarter - def_special StartWallyTutorialBattle def_special ChangePokemonNickname def_special ChoosePartyMon def_special GetFirstFreePokeblockSlot @@ -328,9 +327,6 @@ gSpecials:: def_special IsPokerusInParty def_special SetSootopolisGymCrackedIceMetatiles def_special ShakeCamera - def_special StartGroudonKyogreBattle - def_special BattleSetup_StartLegendaryBattle - def_special StartRegiBattle def_special SetTrainerFacingDirection def_special DoSealedChamberShakingEffect_Short def_special FoundBlackGlasses @@ -340,7 +336,6 @@ gSpecials:: def_special ShowContestEntryMonPic def_special HideContestEntryMonPic def_special SetEReaderTrainerGfxId - def_special BattleSetup_StartLatiBattle def_special SetRoute119Weather def_special SetRoute123Weather def_special GetContestMultiplayerId @@ -575,7 +570,6 @@ gSpecials:: def_special SetFlavorTextFlagFromSpecialVars def_special DisableMsgBoxWalkaway def_special SetWalkingIntoSignVars - def_special StartOldManTutorialBattle def_special DaisyMassageServices def_special GetLeadMonFriendship def_special OpenMuseumFossilPic @@ -597,7 +591,6 @@ gSpecials:: def_special IsPlayerLeftOfVermilionSailor def_special SetVermilionTrashCans def_special DoSSAnneDepartureCutscene - def_special StartMarowakBattle def_special DoesPlayerPartyContainSpecies def_special GetMagikarpSizeRecordInfo def_special CompareMagikarpSize diff --git a/include/battle_main.h b/include/battle_main.h index 6a79b2aad3..801ae1992e 100644 --- a/include/battle_main.h +++ b/include/battle_main.h @@ -107,9 +107,7 @@ void RunBattleScriptCommands(void); enum Type GetDynamicMoveType(struct Pokemon *mon, enum Move move, enum BattlerId battler, enum MonState monInBattle); void SetTypeBeforeUsingMove(enum Move move, enum BattlerId battler); bool32 IsWildMonSmart(void); -u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags); void ModifyPersonalityForNature(u32 *personality, u32 newNature); -u32 GeneratePersonalityForGender(u32 gender, enum Species species); void CustomTrainerPartyAssignMoves(struct Pokemon *mon, const struct TrainerMon *partyEntry); bool32 CanPlayerForfeitNormalTrainerBattle(void); bool32 DidPlayerForfeitNormalTrainerBattle(void); diff --git a/include/battle_setup.h b/include/battle_setup.h index f5891536ef..9f190aeea9 100644 --- a/include/battle_setup.h +++ b/include/battle_setup.h @@ -2,6 +2,7 @@ #define GUARD_BATTLE_SETUP_H #include "battle_transition.h" +#include "data.h" #include "gym_leader_rematch.h" #define REMATCHES_COUNT 5 @@ -50,17 +51,8 @@ extern u16 gPartnerTrainerId; #define TRAINER_BATTLE_PARAM gTrainerBattleParameter.params -void BattleSetup_StartWildBattle(void); -void BattleSetup_StartDoubleWildBattle(void); -void BattleSetup_StartBattlePikeWildBattle(void); -void BattleSetup_StartRoamerBattle(void); -void StartWallyTutorialBattle(void); -void BattleSetup_StartScriptedWildBattle(void); -void BattleSetup_StartScriptedDoubleWildBattle(void); -void BattleSetup_StartLatiBattle(void); -void BattleSetup_StartLegendaryBattle(void); -void StartGroudonKyogreBattle(void); -void StartRegiBattle(void); +void BattleSetup_StartWildBattle(bool32 isDouble); +void DoBattleSetup(bool32 fromScript); enum BattleEnvironments BattleSetup_GetEnvironmentId(void); enum BattleTransition GetWildBattleTransition(void); enum BattleTransition GetTrainerBattleTransition(void); @@ -106,8 +98,6 @@ void ShouldTryGetTrainerScript(void); u16 CountMaxPossibleRematch(u16 trainerId); u16 CountBattledRematchTeams(u16 trainerId); void TrainerBattleLoadArgs(const u8 *data); -void TrainerBattleLoadArgsTrainerA(const u8 *data); -void TrainerBattleLoadArgsTrainerB(const u8 *data); void TrainerBattleLoadArgsSecondTrainer(const u8 *data); void InitTrainerBattleParameter(void); @@ -118,4 +108,6 @@ s32 FirstBattleTrainerIdToRematchTableId(const struct RematchTrainer *table, u16 u16 GetRematchTrainerIdFromTable(const struct RematchTrainer *table, u16 firstBattleTrainerId); u8 GetRivalBattleFlags(void); +void CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer); + #endif // GUARD_BATTLE_SETUP_H diff --git a/include/config/battle.h b/include/config/battle.h index ff9bafe78f..39fbb4db11 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -357,7 +357,7 @@ #define B_WILD_NATURAL_ENEMIES TRUE // If set to TRUE, certain wild mon species will attack other species when partnered in double wild battles (eg. Zangoose vs Seviper) #define B_AFFECTION_MECHANICS TRUE // In Gen6+, there's a stat called affection that can trigger different effects in battle. From LGPE onwards, those effects use friendship instead. #define B_TRAINER_CLASS_POKE_BALLS GEN_LATEST // In Gen7+, trainers will use certain types of Poké Balls depending on their trainer class. -#define B_TRAINER_MON_RANDOM_ABILITY FALSE // If this is set to TRUE a random legal ability will be generated for a trainer mon +#define B_TRAINER_MON_HIDDEN_ABILITY FALSE // If this is set to TRUE, hidden ability can be randomly rolled for trainers/partners who do not have a set ability. If FALSE, it's still random but can't get an hidden ability. #define B_OBEDIENCE_MECHANICS GEN_LATEST // In PLA+ (here Gen8+), obedience restrictions also apply to non-outsider Pokémon, albeit based on their level met rather than actual level #define B_USE_FROSTBITE FALSE // In PLA, Frostbite replaces Freeze. Enabling this flag does the same here. Moves can still be cherry-picked to either Freeze or Frostbite. Freeze-Dry, Secret Power & Tri Attack depend on this config. #define B_TOXIC_REVERSAL GEN_LATEST // In Gen5+, bad poison will change to regular poison at the end of battles. diff --git a/include/constants/battle.h b/include/constants/battle.h index 01da04db4f..a3c3e18e9b 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -76,6 +76,7 @@ enum BattleSide #define BIT_FLANK 2 // Battle Type Flags +#define BATTLE_TYPE_BASIC_WILD 0 #define BATTLE_TYPE_DOUBLE (1 << 0) #define BATTLE_TYPE_LINK (1 << 1) #define BATTLE_TYPE_IS_MASTER (1 << 2) // In not-link battles, it's always set. @@ -118,7 +119,7 @@ enum BattleSide #define WILD_DOUBLE_BATTLE ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER)))) #define RECORDED_WILD_BATTLE ((gBattleTypeFlags & BATTLE_TYPE_RECORDED) && !(gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FRONTIER))) -#define BATTLE_TWO_VS_ONE_OPPONENT ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && TRAINER_BATTLE_PARAM.opponentB == 0xFFFF)) +#define BATTLE_TWO_VS_ONE_OPPONENT ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && TRAINER_BATTLE_PARAM.opponentB == TRAINER_NONE)) #define BATTLE_TYPE_HAS_AI (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER | BATTLE_TYPE_INGAME_PARTNER) #define BATTLE_TYPE_MORE_THAN_TWO_BATTLERS (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_TWO_OPPONENTS) #define BATTLE_TYPE_PLAYER_HAS_PARTNER (BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_TOWER_LINK_MULTI) diff --git a/include/constants/battle_setup.h b/include/constants/battle_setup.h index 541d2f68b4..d32643d82d 100644 --- a/include/constants/battle_setup.h +++ b/include/constants/battle_setup.h @@ -13,4 +13,14 @@ #define TRAINER_BATTLE_TWO_TRAINERS_NO_INTRO 13 #define TRAINER_BATTLE_EARLY_RIVAL 14 +#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 + +#define SPECIAL_WILD_NONE 0 +#define SPECIAL_WILD_LEGENDARY 1 +#define SPECIAL_WILD_GHOST 2 +#define SPECIAL_WILD_CATCH_TUTORIAL 3 + #endif // GUARD_CONSTANTS_BATTLE_SETUP_H diff --git a/include/constants/battle_special.h b/include/constants/battle_special.h deleted file mode 100644 index 82d70a8efe..0000000000 --- a/include/constants/battle_special.h +++ /dev/null @@ -1,10 +0,0 @@ -#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/pokemon.h b/include/pokemon.h index 282796eec5..4d7724414e 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -738,7 +738,6 @@ void CreateBoxMon(struct BoxPokemon *boxMon, enum Species species, u8 level, u32 void CreateMonWithIVs(struct Pokemon *mon, enum Species species, u8 level, u32 personality, struct OriginalTrainerId trainerId, u8 fixedIV); void SetBoxMonIVs(struct BoxPokemon *mon, u8 fixedIV); void SetBoxMonPerfectIVs(struct BoxPokemon *mon, u32 numPerfect); -void CreateMaleMon(struct Pokemon *mon, enum Species species, u8 level); void CreateMonWithIVsPersonality(struct Pokemon *mon, enum Species species, u8 level, u32 ivs, u32 personality); void CreateBattleTowerMon(struct Pokemon *mon, struct BattleTowerPokemon *src); void CreateBattleTowerMon_HandleLevel(struct Pokemon *mon, struct BattleTowerPokemon *src, bool8 lvl50); diff --git a/include/script_pokemon_util.h b/include/script_pokemon_util.h index bec3b87f21..ecc3192e5e 100644 --- a/include/script_pokemon_util.h +++ b/include/script_pokemon_util.h @@ -4,7 +4,7 @@ u32 ScriptGiveMon(enum Species species, u8 level, enum Item item); u8 ScriptGiveEgg(enum Species species); void CreateScriptedWildMon(enum Species species, u8 level, enum Item item); -void CreateScriptedDoubleWildMon(enum Species species, u8 level, enum Item item, enum Species species2, u8 level2, enum Item item2); +void CreateStaticWildMon(struct Pokemon *mon, enum Species species, u8 level, enum Item item); void ScriptSetMonMoveSlot(u8 monIndex, enum Move move, u8 slot); void ReducePlayerPartyToSelectedMons(void); void HealPlayerParty(void); diff --git a/include/trainer_util.h b/include/trainer_util.h new file mode 100644 index 0000000000..76bfd0b6f8 --- /dev/null +++ b/include/trainer_util.h @@ -0,0 +1,19 @@ +#ifndef GUARD_TRAINER_UTIL_H +#define GUARD_TRAINER_UTIL_H + +struct TrainerGenerator +{ + u8 gender:7; + u8 isFrontier:1; + u8 name[TRAINER_NAME_LENGTH + 1]; + u8 trainerClass; + struct OriginalTrainerId otID; + rng_value_t localRngState; +}; + +u32 Crc32B(const u8 *data, u32 size); +rng_value_t GeneratePartySeed(const struct Trainer *trainer); +void GenerateMonFromTrainerMon(struct Pokemon *mon, const struct TrainerMon *trainerMon, struct TrainerGenerator *trainer); +u32 GeneratePersonalityForGender(u32 gender, u32 species); + +#endif // GUARD_TRAINER_UTIL_H diff --git a/include/wild_encounter.h b/include/wild_encounter.h index db402ee553..ff295c018d 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -58,7 +58,6 @@ u16 GetLocalWildMon(bool8 *isWaterMon); u16 GetLocalWaterMon(void); bool8 UpdateRepelCounter(void); bool8 TryDoDoubleWildBattle(void); -bool8 StandardWildEncounter_Debug(void); u32 CalculateChainFishingShinyRolls(void); void CreateWildMon(enum Species species, u8 level); u16 GetCurrentMapWildMonHeaderId(void); diff --git a/migration_scripts/1.16/battle_setup_rework.py b/migration_scripts/1.16/battle_setup_rework.py new file mode 100644 index 0000000000..b3f5e6af72 --- /dev/null +++ b/migration_scripts/1.16/battle_setup_rework.py @@ -0,0 +1,26 @@ +from itertools import chain + +import glob +import os + +replacements = [ + ["special BattleSetup_StartLatiBattle", "startlegendarybattle"], + ["special StartRegiBattle", "startlegendarybattle"], + ["special BattleSetup_StartLegendaryBattle", "startlegendarybattle"], +] + +if not os.path.exists("Makefile"): + print("Please run this script from your root folder.") + quit() + +for inc_fname in chain(glob.glob("./data/scripts/*.inc"), glob.glob("./data/maps/*/scripts.inc")): + new_lines = [] + with open(inc_fname, "r") as inc_fp: + lines = inc_fp.readlines() + for line in lines: + for replacement in replacements: + line = line.replace(replacement[0], replacement[1]) + new_lines.append(line) + with open(inc_fname, 'w+') as file: + for line in new_lines: + file.write(line) \ No newline at end of file diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 19d1792ac6..173cf77583 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -315,7 +315,7 @@ void BattleAI_SetupFlags(void) else { gAiThinkingStruct->aiFlags[B_BATTLER_1] = GetAiFlags(TRAINER_BATTLE_PARAM.opponentA, B_BATTLER_1); - if ((TRAINER_BATTLE_PARAM.opponentB != 0) && (TRAINER_BATTLE_PARAM.opponentB != 0xFFFF)) + if (TRAINER_BATTLE_PARAM.opponentB != TRAINER_NONE) gAiThinkingStruct->aiFlags[B_BATTLER_3] = GetAiFlags(TRAINER_BATTLE_PARAM.opponentB, B_BATTLER_3); else gAiThinkingStruct->aiFlags[B_BATTLER_3] = gAiThinkingStruct->aiFlags[B_BATTLER_1]; diff --git a/src/battle_end_turn.c b/src/battle_end_turn.c index c709281e61..75725a80bf 100644 --- a/src/battle_end_turn.c +++ b/src/battle_end_turn.c @@ -1407,8 +1407,7 @@ static bool32 HandleEndTurnTrainerBSlides(enum BattlerId battler) if (slide == TRUE) { if ((TRAINER_BATTLE_PARAM.opponentB == TRAINER_BATTLE_PARAM.opponentA) - || (TRAINER_BATTLE_PARAM.opponentB == TRAINER_NONE) - || (TRAINER_BATTLE_PARAM.opponentB == 0xFFFF)) + || (TRAINER_BATTLE_PARAM.opponentB == TRAINER_NONE)) BattleScriptExecute(BattleScript_TrainerASlideMsgEnd2); else BattleScriptExecute(BattleScript_TrainerBSlideMsgEnd2); diff --git a/src/battle_frontier.c b/src/battle_frontier.c index 6c2f8a8703..3dd5583d2a 100644 --- a/src/battle_frontier.c +++ b/src/battle_frontier.c @@ -16,6 +16,7 @@ #include "string_util.h" #include "task.h" #include "text.h" +#include "trainer_util.h" #include "constants/abilities.h" #include "constants/battle_frontier.h" #include "constants/battle_frontier_mons.h" diff --git a/src/battle_main.c b/src/battle_main.c index bce94d9193..0c99e07dd8 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -58,7 +58,7 @@ #include "test/battle.h" #include "test_runner.h" #include "text.h" -#include "trainer_pools.h" +#include "trainer_util.h" #include "trig.h" #include "tv.h" #include "util.h" @@ -91,7 +91,6 @@ static void CB2_HandleStartMultiPartnerBattle(void); static void CB2_HandleStartMultiBattle(void); static void CB2_HandleStartBattle(void); static void TryCorrectShedinjaLanguage(struct Pokemon *mon); -static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer); static void BattleMainCB1(void); static void CB2_EndLinkBattle(void); static void EndLinkBattleInSteps(void); @@ -128,8 +127,6 @@ static void HandleEndTurn_BattleLost(void); static void HandleEndTurn_RanFromBattle(void); static void HandleEndTurn_MonFled(void); static void HandleEndTurn_FinishBattle(void); -static u32 Crc32B (const u8 *data, u32 size); -static u32 GeneratePartyHash(const struct Trainer *trainer, u32 i); EWRAM_DATA u16 gBattle_BG0_X = 0; EWRAM_DATA u16 gBattle_BG0_Y = 0; @@ -593,18 +590,6 @@ static void CB2_InitBattleInternal(void) else SetMainCallback2(CB2_HandleStartBattle); - if (!DEBUG_OVERWORLD_MENU || (DEBUG_OVERWORLD_MENU && !gIsDebugBattle)) - { - if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED))) - { - CreateNPCTrainerParty(&gEnemyParty[0], TRAINER_BATTLE_PARAM.opponentA, TRUE); - if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && !BATTLE_TWO_VS_ONE_OPPONENT) - CreateNPCTrainerParty(&gEnemyParty[PARTY_SIZE / 2], TRAINER_BATTLE_PARAM.opponentB, FALSE); - SetWildMonHeldItem(); - CalculateEnemyPartyCount(); - } - } - gMain.inBattle = TRUE; gSaveBlock2Ptr->frontier.disableRecordBattle = FALSE; @@ -619,14 +604,16 @@ static void CB2_InitBattleInternal(void) if (!(gBattleTypeFlags & BATTLE_TYPE_TRAINER)) { + if (!(gBattleTypeFlags & (BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_PYRAMID | BATTLE_TYPE_PIKE))) + SetWildMonHeldItem(); TryFormChange(&gEnemyParty[0], FORM_CHANGE_BEGIN_WILD_ENCOUNTER); if (IsDoubleBattle()) TryFormChange(&gEnemyParty[1], FORM_CHANGE_BEGIN_WILD_ENCOUNTER); } + CalculateEnemyPartyCount(); #if TESTING gPlayerPartyCount = CalculatePartyCount(gPlayerParty); - gEnemyPartyCount = CalculatePartyCount(gEnemyParty); #endif gBattleCommunication[MULTIUSE_STATE] = 0; @@ -1848,33 +1835,6 @@ void CB2_QuitRecordedBattle(void) } } -static u32 Crc32B (const u8 *data, u32 size) -{ - s32 i, j; - u32 byte, crc, mask; - - i = 0; - crc = 0xFFFFFFFF; - for (i = 0; i < size; ++i) - { - byte = data[i]; - crc = crc ^ byte; - for (j = 7; j >= 0; --j) - { - mask = -(crc & 1); - crc = (crc >> 1) ^ (0xEDB88320 & mask); - } - } - return ~crc; -} - -static u32 GeneratePartyHash(const struct Trainer *trainer, u32 i) -{ - const u8 *buffer = (const u8 *) &trainer->party[i]; - u32 n = sizeof(*trainer->party); - return Crc32B(buffer, n); -} - void ModifyPersonalityForNature(u32 *personality, u32 newNature) { u32 nature = GetNatureFromPersonality(*personality); @@ -1888,214 +1848,6 @@ void ModifyPersonalityForNature(u32 *personality, u32 newNature) *personality -= (diff * sign); } -u32 GeneratePersonalityForGender(u32 gender, enum Species species) -{ - const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[species]; - if (gender == MON_GENDERLESS) - return 0; - else if (gender == MON_MALE) - return ((255 - speciesInfo->genderRatio) / 2) + speciesInfo->genderRatio; - else - return speciesInfo->genderRatio / 2; -} - -void CustomTrainerPartyAssignMoves(struct Pokemon *mon, const struct TrainerMon *partyEntry) -{ - bool32 noMoveSet = TRUE; - u32 j; - - for (j = 0; j < MAX_MON_MOVES; ++j) - { - if (partyEntry->moves[j] != MOVE_NONE) - noMoveSet = FALSE; - } - if (noMoveSet) - { - GiveMonInitialMoveset(mon); - // TODO: Figure out a default strategy when moves are not set, to generate a good moveset - return; - } - - for (j = 0; j < MAX_MON_MOVES; ++j) - { - u32 pp = GetMovePP(partyEntry->moves[j]); - SetMonData(mon, MON_DATA_MOVE1 + j, &partyEntry->moves[j]); - SetMonData(mon, MON_DATA_PP1 + j, &pp); - } -} - -u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags) -{ - u32 personalityValue; - s32 i; - u8 monsCount; - if (battleTypeFlags & BATTLE_TYPE_TRAINER && !(battleTypeFlags & (BATTLE_TYPE_FRONTIER - | BATTLE_TYPE_EREADER_TRAINER - | BATTLE_TYPE_TRAINER_HILL))) - { - if (firstTrainer == TRUE) - ZeroEnemyPartyMons(); - - if (battleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) - { - if (trainer->partySize > PARTY_SIZE / 2) - monsCount = PARTY_SIZE / 2; - else - monsCount = trainer->partySize; - } - else - { - monsCount = trainer->partySize; - } - - u32 monIndices[monsCount]; - DoTrainerPartyPool(trainer, monIndices, monsCount, battleTypeFlags); - - for (i = 0; i < monsCount; i++) - { - u32 monIndex = monIndices[i]; - s32 ball = -1; - u32 personalityHash = GeneratePartyHash(trainer, i); - const struct TrainerMon *partyData = trainer->party; - struct OriginalTrainerId otId = OTID_STRUCT_RANDOM_NO_SHINY; - u32 abilityNum = 0; - - if (trainer->battleType != TRAINER_BATTLE_TYPE_SINGLES) - personalityValue = 0x80; - else if (trainer->gender == TRAINER_GENDER_FEMALE) - personalityValue = 0x78; // Use personality more likely to result in a female Pokémon - else - personalityValue = 0x88; // Use personality more likely to result in a male Pokémon - - personalityValue += personalityHash << 8; - if (partyData[monIndex].gender == TRAINER_MON_MALE) - personalityValue = (personalityValue & 0xFFFFFF00) | GeneratePersonalityForGender(MON_MALE, partyData[monIndex].species); - else if (partyData[monIndex].gender == TRAINER_MON_FEMALE) - personalityValue = (personalityValue & 0xFFFFFF00) | GeneratePersonalityForGender(MON_FEMALE, partyData[monIndex].species); - else if (partyData[monIndex].gender == TRAINER_MON_RANDOM_GENDER) - personalityValue = (personalityValue & 0xFFFFFF00) | GeneratePersonalityForGender(Random() & 1 ? MON_MALE : MON_FEMALE, partyData[monIndex].species); - ModifyPersonalityForNature(&personalityValue, partyData[monIndex].nature); - if (partyData[monIndex].isShiny) - { - otId.method = OT_ID_PRESET; - otId.value = HIHALF(personalityValue) ^ LOHALF(personalityValue); - } - CreateMon(&party[i], partyData[monIndex].species, partyData[monIndex].lvl, personalityValue, otId); - SetMonData(&party[i], MON_DATA_HELD_ITEM, &partyData[monIndex].heldItem); - - CustomTrainerPartyAssignMoves(&party[i], &partyData[monIndex]); - SetMonData(&party[i], MON_DATA_IVS, &(partyData[monIndex].iv)); - if (partyData[monIndex].ev != NULL) - { - SetMonData(&party[i], MON_DATA_HP_EV, &(partyData[monIndex].ev[0])); - SetMonData(&party[i], MON_DATA_ATK_EV, &(partyData[monIndex].ev[1])); - SetMonData(&party[i], MON_DATA_DEF_EV, &(partyData[monIndex].ev[2])); - SetMonData(&party[i], MON_DATA_SPATK_EV, &(partyData[monIndex].ev[3])); - SetMonData(&party[i], MON_DATA_SPDEF_EV, &(partyData[monIndex].ev[4])); - SetMonData(&party[i], MON_DATA_SPEED_EV, &(partyData[monIndex].ev[5])); - } - if (partyData[monIndex].ability != ABILITY_NONE) - { - const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[partyData[monIndex].species]; - u32 maxAbilityNum = ARRAY_COUNT(speciesInfo->abilities); - for (abilityNum = 0; abilityNum < maxAbilityNum; ++abilityNum) - { - if (speciesInfo->abilities[abilityNum] == partyData[monIndex].ability) - break; - } - assertf(abilityNum < maxAbilityNum, "illegal ability %S for %S", gAbilitiesInfo[partyData[monIndex].ability].name, speciesInfo->speciesName); - } - else if (B_TRAINER_MON_RANDOM_ABILITY) - { - const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[partyData[monIndex].species]; - abilityNum = personalityHash % 3; - while (speciesInfo->abilities[abilityNum] == ABILITY_NONE) - { - abilityNum--; - } - } - SetMonData(&party[i], MON_DATA_ABILITY_NUM, &abilityNum); - SetMonData(&party[i], MON_DATA_FRIENDSHIP, &(partyData[monIndex].friendship)); - if (partyData[monIndex].ball < POKEBALL_COUNT) - { - ball = partyData[monIndex].ball; - SetMonData(&party[i], MON_DATA_POKEBALL, &ball); - } - if (partyData[monIndex].nickname != NULL) - { - SetMonData(&party[i], MON_DATA_NICKNAME, partyData[monIndex].nickname); - } - if (partyData[monIndex].isShiny) - { - bool32 data = TRUE; - SetMonData(&party[i], MON_DATA_IS_SHINY, &data); - } - if (partyData[monIndex].dynamaxLevel > 0) - { - u32 data = partyData[monIndex].dynamaxLevel; - if (partyData[monIndex].shouldUseDynamax) - gBattleStruct->opponentMonCanDynamax |= 1 << i; - SetMonData(&party[i], MON_DATA_DYNAMAX_LEVEL, &data); - } - if (partyData[monIndex].gigantamaxFactor) - { - u32 data = partyData[monIndex].gigantamaxFactor; - SetMonData(&party[i], MON_DATA_GIGANTAMAX_FACTOR, &data); - } - if (partyData[monIndex].teraType > 0) - { - gBattleStruct->opponentMonCanTera |= 1 << i; - enum Type data = partyData[monIndex].teraType; - SetMonData(&party[i], MON_DATA_TERA_TYPE, &data); - } - CalculateMonStats(&party[i]); - - if (B_TRAINER_CLASS_POKE_BALLS >= GEN_7 && ball == -1) - { - ball = gTrainerClasses[trainer->trainerClass].ball ?: ITEM_POKE_BALL; - SetMonData(&party[i], MON_DATA_POKEBALL, &ball); - } - } - } - - return trainer->partySize; -} - -static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer) -{ - u8 retVal; - if (trainerNum == TRAINER_SECRET_BASE) - return 0; - if (GetTrainerStructFromId(trainerNum)->overrideTrainer) - { - struct Trainer tempTrainer; - memcpy(&tempTrainer, GetTrainerStructFromId(trainerNum), sizeof(struct Trainer)); - const struct Trainer *origTrainer = GetTrainerStructFromId(tempTrainer.overrideTrainer); - - tempTrainer.party = origTrainer->party; - - tempTrainer.poolSize = origTrainer->poolSize; - if (tempTrainer.partySize == 0) - tempTrainer.partySize = origTrainer->partySize; - - retVal = CreateNPCTrainerPartyFromTrainer(party, (const struct Trainer *)(&tempTrainer), firstTrainer, gBattleTypeFlags); - } - else - { - retVal = CreateNPCTrainerPartyFromTrainer(party, GetTrainerStructFromId(trainerNum), firstTrainer, gBattleTypeFlags); - } - return retVal; -} - -void CreateTrainerPartyForPlayer(void) -{ - Script_RequestEffects(SCREFF_V1); - - ZeroPlayerPartyMons(); - gPartnerTrainerId = gSpecialVar_0x8004; - CreateNPCTrainerPartyFromTrainer(gPlayerParty, GetTrainerStructFromId(gSpecialVar_0x8004), TRUE, BATTLE_TYPE_TRAINER); -} - void VBlankCB_Battle(void) { // Change gRngSeed every vblank unless the battle could be recorded. @@ -3740,7 +3492,7 @@ static void DoBattleIntro(void) if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) { statusesOpponentA = GetTrainerStartingStatusFromId(TRAINER_BATTLE_PARAM.opponentA); - if (TRAINER_BATTLE_PARAM.opponentB != 0xFFFF) + if (TRAINER_BATTLE_PARAM.opponentB != TRAINER_NONE) statusesOpponentB = GetTrainerStartingStatusFromId(TRAINER_BATTLE_PARAM.opponentB); } STARTING_STATUS_DEFINITIONS(UNPACK_STARTING_STATUS_TO_BATTLE); @@ -3847,8 +3599,7 @@ static void TryDoEventsBeforeFirstTurn(void) { // Ensures only trainer A slide is played in single-trainer doubles (B == A / B == TRAINER_NONE) and 2v1 multibattles (B == 0xFFFF) if (!((TRAINER_BATTLE_PARAM.opponentB == TRAINER_BATTLE_PARAM.opponentA) - || (TRAINER_BATTLE_PARAM.opponentB == TRAINER_NONE) - || (TRAINER_BATTLE_PARAM.opponentB == 0xFFFF))) + || (TRAINER_BATTLE_PARAM.opponentB == TRAINER_NONE))) { BattleScriptExecute(BattleScript_TrainerBSlideMsgEnd2); } diff --git a/src/battle_partner.c b/src/battle_partner.c index 81e286d17b..6948057569 100644 --- a/src/battle_partner.c +++ b/src/battle_partner.c @@ -6,7 +6,9 @@ #include "data.h" #include "frontier_util.h" #include "difficulty.h" +#include "malloc.h" #include "string_util.h" +#include "trainer_util.h" #include "text.h" #include "constants/abilities.h" @@ -21,15 +23,25 @@ const struct Trainer gBattlePartners[DIFFICULTY_COUNT][PARTNER_COUNT] = #define STEVEN_OTID 61226 +static void MakePartnerGenerator(struct TrainerGenerator *trainerGen, const struct Trainer *trainer) +{ + u32 otID; + trainerGen->gender = trainer->gender; + trainerGen->isFrontier = FALSE; + StringCopyN(trainerGen->name, trainer->trainerName, TRAINER_NAME_LENGTH + 1); + trainerGen->trainerClass = trainer->trainerClass; + otID = Crc32B((const u8 *)trainer, sizeof(struct Trainer)); + trainerGen->otID = OTID_STRUCT_PRESET(otID); + trainerGen->localRngState = LocalRandomSeed(otID); +} + void FillPartnerParty(u16 trainerId) { - s32 i, j, k; - u32 firstIdPart = 0, secondIdPart = 0, thirdIdPart = 0; - u32 ivs, level, personality; + s32 i, j; + u32 ivs, level; u16 monId; - u32 otID; + u8 trainerName[(PLAYER_NAME_LENGTH * 3) + 1]; - enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(trainerId); SetFacilityPtrsGetLevel(); if (trainerId > TRAINER_PARTNER(PARTNER_NONE)) @@ -37,85 +49,16 @@ void FillPartnerParty(u16 trainerId) 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 Trainer *partner = GetTrainerStructFromId(trainerId); + struct TrainerGenerator *partnerGen = AllocZeroed(sizeof(struct TrainerGenerator)); + MakePartnerGenerator(partnerGen, partner); + if (trainerId == TRAINER_PARTNER(PARTNER_STEVEN)) + partnerGen->otID = OTID_STRUCT_PRESET(STEVEN_OTID); + for (i = 0; i < 3 && i < partner->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, personality, OTID_STRUCT_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 < POKEBALL_COUNT) - { - enum PokeBall 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))].gender; - SetMonData(&gPlayerParty[i + 3], MON_DATA_OT_GENDER, &j); + GenerateMonFromTrainerMon(&gPlayerParty[i + 3], &partner->party[i], partnerGen); } + Free(partnerGen); } else if (trainerId == TRAINER_EREADER) { @@ -126,7 +69,7 @@ void FillPartnerParty(u16 trainerId) { level = SetFacilityPtrsGetLevel(); ivs = GetFrontierTrainerFixedIvs(trainerId); - otID = Random32(); + u32 otID = Random32(); for (i = 0; i < FRONTIER_MULTI_PARTY_SIZE; i++) { monId = gSaveBlock2Ptr->frontier.trainerIds[i + 18]; diff --git a/src/battle_setup.c b/src/battle_setup.c index 92b5c74941..28d99df140 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -1,50 +1,56 @@ #include "global.h" -#include "battle.h" -#include "load_save.h" -#include "battle_setup.h" -#include "battle_tower.h" -#include "battle_transition.h" +#include "data.h" #include "main.h" -#include "task.h" -#include "safari_zone.h" -#include "script.h" -#include "event_data.h" -#include "metatile_behavior.h" -#include "field_player_avatar.h" -#include "fieldmap.h" -#include "follower_npc.h" -#include "random.h" -#include "starter_choose.h" -#include "script_pokemon_util.h" -#include "palette.h" -#include "window.h" -#include "event_object_movement.h" -#include "event_scripts.h" -#include "tv.h" -#include "trainer_see.h" -#include "field_message_box.h" -#include "sound.h" -#include "strings.h" -#include "trainer_hill.h" -#include "secret_base.h" -#include "string_util.h" -#include "overworld.h" -#include "field_weather.h" -#include "battle_tower.h" -#include "gym_leader_rematch.h" +#include "battle.h" #include "battle_frontier.h" #include "battle_pike.h" #include "battle_pyramid.h" -#include "fldeff.h" -#include "fldeff_misc.h" -#include "field_control_avatar.h" -#include "mirage_tower.h" -#include "field_screen_effect.h" -#include "data.h" -#include "vs_seeker.h" -#include "item.h" +#include "battle_setup.h" +#include "battle_partner.h" +#include "battle_tower.h" +#include "battle_transition.h" +#include "event_data.h" +#include "event_object_movement.h" +#include "event_scripts.h" +#include "fieldmap.h" #include "script.h" #include "field_name_box.h" +#include "field_control_avatar.h" +#include "field_message_box.h" +#include "field_player_avatar.h" +#include "field_screen_effect.h" +#include "field_weather.h" +#include "fishing.h" +#include "fldeff.h" +#include "fldeff_misc.h" +#include "follower_npc.h" +#include "gym_leader_rematch.h" +#include "item.h" +#include "load_save.h" +#include "malloc.h" +#include "metatile_behavior.h" +#include "mirage_tower.h" +#include "palette.h" +#include "random.h" +#include "safari_zone.h" +#include "script.h" +#include "script_pokemon_util.h" +#include "secret_base.h" +#include "sound.h" +#include "starter_choose.h" +#include "strings.h" +#include "string_util.h" +#include "task.h" +#include "trainer_hill.h" +#include "trainer_pools.h" +#include "trainer_see.h" +#include "trainer_util.h" +#include "tv.h" +#include "overworld.h" + +#include "vs_seeker.h" +#include "window.h" + #include "constants/battle_frontier.h" #include "constants/battle_setup.h" #include "constants/event_objects.h" @@ -54,7 +60,7 @@ #include "constants/trainers.h" #include "constants/trainer_hill.h" #include "constants/weather.h" -#include "fishing.h" + enum TransitionType { @@ -64,11 +70,13 @@ enum TransitionType TRANSITION_TYPE_WATER, }; +struct TransitionData +{ + enum BattleTransition effect; + u16 song; +}; + // this file's functions -static void DoBattlePikeWildBattle(void); -static void DoSafariBattle(void); -static void DoGhostBattle(void); -static void DoStandardWildBattle(bool32 isDouble); static void CB2_EndWildBattle(void); static void CB2_EndScriptedWildBattle(void); static void CB2_EndMarowakBattle(void); @@ -88,6 +96,9 @@ static void RegisterTrainerInMatchCall(void); static void HandleRematchVarsOnBattleEnd(void); static const u8 *GetIntroSpeechOfApproachingTrainer(void); static const u8 *GetTrainerCantBattleSpeech(void); +static void CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer); +static void DoTrainerBattle(void); +static struct TransitionData GetLegendaryBattleTransition(void); EWRAM_DATA TrainerBattleParameter gTrainerBattleParameter = {0}; EWRAM_DATA u16 gPartnerTrainerId = 0; @@ -327,305 +338,172 @@ static bool8 CheckSilphScopeInPokemonTower(u16 mapGroup, u16 mapNum) return FALSE; } -void BattleSetup_StartWildBattle(void) -{ - if (GetSafariZoneFlag()) - DoSafariBattle(); - else if (CheckSilphScopeInPokemonTower(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) - DoGhostBattle(); - else - DoStandardWildBattle(FALSE); -} - -void BattleSetup_StartDoubleWildBattle(void) -{ - DoStandardWildBattle(TRUE); -} - -void BattleSetup_StartBattlePikeWildBattle(void) -{ - DoBattlePikeWildBattle(); -} - -static void DoStandardWildBattle(bool32 isDouble) +void BattleSetup_StartWildBattle(bool32 isDouble) { LockPlayerFieldControls(); FreezeObjectEvents(); StopPlayerAvatar(); - gMain.savedCallback = CB2_EndWildBattle; - gBattleTypeFlags = 0; - if (IsNPCFollowerWildBattle()) - { - gBattleTypeFlags |= BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_DOUBLE; - } + + if (GetSafariZoneFlag() && !isDouble) + gBattleTypeFlags = BATTLE_TYPE_SAFARI; + else if (CheckSilphScopeInPokemonTower(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum) && !isDouble) + gBattleTypeFlags = BATTLE_TYPE_GHOST; + else if (IsNPCFollowerWildBattle()) + gBattleTypeFlags = BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_DOUBLE; else if (isDouble) - gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; + gBattleTypeFlags = BATTLE_TYPE_DOUBLE; + else + gBattleTypeFlags = BATTLE_TYPE_BASIC_WILD; if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) { VarSet(VAR_TEMP_E, 0); gBattleTypeFlags |= BATTLE_TYPE_PYRAMID; } - CreateBattleStartTask(GetWildBattleTransition(), 0); - IncrementGameStat(GAME_STAT_TOTAL_BATTLES); - IncrementGameStat(GAME_STAT_WILD_BATTLES); - IncrementDailyWildBattles(); - TryUpdateGymLeaderRematchFromWild(); + DoBattleSetup(FALSE); } -void DoStandardWildBattle_Debug(void) +void BattleSetup_StartMultiBattle(void) { - LockPlayerFieldControls(); - FreezeObjectEvents(); - StopPlayerAvatar(); - gMain.savedCallback = CB2_EndWildBattle; - gBattleTypeFlags = 0; - if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) + 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 + 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; + + if (gSpecialVar_0x8005 & MULTI_BATTLE_CHOOSE_MONS) // Skip mons restoring(done in the script) + gBattleScripting.specialTrainerBattleType = 0xFF; + + DoBattleSetup(FALSE); +} + +static void IncrementBattleStats() +{ + IncrementGameStat(GAME_STAT_TOTAL_BATTLES); + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) { - VarSet(VAR_TEMP_PLAYING_PYRAMID_MUSIC, 0); - gBattleTypeFlags |= BATTLE_TYPE_PYRAMID; + IncrementGameStat(GAME_STAT_WILD_BATTLES); + IncrementDailyWildBattles(); + TryUpdateGymLeaderRematchFromWild(); + } + else + { + IncrementGameStat(GAME_STAT_TRAINER_BATTLES); + TryUpdateGymLeaderRematchFromTrainer(); } - CreateBattleStartTask_Debug(GetWildBattleTransition(), 0); - //IncrementGameStat(GAME_STAT_TOTAL_BATTLES); - //IncrementGameStat(GAME_STAT_WILD_BATTLES); - //IncrementDailyWildBattles(); - //TryUpdateGymLeaderRematchFromWild(); } -void BattleSetup_StartRoamerBattle(void) +#define ADD_DEFAULT_SONG(transition) (struct TransitionData){transition, 0} +static struct TransitionData GetBattleTransition(void) { - LockPlayerFieldControls(); - FreezeObjectEvents(); - StopPlayerAvatar(); - gMain.savedCallback = CB2_EndWildBattle; - gBattleTypeFlags = BATTLE_TYPE_ROAMER; - CreateBattleStartTask(GetWildBattleTransition(), 0); - IncrementGameStat(GAME_STAT_TOTAL_BATTLES); - IncrementGameStat(GAME_STAT_WILD_BATTLES); - IncrementDailyWildBattles(); - TryUpdateGymLeaderRematchFromWild(); + if (gBattleTypeFlags & BATTLE_TYPE_PYRAMID) + return ADD_DEFAULT_SONG(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_PYRAMID)); + else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_HILL) + return ADD_DEFAULT_SONG(GetSpecialBattleTransition(B_TRANSITION_GROUP_TRAINER_HILL)); + else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) + return ADD_DEFAULT_SONG(GetTrainerBattleTransition()); + else if (gBattleTypeFlags & BATTLE_TYPE_CATCH_TUTORIAL) + return (struct TransitionData) {B_TRANSITION_SLICE, 0}; + else if (gBattleTypeFlags & BATTLE_TYPE_LEGENDARY) + return GetLegendaryBattleTransition(); + else + return ADD_DEFAULT_SONG(GetWildBattleTransition()); } -static void DoSafariBattle(void) +static void FillEnnemyParties() { - LockPlayerFieldControls(); - FreezeObjectEvents(); - StopPlayerAvatar(); - gMain.savedCallback = CB2_EndSafariBattle; - gBattleTypeFlags = BATTLE_TYPE_SAFARI; - CreateBattleStartTask(GetWildBattleTransition(), 0); + if (gBattleTypeFlags & BATTLE_TYPE_PYRAMID) + { + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + FillFrontierTrainersParties(1); + else + FillFrontierTrainerParty(1); + return; + } + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_HILL) + { + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + FillHillTrainersParties(); + else + FillHillTrainerParty(); + return; + } + CreateNPCTrainerParty(&gEnemyParty[0], TRAINER_BATTLE_PARAM.opponentA, TRUE); + if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && !BATTLE_TWO_VS_ONE_OPPONENT) + CreateNPCTrainerParty(&gEnemyParty[PARTY_SIZE / 2], TRAINER_BATTLE_PARAM.opponentB, FALSE); } -static void DoGhostBattle(void) +void DoBattleSetup(bool32 fromScript) { - LockPlayerFieldControls(); - FreezeObjectEvents(); - StopPlayerAvatar(); - gMain.savedCallback = CB2_EndWildBattle; - gBattleTypeFlags = BATTLE_TYPE_GHOST; - CreateBattleStartTask(GetWildBattleTransition(), 0); - SetMonData(&gEnemyParty[0], MON_DATA_NICKNAME, gText_Ghost); - IncrementGameStat(GAME_STAT_TOTAL_BATTLES); - IncrementGameStat(GAME_STAT_WILD_BATTLES); -} + if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) + gMain.savedCallback = CB2_EndSafariBattle; + else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) + gMain.savedCallback = CB2_EndTrainerBattle; + else if (gBattleTypeFlags & BATTLE_TYPE_CATCH_TUTORIAL) + gMain.savedCallback = CB2_ReturnToFieldContinueScriptPlayMapMusic; + else if (gBattleTypeFlags & BATTLE_TYPE_GHOST && fromScript) + gMain.savedCallback = CB2_EndMarowakBattle; + else if (fromScript) + gMain.savedCallback = CB2_EndScriptedWildBattle; + else + gMain.savedCallback = CB2_EndWildBattle; -static void DoBattlePikeWildBattle(void) -{ - LockPlayerFieldControls(); - FreezeObjectEvents(); - StopPlayerAvatar(); - gMain.savedCallback = CB2_EndWildBattle; - gBattleTypeFlags = BATTLE_TYPE_PIKE; - CreateBattleStartTask(GetWildBattleTransition(), 0); - IncrementGameStat(GAME_STAT_TOTAL_BATTLES); - IncrementGameStat(GAME_STAT_WILD_BATTLES); - IncrementDailyWildBattles(); - TryUpdateGymLeaderRematchFromWild(); + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) + FillEnnemyParties(); + + if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) + FillPartnerParty(gPartnerTrainerId); + if (gBattleTypeFlags & BATTLE_TYPE_GHOST) + SetMonData(&gEnemyParty[0], MON_DATA_NICKNAME, gText_Ghost); + struct TransitionData transition = GetBattleTransition(); + CreateBattleStartTask(transition.effect, transition.song); + if (!(gBattleTypeFlags & (BATTLE_TYPE_SAFARI | BATTLE_TYPE_CATCH_TUTORIAL))) + IncrementBattleStats(); } static void DoTrainerBattle(void) { + CreateNPCTrainerParty(&gEnemyParty[0], TRAINER_BATTLE_PARAM.opponentA, TRUE); + if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && !BATTLE_TWO_VS_ONE_OPPONENT) + CreateNPCTrainerParty(&gEnemyParty[PARTY_SIZE / 2], TRAINER_BATTLE_PARAM.opponentB, FALSE); CreateBattleStartTask(GetTrainerBattleTransition(), 0); IncrementGameStat(GAME_STAT_TOTAL_BATTLES); IncrementGameStat(GAME_STAT_TRAINER_BATTLES); TryUpdateGymLeaderRematchFromTrainer(); } -static void DoBattlePyramidTrainerHillBattle(void) +static struct TransitionData GetLegendaryBattleTransition(void) { - if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) - CreateBattleStartTask(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_PYRAMID), 0); - else - CreateBattleStartTask(GetSpecialBattleTransition(B_TRANSITION_GROUP_TRAINER_HILL), 0); - - IncrementGameStat(GAME_STAT_TOTAL_BATTLES); - IncrementGameStat(GAME_STAT_TRAINER_BATTLES); - TryUpdateGymLeaderRematchFromTrainer(); -} - -// Initiates battle where Wally catches Ralts -void StartWallyTutorialBattle(void) -{ - CreateMaleMon(&gEnemyParty[0], SPECIES_RALTS, 5); - LockPlayerFieldControls(); - gMain.savedCallback = CB2_ReturnToFieldContinueScriptPlayMapMusic; - gBattleTypeFlags = BATTLE_TYPE_CATCH_TUTORIAL; - CreateBattleStartTask(B_TRANSITION_SLICE, 0); -} - -void StartOldManTutorialBattle(void) -{ - CreateMaleMon(&gEnemyParty[0], SPECIES_WEEDLE, 5); - LockPlayerFieldControls(); - gMain.savedCallback = CB2_ReturnToFieldContinueScriptPlayMapMusic; - gBattleTypeFlags = BATTLE_TYPE_CATCH_TUTORIAL; - CreateBattleStartTask(B_TRANSITION_SLICE, 0); -} - -void BattleSetup_StartScriptedWildBattle(void) -{ - LockPlayerFieldControls(); - gMain.savedCallback = CB2_EndScriptedWildBattle; - gBattleTypeFlags = 0; - CreateBattleStartTask(GetWildBattleTransition(), 0); - IncrementGameStat(GAME_STAT_TOTAL_BATTLES); - IncrementGameStat(GAME_STAT_WILD_BATTLES); - IncrementDailyWildBattles(); - TryUpdateGymLeaderRematchFromWild(); -} - -void BattleSetup_StartScriptedDoubleWildBattle(void) -{ - LockPlayerFieldControls(); - gMain.savedCallback = CB2_EndScriptedWildBattle; - gBattleTypeFlags = BATTLE_TYPE_DOUBLE; - CreateBattleStartTask(GetWildBattleTransition(), 0); - IncrementGameStat(GAME_STAT_TOTAL_BATTLES); - IncrementGameStat(GAME_STAT_WILD_BATTLES); - IncrementDailyWildBattles(); - TryUpdateGymLeaderRematchFromWild(); -} - -void StartMarowakBattle(void) -{ - LockPlayerFieldControls(); - gMain.savedCallback = CB2_EndMarowakBattle; - gBattleTypeFlags = BATTLE_TYPE_GHOST; - - if (CheckBagHasItem(ITEM_SILPH_SCOPE, 1)) - { - u32 personality = GetMonPersonality(SPECIES_MAROWAK, MON_FEMALE, NATURE_SERIOUS, RANDOM_UNOWN_LETTER); - - CreateMonWithIVsPersonality(&gEnemyParty[0], SPECIES_MAROWAK, 30, 31, personality); - } - - CreateBattleStartTask(GetWildBattleTransition(), 0); - SetMonData(&gEnemyParty[0], MON_DATA_NICKNAME, gText_Ghost); - IncrementGameStat(GAME_STAT_TOTAL_BATTLES); - IncrementGameStat(GAME_STAT_WILD_BATTLES); -} - -void BattleSetup_StartLatiBattle(void) -{ - LockPlayerFieldControls(); - gMain.savedCallback = CB2_EndScriptedWildBattle; - gBattleTypeFlags = BATTLE_TYPE_LEGENDARY; - CreateBattleStartTask(GetWildBattleTransition(), 0); - IncrementGameStat(GAME_STAT_TOTAL_BATTLES); - IncrementGameStat(GAME_STAT_WILD_BATTLES); - IncrementDailyWildBattles(); - TryUpdateGymLeaderRematchFromWild(); -} - -void BattleSetup_StartLegendaryBattle(void) -{ - LockPlayerFieldControls(); - gMain.savedCallback = CB2_EndScriptedWildBattle; - gBattleTypeFlags = BATTLE_TYPE_LEGENDARY; - switch (GetMonData(&gEnemyParty[0], MON_DATA_SPECIES)) { + case SPECIES_REGIROCK: + return (struct TransitionData) {B_TRANSITION_REGIROCK, MUS_VS_REGI}; + case SPECIES_REGICE: + return (struct TransitionData) {B_TRANSITION_REGICE, MUS_VS_REGI}; + case SPECIES_REGISTEEL: + return (struct TransitionData) {B_TRANSITION_REGISTEEL, MUS_VS_REGI}; case SPECIES_GROUDON: case SPECIES_GROUDON_PRIMAL: - CreateBattleStartTask(B_TRANSITION_GROUDON, MUS_VS_KYOGRE_GROUDON); - break; + return (struct TransitionData) {B_TRANSITION_GROUDON, MUS_VS_KYOGRE_GROUDON}; case SPECIES_KYOGRE: case SPECIES_KYOGRE_PRIMAL: - CreateBattleStartTask(B_TRANSITION_KYOGRE, MUS_VS_KYOGRE_GROUDON); - break; + return (struct TransitionData) {B_TRANSITION_KYOGRE, MUS_VS_KYOGRE_GROUDON}; case SPECIES_RAYQUAZA: case SPECIES_RAYQUAZA_MEGA: - CreateBattleStartTask(B_TRANSITION_RAYQUAZA, MUS_VS_RAYQUAZA); - break; + return (struct TransitionData) {B_TRANSITION_RAYQUAZA, MUS_VS_RAYQUAZA}; case SPECIES_DEOXYS_NORMAL: case SPECIES_DEOXYS_ATTACK: case SPECIES_DEOXYS_DEFENSE: case SPECIES_DEOXYS_SPEED: - CreateBattleStartTask(B_TRANSITION_BLUR, MUS_RG_VS_DEOXYS); - break; - case SPECIES_LUGIA: - case SPECIES_HO_OH: - default: - CreateBattleStartTask(B_TRANSITION_BLUR, MUS_RG_VS_LEGEND); - break; + return (struct TransitionData) {B_TRANSITION_BLUR, MUS_RG_VS_DEOXYS}; case SPECIES_MEW: - CreateBattleStartTask(B_TRANSITION_GRID_SQUARES, MUS_VS_MEW); - break; - } - - IncrementGameStat(GAME_STAT_TOTAL_BATTLES); - IncrementGameStat(GAME_STAT_WILD_BATTLES); - IncrementDailyWildBattles(); - TryUpdateGymLeaderRematchFromWild(); -} - -void StartGroudonKyogreBattle(void) -{ - LockPlayerFieldControls(); - gMain.savedCallback = CB2_EndScriptedWildBattle; - gBattleTypeFlags = BATTLE_TYPE_LEGENDARY; - - if (gGameVersion == VERSION_RUBY) - CreateBattleStartTask(B_TRANSITION_ANGLED_WIPES, MUS_VS_KYOGRE_GROUDON); // GROUDON - else - CreateBattleStartTask(B_TRANSITION_RIPPLE, MUS_VS_KYOGRE_GROUDON); // KYOGRE - - IncrementGameStat(GAME_STAT_TOTAL_BATTLES); - IncrementGameStat(GAME_STAT_WILD_BATTLES); - IncrementDailyWildBattles(); - TryUpdateGymLeaderRematchFromWild(); -} - -void StartRegiBattle(void) -{ - enum BattleTransition transitionId; - enum Species species; - - LockPlayerFieldControls(); - gMain.savedCallback = CB2_EndScriptedWildBattle; - gBattleTypeFlags = BATTLE_TYPE_LEGENDARY; - - species = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); - switch (species) - { - case SPECIES_REGIROCK: - transitionId = B_TRANSITION_REGIROCK; - break; - case SPECIES_REGICE: - transitionId = B_TRANSITION_REGICE; - break; - case SPECIES_REGISTEEL: - transitionId = B_TRANSITION_REGISTEEL; - break; + return (struct TransitionData) {B_TRANSITION_GRID_SQUARES, MUS_VS_MEW}; + case SPECIES_LATIOS: + case SPECIES_LATIAS: + return (struct TransitionData) {GetWildBattleTransition(), 0}; default: - transitionId = B_TRANSITION_GRID_SQUARES; - break; + return (struct TransitionData) {B_TRANSITION_BLUR, MUS_RG_VS_LEGEND}; } - CreateBattleStartTask(transitionId, MUS_VS_REGI); - - IncrementGameStat(GAME_STAT_TOTAL_BATTLES); - IncrementGameStat(GAME_STAT_WILD_BATTLES); - IncrementDailyWildBattles(); - TryUpdateGymLeaderRematchFromWild(); } static void DowngradeBadPoison(void) @@ -1056,30 +934,6 @@ void TrainerBattleLoadArgs(const u8 *data) sTrainerBattleEndScript = (u8*)data + sizeof(TrainerBattleParameter); } -void TrainerBattleLoadArgsTrainerA(const u8 *data) -{ - TrainerBattleParameter *temp = (TrainerBattleParameter*)data; - - TRAINER_BATTLE_PARAM.playMusicA = temp->params.playMusicA; - TRAINER_BATTLE_PARAM.objEventLocalIdA = temp->params.objEventLocalIdA; - TRAINER_BATTLE_PARAM.opponentA = temp->params.opponentA; - TRAINER_BATTLE_PARAM.introTextA = temp->params.introTextA; - TRAINER_BATTLE_PARAM.defeatTextA = temp->params.defeatTextA; - TRAINER_BATTLE_PARAM.battleScriptRetAddrA = temp->params.battleScriptRetAddrA; -} - -void TrainerBattleLoadArgsTrainerB(const u8 *data) -{ - TrainerBattleParameter *temp = (TrainerBattleParameter*)data; - - TRAINER_BATTLE_PARAM.playMusicB = temp->params.playMusicB; - TRAINER_BATTLE_PARAM.objEventLocalIdB = temp->params.objEventLocalIdB; - TRAINER_BATTLE_PARAM.opponentB = temp->params.opponentB; - TRAINER_BATTLE_PARAM.introTextB = temp->params.introTextB; - TRAINER_BATTLE_PARAM.defeatTextB = temp->params.defeatTextB; - TRAINER_BATTLE_PARAM.battleScriptRetAddrB = temp->params.battleScriptRetAddrB; -} - // loads trainer A parameter to trainer B. Used for second trainer in trainer_see.c void TrainerBattleLoadArgsSecondTrainer(const u8 *data) { @@ -1299,25 +1153,12 @@ void ClearTrainerFlag(u16 trainerId) void BattleSetup_StartTrainerBattle(void) { + gBattleTypeFlags = BATTLE_TYPE_TRAINER; if (gNoOfApproachingTrainers == 2) - { - if (FollowerNPCIsBattlePartner()) - gBattleTypeFlags = (BATTLE_TYPE_MULTI | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_TRAINER); - else - gBattleTypeFlags = (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_TRAINER); - } - else - { - if (FollowerNPCIsBattlePartner()) - { - gBattleTypeFlags = (BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TRAINER); - TRAINER_BATTLE_PARAM.opponentB = 0xFFFF; - } - else - { - gBattleTypeFlags = (BATTLE_TYPE_TRAINER); - } - } + gBattleTypeFlags |= (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS); + + if (FollowerNPCIsBattlePartner()) + gBattleTypeFlags |= (BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER); if (GetTrainerBattleMode() == TRAINER_BATTLE_EARLY_RIVAL && GetRivalBattleFlags() & RIVAL_BATTLE_TUTORIAL) gBattleTypeFlags |= BATTLE_TYPE_FIRST_BATTLE; @@ -1326,33 +1167,11 @@ void BattleSetup_StartTrainerBattle(void) { VarSet(VAR_TEMP_PLAYING_PYRAMID_MUSIC, 0); gBattleTypeFlags |= BATTLE_TYPE_PYRAMID; - - if (gNoOfApproachingTrainers == 2) - { - FillFrontierTrainersParties(1); - ZeroMonData(&gEnemyParty[1]); - ZeroMonData(&gEnemyParty[2]); - ZeroMonData(&gEnemyParty[4]); - ZeroMonData(&gEnemyParty[5]); - } - else - { - FillFrontierTrainerParty(1); - ZeroMonData(&gEnemyParty[1]); - ZeroMonData(&gEnemyParty[2]); - } - MarkApproachingPyramidTrainersAsBattled(); } else if (InTrainerHillChallenge()) { gBattleTypeFlags |= BATTLE_TYPE_TRAINER_HILL; - - if (gNoOfApproachingTrainers == 2) - FillHillTrainersParties(); - else - FillHillTrainerParty(); - SetHillTrainerFlag(); } else if (GetTrainerBattleType(TRAINER_BATTLE_PARAM.opponentA) == TRAINER_BATTLE_TYPE_DOUBLES) @@ -1364,12 +1183,8 @@ void BattleSetup_StartTrainerBattle(void) gNoOfApproachingTrainers = 0; sShouldCheckTrainerBScript = FALSE; gWhichTrainerToFaceAfterBattle = 0; - gMain.savedCallback = CB2_EndTrainerBattle; - if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE || InTrainerHillChallenge()) - DoBattlePyramidTrainerHillBattle(); - else - DoTrainerBattle(); + DoBattleSetup(FALSE); ScriptContext_Stop(); } @@ -2130,3 +1945,67 @@ void SetMultiTrainerBattle(struct ScriptContext *ctx) TRAINER_BATTLE_PARAM.defeatTextB = (u8*)ScriptReadWord(ctx); gPartnerTrainerId = TRAINER_PARTNER(ScriptReadHalfword(ctx)); }; + +static void MakeTrainerGenerator(struct TrainerGenerator *trainerGen, const struct Trainer *trainer) +{ + trainerGen->gender = trainer->gender; + trainerGen->isFrontier = FALSE; + StringCopyN(trainerGen->name, trainer->trainerName, TRAINER_NAME_LENGTH + 1); + trainerGen->trainerClass = trainer->trainerClass; + trainerGen->otID = OTID_STRUCT_RANDOM_NO_SHINY; + trainerGen->localRngState = GeneratePartySeed(trainer); +} + +void CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer) +{ + s32 i; + u8 monsCount; + + if (firstTrainer == TRUE) + ZeroEnemyPartyMons(); + + if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && trainer->partySize > PARTY_SIZE / 2) + monsCount = PARTY_SIZE / 2; + else + monsCount = trainer->partySize; + + u32 monIndices[monsCount]; + struct TrainerGenerator *trainerGen = AllocZeroed(sizeof(struct TrainerGenerator)); + MakeTrainerGenerator(trainerGen, trainer); + DoTrainerPartyPool(trainer, monIndices, monsCount, gBattleTypeFlags); + + for (i = 0; i < monsCount; i++) + { + u32 monIndex = monIndices[i]; + GenerateMonFromTrainerMon(&party[i], &trainer->party[monIndex], trainerGen); + } + Free(trainerGen); +} + +static void CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer) +{ + if (!GetTrainerStructFromId(trainerNum)->overrideTrainer) { + CreateNPCTrainerPartyFromTrainer(party, GetTrainerStructFromId(trainerNum), firstTrainer); + return; + } + + struct Trainer tempTrainer; + memcpy(&tempTrainer, GetTrainerStructFromId(trainerNum), sizeof(struct Trainer)); + const struct Trainer *origTrainer = GetTrainerStructFromId(tempTrainer.overrideTrainer); + + tempTrainer.party = origTrainer->party; + + tempTrainer.poolSize = origTrainer->poolSize; + if (tempTrainer.partySize == 0) + tempTrainer.partySize = origTrainer->partySize; + CreateNPCTrainerPartyFromTrainer(party, (const struct Trainer *)(&tempTrainer), firstTrainer); +} + +void CreateTrainerPartyForPlayer(void) +{ + Script_RequestEffects(SCREFF_V1); + + ZeroPlayerPartyMons(); + gPartnerTrainerId = gSpecialVar_0x8004; + CreateNPCTrainerPartyFromTrainer(gPlayerParty, GetTrainerStructFromId(gSpecialVar_0x8004), TRUE); +} diff --git a/src/battle_special.c b/src/battle_special.c index 30dcb34886..7c469db6cf 100644 --- a/src/battle_special.c +++ b/src/battle_special.c @@ -16,7 +16,6 @@ #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); @@ -96,31 +95,8 @@ void DoSpecialTrainerBattle(void) #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; + default: + errorf("Unknown special battle type %d", gSpecialVar_0x8004); } } diff --git a/src/debug.c b/src/debug.c index cd11ca357c..6f15a2b653 100644 --- a/src/debug.c +++ b/src/debug.c @@ -2164,12 +2164,13 @@ static void DebugAction_Trainers_TryBattle(u8 taskId) } gBattleTypeFlags = BATTLE_TYPE_TRAINER; TRAINER_BATTLE_PARAM.opponentA = trainer1Id; - TRAINER_BATTLE_PARAM.opponentB = 0xFFFF; + TRAINER_BATTLE_PARAM.opponentB = trainer2Id; + CreateNPCTrainerPartyFromTrainer(&gEnemyParty[0], GetTrainerStructFromId(trainer1Id), TRUE); if (sDebugMenuListData->data[5] || partnerId != PARTNER_NONE || trainer2Id != TRAINER_NONE) gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; if (trainer2Id != TRAINER_NONE) { - TRAINER_BATTLE_PARAM.opponentB = trainer2Id; + CreateNPCTrainerPartyFromTrainer(&gEnemyParty[PARTY_SIZE / 2], GetTrainerStructFromId(trainer2Id), FALSE); gBattleTypeFlags |= BATTLE_TYPE_TWO_OPPONENTS; } if (partnerId != PARTNER_NONE) @@ -4898,7 +4899,7 @@ const struct Trainer* GetDebugAiTrainer(void) static void DebugAction_Party_SetParty(u8 taskId) { ZeroPlayerPartyMons(); - CreateNPCTrainerPartyFromTrainer(gPlayerParty, &sDebugTrainers[DIFFICULTY_NORMAL][DEBUG_TRAINER_PLAYER], TRUE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(gPlayerParty, &sDebugTrainers[DIFFICULTY_NORMAL][DEBUG_TRAINER_PLAYER], TRUE); ScriptContext_Enable(); Debug_DestroyMenu_Full(taskId); } @@ -4907,8 +4908,8 @@ static void DebugAction_Party_BattleSingle(u8 taskId) { ZeroPlayerPartyMons(); ZeroEnemyPartyMons(); - CreateNPCTrainerPartyFromTrainer(gPlayerParty, &sDebugTrainers[DIFFICULTY_NORMAL][DEBUG_TRAINER_PLAYER], TRUE, BATTLE_TYPE_TRAINER); - CreateNPCTrainerPartyFromTrainer(gEnemyParty, GetDebugAiTrainer(), FALSE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(gPlayerParty, &sDebugTrainers[DIFFICULTY_NORMAL][DEBUG_TRAINER_PLAYER], TRUE); + CreateNPCTrainerPartyFromTrainer(gEnemyParty, GetDebugAiTrainer(), FALSE); gBattleTypeFlags = BATTLE_TYPE_TRAINER; if (sDebugTrainers[DIFFICULTY_NORMAL][DEBUG_TRAINER_AI].battleType == TRAINER_BATTLE_TYPE_DOUBLES) diff --git a/src/pokemon.c b/src/pokemon.c index 8207d62ac4..484d5d9fbc 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1516,14 +1516,6 @@ u32 GetMonPersonality(enum Species species, u8 gender, u8 nature, u8 unownLetter return personality; } -// This is only used to create Wally's Ralts. -void CreateMaleMon(struct Pokemon *mon, enum Species species, u8 level) -{ - u32 personality = GetMonPersonality(species, MON_MALE, NATURE_RANDOM, RANDOM_UNOWN_LETTER); - CreateMonWithIVs(mon, species, level, personality, OTID_STRUCT_PLAYER_ID, USE_RANDOM_IVS); - GiveMonInitialMoveset(mon); -} - void CreateMonWithIVsPersonality(struct Pokemon *mon, enum Species species, u8 level, u32 ivs, u32 personality) { CreateMon(mon, species, level, personality, OTID_STRUCT_PLAYER_ID); @@ -6081,60 +6073,57 @@ static inline bool32 CanFirstMonBoostHeldItemRarity(void) void SetWildMonHeldItem(void) { - if (!(gBattleTypeFlags & (BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_TRAINER | BATTLE_TYPE_PYRAMID | BATTLE_TYPE_PIKE))) + u16 rnd; + enum Species species; + u16 count = (WILD_DOUBLE_BATTLE) ? 2 : 1; + u16 i; + bool32 itemHeldBoost = CanFirstMonBoostHeldItemRarity(); + u16 chanceNoItem = itemHeldBoost ? 20 : 45; + u16 chanceNotRare = itemHeldBoost ? 80 : 95; + + for (i = 0; i < count; i++) { - u16 rnd; - enum Species species; - u16 count = (WILD_DOUBLE_BATTLE) ? 2 : 1; - u16 i; - bool32 itemHeldBoost = CanFirstMonBoostHeldItemRarity(); - u16 chanceNoItem = itemHeldBoost ? 20 : 45; - u16 chanceNotRare = itemHeldBoost ? 80 : 95; + if (GetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM) != ITEM_NONE) + continue; // prevent overwriting previously set item - for (i = 0; i < count; i++) + rnd = Random() % 100; + species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES, 0); + if (gMapHeader.mapLayoutId == LAYOUT_ALTERING_CAVE) { - if (GetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM) != ITEM_NONE) - continue; // prevent overwriting previously set item - - rnd = Random() % 100; - species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES, 0); - if (gMapHeader.mapLayoutId == LAYOUT_ALTERING_CAVE) + s32 alteringCaveId = GetWildMonTableIdInAlteringCave(species); + if (alteringCaveId != 0) { - s32 alteringCaveId = GetWildMonTableIdInAlteringCave(species); - if (alteringCaveId != 0) - { - // In active Altering Cave, use special item list - if (rnd < chanceNotRare) - continue; - SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &sAlteringCaveWildMonHeldItems[alteringCaveId].item); - } - else - { - // In inactive Altering Cave, use normal items - if (rnd < chanceNoItem) - continue; - if (rnd < chanceNotRare) - SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemCommon); - else - SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemRare); - } + // In active Altering Cave, use special item list + if (rnd < chanceNotRare) + continue; + SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &sAlteringCaveWildMonHeldItems[alteringCaveId].item); } else { - if (gSpeciesInfo[species].itemCommon == gSpeciesInfo[species].itemRare && gSpeciesInfo[species].itemCommon != ITEM_NONE) - { - // Both held items are the same, 100% chance to hold item + // In inactive Altering Cave, use normal items + if (rnd < chanceNoItem) + continue; + if (rnd < chanceNotRare) SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemCommon); - } else - { - if (rnd < chanceNoItem) - continue; - if (rnd < chanceNotRare) - SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemCommon); - else - SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemRare); - } + SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemRare); + } + } + else + { + if (gSpeciesInfo[species].itemCommon == gSpeciesInfo[species].itemRare && gSpeciesInfo[species].itemCommon != ITEM_NONE) + { + // Both held items are the same, 100% chance to hold item + SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemCommon); + } + else + { + if (rnd < chanceNoItem) + continue; + if (rnd < chanceNotRare) + SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemCommon); + else + SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemRare); } } } diff --git a/src/scrcmd.c b/src/scrcmd.c index f95f7b0952..112a4fb72e 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -62,6 +62,7 @@ #include "list_menu.h" #include "malloc.h" #include "battle.h" +#include "constants/battle_setup.h" #include "constants/event_objects.h" #include "constants/map_types.h" @@ -2524,14 +2525,10 @@ bool8 ScrCmd_setwildbattle(struct ScriptContext *ctx) Script_RequestEffects(SCREFF_V1); - if (species2 == SPECIES_NONE) + CreateScriptedWildMon(species, level, item); + if (species2 != SPECIES_NONE) { - CreateScriptedWildMon(species, level, item); - sIsScriptedWildDouble = FALSE; - } - else - { - CreateScriptedDoubleWildMon(species, level, item, species2, level2, item2); + CreateStaticWildMon(&gEnemyParty[1], species2, level2, item2); sIsScriptedWildDouble = TRUE; } @@ -2542,10 +2539,22 @@ bool8 ScrCmd_dowildbattle(struct ScriptContext *ctx) { Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); - if (sIsScriptedWildDouble == FALSE) - BattleSetup_StartScriptedWildBattle(); + u8 special = ScriptReadByte(ctx); + + gBattleTypeFlags = BATTLE_TYPE_BASIC_WILD; + if (special == SPECIAL_WILD_LEGENDARY) + gBattleTypeFlags = BATTLE_TYPE_BASIC_WILD; + else if (special == SPECIAL_WILD_GHOST) + gBattleTypeFlags = BATTLE_TYPE_GHOST; + else if (special == SPECIAL_WILD_CATCH_TUTORIAL) + gBattleTypeFlags = BATTLE_TYPE_CATCH_TUTORIAL; else - BattleSetup_StartScriptedDoubleWildBattle(); + assertf(special != SPECIAL_WILD_NONE, "Unkwown value for special wild battle %d", special); + + if (sIsScriptedWildDouble == TRUE) + gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; + + DoBattleSetup(TRUE); ScriptContext_Stop(); diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index 342bc32d33..319b80c542 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -113,55 +113,27 @@ bool8 DoesPartyHaveEnigmaBerry(void) return hasItem; } -void CreateScriptedWildMon(enum Species species, u8 level, enum Item item) +void CreateStaticWildMon(struct Pokemon *mon, enum Species species, u8 level, enum Item item) { u8 heldItem[2]; - - ZeroEnemyPartyMons(); u32 personality = GetMonPersonality(species, GetSynchronizedGender(STATIC_WILDMON_ORIGIN, species), GetSynchronizedNature(STATIC_WILDMON_ORIGIN, species), RANDOM_UNOWN_LETTER); - CreateMonWithIVs(&gEnemyParty[0], species, level, personality, OTID_STRUCT_PLAYER_ID, USE_RANDOM_IVS); - GiveMonInitialMoveset(&gEnemyParty[0]); + CreateMonWithIVs(mon, species, level, personality, OTID_STRUCT_PLAYER_ID, USE_RANDOM_IVS); + GiveMonInitialMoveset(mon); if (item) { heldItem[0] = item; heldItem[1] = item >> 8; - SetMonData(&gEnemyParty[0], MON_DATA_HELD_ITEM, heldItem); + SetMonData(mon, MON_DATA_HELD_ITEM, heldItem); } } -void CreateScriptedDoubleWildMon(enum Species species1, u8 level1, enum Item item1, enum Species species2, u8 level2, enum Item item2) + +void CreateScriptedWildMon(enum Species species, u8 level, enum Item item) { - u8 heldItem1[2]; - u8 heldItem2[2]; - ZeroEnemyPartyMons(); - u32 personality = GetMonPersonality(species1, - GetSynchronizedGender(STATIC_WILDMON_ORIGIN, species1), - GetSynchronizedNature(STATIC_WILDMON_ORIGIN, species1), - RANDOM_UNOWN_LETTER); - CreateMonWithIVs(&gEnemyParty[0], species1, level1, personality, OTID_STRUCT_PLAYER_ID, USE_RANDOM_IVS); - GiveMonInitialMoveset(&gEnemyParty[0]); - if (item1) - { - heldItem1[0] = item1; - heldItem1[1] = item1 >> 8; - SetMonData(&gEnemyParty[0], MON_DATA_HELD_ITEM, heldItem1); - } - - personality = GetMonPersonality(species2, - GetSynchronizedGender(STATIC_WILDMON_ORIGIN, species2), - GetSynchronizedNature(STATIC_WILDMON_ORIGIN, species2), - RANDOM_UNOWN_LETTER); - CreateMonWithIVs(&gEnemyParty[1], species2, level2, personality, OTID_STRUCT_PLAYER_ID, USE_RANDOM_IVS); - GiveMonInitialMoveset(&gEnemyParty[1]); - if (item2) - { - heldItem2[0] = item2; - heldItem2[1] = item2 >> 8; - SetMonData(&gEnemyParty[1], MON_DATA_HELD_ITEM, heldItem2); - } + CreateStaticWildMon(&gEnemyParty[0], species, level, item); } void ScriptSetMonMoveSlot(u8 monIndex, enum Move move, u8 slot) diff --git a/src/trainer_slide.c b/src/trainer_slide.c index c8f5841b3d..b76a1d474f 100644 --- a/src/trainer_slide.c +++ b/src/trainer_slide.c @@ -359,9 +359,8 @@ enum TrainerSlideTargets ShouldDoTrainerSlide(enum BattlerId battler, enum Train return TRAINER_SLIDE_TARGET_NONE; // Prevents slides triggering twice in single-trainer doubles (B == A / B == TRAINER_NONE) and 2v1 multibattles (B == 0xFFFF) - if (((TRAINER_BATTLE_PARAM.opponentB == TRAINER_BATTLE_PARAM.opponentA) - || (TRAINER_BATTLE_PARAM.opponentB == TRAINER_NONE) - || (TRAINER_BATTLE_PARAM.opponentB == 0xFFFF))) + if ((TRAINER_BATTLE_PARAM.opponentB == TRAINER_BATTLE_PARAM.opponentA) + || (TRAINER_BATTLE_PARAM.opponentB == TRAINER_NONE)) { MarkTrainerSlideAsPlayed(BATTLE_PARTNER(battler), slideId); } diff --git a/src/trainer_util.c b/src/trainer_util.c new file mode 100644 index 0000000000..2458f36f03 --- /dev/null +++ b/src/trainer_util.c @@ -0,0 +1,203 @@ +#include "global.h" +#include "main.h" +#include "data.h" +#include "move.h" +#include "random.h" +#include "string_util.h" +#include "trainer_util.h" +#include "text.h" + +#include "constants/pokeball.h" + +u32 Crc32B(const u8 *data, u32 size) +{ + s32 i, j; + u32 byte, crc, mask; + + i = 0; + crc = 0xFFFFFFFF; + for (i = 0; i < size; ++i) + { + byte = data[i]; + crc = crc ^ byte; + for (j = 7; j >= 0; --j) + { + mask = -(crc & 1); + crc = (crc >> 1) ^ (0xEDB88320 & mask); + } + } + return ~crc; +} + +rng_value_t GeneratePartySeed(const struct Trainer *trainer) +{ + u32 seed = Crc32B((const u8 *)trainer, sizeof(struct Trainer)) ^ READ_OTID_FROM_SAVE; + return LocalRandomSeed(seed); +} + +static void CustomTrainerPartyAssignMoves(struct Pokemon *mon, const struct TrainerMon *partyEntry) +{ + bool32 noMoveSet = TRUE; + u32 j; + + for (j = 0; j < MAX_MON_MOVES; ++j) + { + if (partyEntry->moves[j] != MOVE_NONE) + noMoveSet = FALSE; + } + if (noMoveSet) + { + GiveMonInitialMoveset(mon); + // TODO: Figure out a default strategy when moves are not set, to generate a good moveset + return; + } + + for (j = 0; j < MAX_MON_MOVES; ++j) + { + u32 pp = GetMovePP(partyEntry->moves[j]); + SetMonData(mon, MON_DATA_MOVE1 + j, &partyEntry->moves[j]); + SetMonData(mon, MON_DATA_PP1 + j, &pp); + } +} + +u32 GeneratePersonalityForGender(u32 gender, u32 species) +{ + const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[species]; + if (gender == MON_MALE) + { + assertf(speciesInfo->genderRatio < MON_FEMALE, "species %d cannot be male", species); + return ((255 - speciesInfo->genderRatio) / 2) + speciesInfo->genderRatio; + } + if (gender == MON_FEMALE) + { + assertf(speciesInfo->genderRatio != MON_MALE && speciesInfo->genderRatio != MON_GENDERLESS, "species %d cannot be female", species); + return speciesInfo->genderRatio / 2; + } + if (gender == MON_GENDERLESS) + assertf(speciesInfo->genderRatio == MON_GENDERLESS, "species %d cannot be genderless", species); + else + errorf("GeneratePersonalityForGender called with invalid gender value %d", gender); + return 0; +} + +const u8 sModuloLUT[25] = {0, 21, 17, 13, 9, 5, 1, 22, 18, 14, 10, 6, 2, 23, 19, 15, 11, 7, 3, 24, 20, 16, 12, 8, 4}; + +static void ModifyPersonalityForNature(u32 *personality, s32 newNature) +{ + s32 nature = GetNatureFromPersonality(*personality); + s32 diff = abs(newNature - nature); + s32 sign = (newNature > nature) ? 1 : -1; + if (diff > NUM_NATURES / 2) + { + diff = NUM_NATURES - diff; + sign *= -1; + } + *personality += (sModuloLUT[diff] * 0x100 * sign); +} + +static void SetCorrectAbilityNum(struct Pokemon *mon, u32 species, u32 ability) +{ + const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[species]; + u32 abilityNum; + u32 maxAbilityNum = ARRAY_COUNT(speciesInfo->abilities); + for (abilityNum = 0; abilityNum < maxAbilityNum; ++abilityNum) + { + if (speciesInfo->abilities[abilityNum] == ability) + break; + } + assertf(abilityNum < maxAbilityNum, "illegal ability %S for %S", gAbilitiesInfo[ability].name, speciesInfo->speciesName) + { + return; + } + SetMonData(mon, MON_DATA_ABILITY_NUM, &abilityNum); +} + +void GenerateMonFromTrainerMon(struct Pokemon *mon, const struct TrainerMon *trainerMon, struct TrainerGenerator *trainer) +{ + u32 data; + u32 personality = (LocalRandom32(&trainer->localRngState) & 0xFFFFDF00) + 0x1000; + u32 genderValue = 0; + if (trainerMon->gender == TRAINER_MON_RANDOM_GENDER) + genderValue = LocalRandom32(&trainer->localRngState) & 0x000000FF; + else if (trainerMon->gender == TRAINER_MON_MALE) + genderValue = GeneratePersonalityForGender(MON_MALE, trainerMon->species); + else if (trainerMon->gender == TRAINER_MON_FEMALE) + genderValue = GeneratePersonalityForGender(MON_FEMALE, trainerMon->species); + else + errorf("Unkwown trainer mon gender value %d", trainerMon->gender); + personality |= genderValue; + ModifyPersonalityForNature(&personality, trainerMon->nature); + CreateMon(mon, trainerMon->species, trainerMon->lvl, personality, trainer->otID); + if (trainerMon->nickname != NULL) + SetMonData(mon, MON_DATA_NICKNAME, trainerMon->nickname); + if (trainerMon->ev) //ev in struct TrainerMon are stored in Showdown order not GF order + { + SetMonData(mon, MON_DATA_HP_EV, &trainerMon->ev[0]); + SetMonData(mon, MON_DATA_ATK_EV, &trainerMon->ev[1]); + SetMonData(mon, MON_DATA_DEF_EV, &trainerMon->ev[2]); + SetMonData(mon, MON_DATA_SPATK_EV, &trainerMon->ev[3]); + SetMonData(mon, MON_DATA_SPDEF_EV, &trainerMon->ev[4]); + SetMonData(mon, MON_DATA_SPEED_EV, &trainerMon->ev[5]); + + /* + for (u32 i = 0; i < NUM_STATS; i++) + SetMonData(mon, MON_DATA_HP_EV + i, &trainerMon->ev[i]); + */ + } + + SetMonData(mon, MON_DATA_IVS, &trainerMon->iv); + CustomTrainerPartyAssignMoves(mon, trainerMon); + SetMonData(mon, MON_DATA_HELD_ITEM, &trainerMon->heldItem); + + if (trainerMon->ability) + { + SetCorrectAbilityNum(mon, trainerMon->species, trainerMon->ability); + } + else if (B_TRAINER_MON_HIDDEN_ABILITY) + { + do { + data = Random() % NUM_ABILITY_SLOTS; // includes hidden abilities + } while (GetAbilityBySpecies(trainerMon->species, data) == ABILITY_NONE); + SetMonData(mon, MON_DATA_ABILITY_NUM, &data); + } + + if (trainerMon->ball < POKEBALL_COUNT) + { + data = trainerMon->ball; + SetMonData(mon, MON_DATA_POKEBALL, &data); + } + else if (B_TRAINER_CLASS_POKE_BALLS >= GEN_7 && trainer->trainerClass && trainerMon->ball == POKEBALL_COUNT) + { + data = gTrainerClasses[trainer->trainerClass].ball ?: BALL_POKE; + SetMonData(mon, MON_DATA_POKEBALL, &data); + } + else if (trainerMon->ball > POKEBALL_COUNT) + { + errorf("Invalid ball for %S in %S's party", GetMonData(mon, MON_DATA_NICKNAME), trainer->name); + } + + SetMonData(mon, MON_DATA_FRIENDSHIP, &trainerMon->friendship); + + data = trainerMon->isShiny; + SetMonData(mon, MON_DATA_IS_SHINY, &data); + if (trainerMon->dynamaxLevel > 0) + { + data = trainerMon->dynamaxLevel; + SetMonData(mon, MON_DATA_DYNAMAX_LEVEL, &data); + } + if (trainerMon->gigantamaxFactor) + { + data = trainerMon->gigantamaxFactor; + SetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR, &data); + } + if (trainerMon->teraType) + { + data = trainerMon->teraType; + SetMonData(mon, MON_DATA_TERA_TYPE, &data); + } + + CalculateMonStats(mon); + SetMonData(mon, MON_DATA_OT_NAME, trainer->name); + data = trainer->gender; + SetMonData(mon, MON_DATA_OT_GENDER, &data); +} \ No newline at end of file diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 115932b799..65c6a9c28d 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -1,4 +1,5 @@ #include "global.h" +#include "battle.h" #include "battle_setup.h" #include "battle_pike.h" #include "battle_pyramid.h" @@ -673,7 +674,8 @@ bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior) else if (!TryGenerateBattlePikeWildMon(TRUE)) return FALSE; - BattleSetup_StartBattlePikeWildBattle(); + gBattleTypeFlags = BATTLE_TYPE_PIKE; + DoBattleSetup(FALSE); return TRUE; } if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) @@ -689,7 +691,7 @@ bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior) return FALSE; GenerateBattlePyramidWildMon(); - BattleSetup_StartWildBattle(); + BattleSetup_StartWildBattle(FALSE); return TRUE; } } @@ -712,14 +714,15 @@ bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior) if (!IsWildLevelAllowedByRepel(roamer->level)) return FALSE; - BattleSetup_StartRoamerBattle(); + gBattleTypeFlags = BATTLE_TYPE_ROAMER; + DoBattleSetup(FALSE); return TRUE; } else { if (DoMassOutbreakEncounterTest() == TRUE && SetUpMassOutbreakEncounter(WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE) == TRUE) { - BattleSetup_StartWildBattle(); + BattleSetup_StartWildBattle(FALSE); return TRUE; } @@ -731,11 +734,11 @@ bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior) struct Pokemon mon1 = gEnemyParty[0]; TryGenerateWildMon(gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, WILD_CHECK_KEEN_EYE); gEnemyParty[1] = mon1; - BattleSetup_StartDoubleWildBattle(); + BattleSetup_StartWildBattle(TRUE); } else { - BattleSetup_StartWildBattle(); + BattleSetup_StartWildBattle(FALSE); } return TRUE; } @@ -763,7 +766,8 @@ bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior) if (!IsWildLevelAllowedByRepel(roamer->level)) return FALSE; - BattleSetup_StartRoamerBattle(); + gBattleTypeFlags = BATTLE_TYPE_ROAMER; + DoBattleSetup(FALSE); return TRUE; } else // try a regular surfing encounter @@ -776,11 +780,11 @@ bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior) struct Pokemon mon1 = gEnemyParty[0]; TryGenerateWildMon(gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo, WILD_AREA_WATER, WILD_CHECK_KEEN_EYE); gEnemyParty[1] = mon1; - BattleSetup_StartDoubleWildBattle(); + BattleSetup_StartWildBattle(TRUE); } else { - BattleSetup_StartWildBattle(); + BattleSetup_StartWildBattle(FALSE); } return TRUE; } @@ -816,11 +820,11 @@ void RockSmashWildEncounter(void) struct Pokemon mon1 = gEnemyParty[0]; TryGenerateWildMon(wildPokemonInfo, WILD_AREA_ROCKS, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE); gEnemyParty[1] = mon1; - BattleSetup_StartDoubleWildBattle(); + BattleSetup_StartWildBattle(TRUE); gSpecialVar_Result = TRUE; } else { - BattleSetup_StartWildBattle(); + BattleSetup_StartWildBattle(FALSE); gSpecialVar_Result = TRUE; } } @@ -853,8 +857,8 @@ bool8 SweetScentWildEncounter(void) if (TryGenerateWildMon(gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) return FALSE; - TryGenerateBattlePikeWildMon(FALSE); - BattleSetup_StartBattlePikeWildBattle(); + gBattleTypeFlags = BATTLE_TYPE_PIKE; + DoBattleSetup(FALSE); return TRUE; } if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) @@ -866,7 +870,7 @@ bool8 SweetScentWildEncounter(void) return FALSE; GenerateBattlePyramidWildMon(); - BattleSetup_StartWildBattle(); + BattleSetup_StartWildBattle(FALSE); return TRUE; } } @@ -881,7 +885,8 @@ bool8 SweetScentWildEncounter(void) if (TryStartRoamerEncounter()) { - BattleSetup_StartRoamerBattle(); + gBattleTypeFlags = BATTLE_TYPE_ROAMER; + DoBattleSetup(FALSE); return TRUE; } @@ -890,7 +895,7 @@ bool8 SweetScentWildEncounter(void) else TryGenerateWildMon(gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0); - BattleSetup_StartWildBattle(); + BattleSetup_StartWildBattle(FALSE); return TRUE; } else if (MetatileBehavior_IsWaterWildEncounter(MapGridGetMetatileBehaviorAt(x, y)) == TRUE) @@ -904,12 +909,13 @@ bool8 SweetScentWildEncounter(void) if (TryStartRoamerEncounter()) { - BattleSetup_StartRoamerBattle(); + gBattleTypeFlags = BATTLE_TYPE_ROAMER; + DoBattleSetup(FALSE); return TRUE; } TryGenerateWildMon(gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo, WILD_AREA_WATER, 0); - BattleSetup_StartWildBattle(); + BattleSetup_StartWildBattle(FALSE); return TRUE; } } @@ -951,7 +957,7 @@ void FishingWildEncounter(u8 rod) IncrementGameStat(GAME_STAT_FISHING_ENCOUNTERS); SetPokemonAnglerSpecies(species); - BattleSetup_StartWildBattle(); + BattleSetup_StartWildBattle(FALSE); } u16 GetLocalWildMon(bool8 *isWaterMon) @@ -1189,18 +1195,6 @@ bool8 TryDoDoubleWildBattle(void) return FALSE; } -bool8 StandardWildEncounter_Debug(void) -{ - u32 headerId = GetCurrentMapWildMonHeaderId(); - enum TimeOfDay timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); - - if (TryGenerateWildMon(gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) - return FALSE; - - DoStandardWildBattle_Debug(); - return TRUE; -} - u32 ChooseHiddenMonIndex(void) { #ifdef ENCOUNTER_CHANCE_HIDDEN_MONS_TOTAL diff --git a/test/battle/trainer_control.c b/test/battle/trainer_control.c index 90fcaf4069..57cbe1a17c 100644 --- a/test/battle/trainer_control.c +++ b/test/battle/trainer_control.c @@ -1,7 +1,7 @@ #include "global.h" #include "test/test.h" #include "battle.h" -#include "battle_main.h" +#include "battle_setup.h" #include "data.h" #include "malloc.h" #include "random.h" @@ -18,7 +18,7 @@ TEST("CreateNPCTrainerPartyForTrainer generates customized Pokémon") struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); u32 currTrainer = 3; u8 nickBuffer[20]; - CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE); EXPECT(IsMonShiny(&testParty[0])); EXPECT(!IsMonShiny(&testParty[1])); @@ -94,7 +94,7 @@ TEST("CreateNPCTrainerPartyForTrainer generates different personalities for diff { struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); u32 currTrainer = 3; - CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE); EXPECT(testParty[0].box.personality != testParty[1].box.personality); Free(testParty); } @@ -120,7 +120,7 @@ TEST("Trainer Class Balls apply to the entire party") u32 j; u32 currTrainer = 14; const struct Trainer *trainer = GetTrainerStructFromId(currTrainer); - CreateNPCTrainerPartyFromTrainer(testParty, trainer, TRUE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(testParty, trainer, TRUE); for(j = 0; j < 6; j++) { EXPECT(GetMonData(&testParty[j], MON_DATA_POKEBALL, 0) == gTrainerClasses[trainer->trainerClass].ball); @@ -133,7 +133,7 @@ TEST("Difficulty default to Normal if the trainer doesn't have a member for the SetCurrentDifficultyLevel(DIFFICULTY_EASY); struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); u32 currTrainer = 4; - CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE); EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_MEWTWO); Free(testParty); SetCurrentDifficultyLevel(DIFFICULTY_NORMAL); @@ -144,7 +144,7 @@ TEST("Difficulty changes which party is used for enemy trainer if defined for th SetCurrentDifficultyLevel(DIFFICULTY_EASY); struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); u32 currTrainer = 5; - CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE); EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_METAPOD); EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 1); Free(testParty); @@ -156,7 +156,7 @@ TEST("Difficulty changes which party is used for enemy trainer if defined for th SetCurrentDifficultyLevel(DIFFICULTY_HARD); struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); u32 currTrainer = 5; - CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE); EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_ARCEUS); EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 99); Free(testParty); @@ -168,7 +168,7 @@ TEST("Difficulty changes which party is used for enemy trainer if defined for th SetCurrentDifficultyLevel(DIFFICULTY_NORMAL); struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); u32 currTrainer = 5; - CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE); EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_MEWTWO); EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 50); Free(testParty); @@ -179,7 +179,7 @@ TEST("Difficulty default to Normal if the partner doesn't have a member for the SetCurrentDifficultyLevel(DIFFICULTY_TEST); struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); u32 currTrainer = TRAINER_PARTNER(1); - CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE); EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_METANG); EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 42); Free(testParty); @@ -191,7 +191,7 @@ TEST("Difficulty changes which party is used for partner if defined for the diff SetCurrentDifficultyLevel(DIFFICULTY_EASY); struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); u32 currTrainer = TRAINER_PARTNER(1); - CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE); EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_METAPOD); EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 1); Free(testParty); @@ -203,7 +203,7 @@ TEST("Difficulty changes which party is used for partner if defined for the diff SetCurrentDifficultyLevel(DIFFICULTY_HARD); struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); u32 currTrainer = TRAINER_PARTNER(1); - CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE); EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_ARCEUS); EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 99); Free(testParty); @@ -215,7 +215,7 @@ TEST("Difficulty changes which party is used for partner if defined for the diff SetCurrentDifficultyLevel(DIFFICULTY_NORMAL); struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); u32 currTrainer = TRAINER_PARTNER(1); - CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE); EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_METANG); EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 42); Free(testParty); @@ -225,7 +225,7 @@ TEST("Trainer Party Pool generates a party from the trainer pool") { struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); u32 currTrainer = 6; - CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE); EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_EEVEE); Free(testParty); } @@ -234,7 +234,7 @@ TEST("Trainer Party Pool picks a random lead and a random ace if tags exist in t { struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); u32 currTrainer = 7; - CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE); EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_ARON); // Lead EXPECT(GetMonData(&testParty[1], MON_DATA_SPECIES) == SPECIES_WYNAUT); // Not Lead or Ace EXPECT(GetMonData(&testParty[2], MON_DATA_SPECIES) == SPECIES_EEVEE); // Ace @@ -245,10 +245,12 @@ TEST("Trainer Party Pool picks according to custom rules") { struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); u32 currTrainer = 8; - CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE); + gBattleTypeFlags = BATTLE_TYPE_DOUBLE; + CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE); EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_TORKOAL); // Lead + Weather Setter EXPECT(GetMonData(&testParty[1], MON_DATA_SPECIES) == SPECIES_BULBASAUR); // Lead + Weather Abuser EXPECT(GetMonData(&testParty[2], MON_DATA_SPECIES) == SPECIES_EEVEE); // Anything else + gBattleTypeFlags = 0; Free(testParty); } @@ -256,7 +258,7 @@ TEST("Trainer Party Pool uses standard party creation if pool is illegal") { struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); u32 currTrainer = 9; - CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE); EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_WYNAUT); EXPECT(GetMonData(&testParty[1], MON_DATA_SPECIES) == SPECIES_WOBBUFFET); Free(testParty); @@ -266,7 +268,7 @@ TEST("Trainer Party Pool can be pruned before picking") { struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); u32 currTrainer = 10; - CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE); EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_EEVEE); EXPECT(GetMonData(&testParty[1], MON_DATA_SPECIES) == SPECIES_WYNAUT); Free(testParty); @@ -276,7 +278,7 @@ TEST("Trainer Party Pool can choose which functions to use for picking mons") { struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); u32 currTrainer = 11; - CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE); EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_WYNAUT); EXPECT(GetMonData(&testParty[1], MON_DATA_SPECIES) == SPECIES_WOBBUFFET); Free(testParty); @@ -297,7 +299,7 @@ TEST("CreateNPCTrainerPartyForTrainer generates default moves if no moves are sp const struct Trainer *trainer = GetTrainerStructFromId(currTrainer); ASSUME(trainer->party[0].moves[0] == MOVE_NONE); struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); - CreateNPCTrainerPartyFromTrainer(testParty, trainer, TRUE, BATTLE_TYPE_TRAINER); + CreateNPCTrainerPartyFromTrainer(testParty, trainer, TRUE); EXPECT(GetMonData(&testParty[0], MON_DATA_MOVE1) != MOVE_NONE); Free(testParty); }