This commit is contained in:
FosterProgramming 2026-03-21 06:07:46 -07:00 committed by GitHub
commit 0d1d51d050
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
52 changed files with 711 additions and 958 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

19
include/trainer_util.h Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

203
src/trainer_util.c Normal file
View File

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

View File

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

View File

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