diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 59664d7864..1c59bdbe81 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -1018,9 +1018,9 @@ .2byte \level .ifnb \item; .set givemon_flags, givemon_flags | (1 << 0); .endif .ifnb \ball; .set givemon_flags, givemon_flags | (1 << 1); .endif - .ifnb \nature; .set givemon_flags, givemon_flags | (1 << 2); .endif + .set givemon_flags, givemon_flags | (1 << 2); .ifnb \abilityNum; .set givemon_flags, givemon_flags | (1 << 3); .endif - .ifnb \gender; .set givemon_flags, givemon_flags | (1 << 4); .endif + .set givemon_flags, givemon_flags | (1 << 4); .ifnb \hpEv; .set givemon_flags, givemon_flags | (1 << 5); .endif .ifnb \atkEv; .set givemon_flags, givemon_flags | (1 << 6); .endif .ifnb \defEv; .set givemon_flags, givemon_flags | (1 << 7); .endif @@ -1044,9 +1044,9 @@ .4byte givemon_flags .ifnb \item; .2byte \item; .endif .ifnb \ball; .2byte \ball; .endif - .ifnb \nature; .2byte \nature; .endif + .ifnb \nature; .2byte \nature; .else; .2byte NATURE_MAY_SYNCHRONIZE; .endif .ifnb \abilityNum; .2byte \abilityNum; .endif - .ifnb \gender; .2byte \gender; .endif + .ifnb \gender; .2byte \gender; .else; .2byte MON_GENDER_MAY_CUTE_CHARM; .endif .ifnb \hpEv; .2byte \hpEv; .endif .ifnb \atkEv; .2byte \atkEv; .endif .ifnb \defEv; .2byte \defEv; .endif diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 74cf376fbe..7d6447cabd 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -44,6 +44,7 @@ - [How to Namebox](tutorials/how_to_namebox.md) - [Vs. Seeker](tutorials/vs_seeker.md) - [Teachable Learnsets Explanations](tutorials/teachable_learnsets.md) + - [Struct Pokemon Generation](tutorials/mon_generation.md) - [Changelog](./CHANGELOG.md) - [1.14.x]() - [Version 1.14.2](changelogs/1.14.x/1.14.2.md) diff --git a/docs/tutorials/mon_generation.md b/docs/tutorials/mon_generation.md new file mode 100644 index 0000000000..3c1bc176dd --- /dev/null +++ b/docs/tutorials/mon_generation.md @@ -0,0 +1,169 @@ +# Generation of struct Pokemon instances + +This document describes the ways you generate an instance of struct Pokemon through script or through code. +These Pokemon can be given to your players, be used as enemy trainer pokemon or as static wild pokemon. + +## Through script (`givemon` and `createmon`) + +### `createmon` + +`createmon` is a script command that allows you to generate a pokemon with any of the properties you might want. +It has a lot of arguments in order to offer this flexibility. +`side`, `slot`, `species` and `level` are the required arguments +`side` determines if the pokemon will be created as a player pokemon or an enemy pokemon: 0 will put the pokemon in the player party and 1 in the enemy party +`slot` determines the slot in the player or enemy party the pokemon will occupy. `slot` goes from 0 to 5 but if `side` is 0 (player pokemon), setting the slot to 6 will instead give the pokemon to player automatically putting it in the first empty slot or sending it to the PC when the party is the full. (Setting `slot` to 6 when trying to create an enemy pokemon will result in the `createmon` command being ignored) +`species` and `level` refers to the species id and the level of the pokemon you want to generate +`item`, `ball`, `nature`, `abilityNum`, `gender`, `hpEv`, `atkEv`, `defEv`, `speedEv`, `spAtkEv`, `spDefEv`, `hpIv`, `atkIv`, `defIv`, `speedIv`, `spAtkIv`, `spDefIv`, `move1`, `move2`, `move3`, `move4`, `shinyMode`, `gmaxFactor`, `teraType`, `dmaxLevel` are the optional arguments. They are pretty explicit in what they refer to but let's describe what they default to when they are not present. +`item` refers to the item the mon is holding. If the argument is missing, the mon won't be holding anything +`ball` refers to the type of ball the pokemon comes out of. This defaults to a PokeBall and if you are generating a wildmon, this will be overwritten if the pokemon is captured +`nature`, and `gender` will default to random values +`abilityNum` will default to the value corresponding to the personality they will roll (in practice, it's random but it will have correlations with other if the pokemon parameters) +`hpEv`, `atkEv`, `defEv`, `speedEv`, `spAtkEv`, `spDefEv` will default to 0 +`hpIv`, `atkIv`, `defIv`, `speedIv`, `spAtkIv`, `spDefIv` will default to `USE_RANDOM_IVS` which tell the game to roll a random IV value (between 0 and 31). If the generated species has a `perfectIVCount`, only the random values will be eligible to be perfected. +`move1`, `move2`, `move3`, `move4` will default to `MOVE_DEFAULT` which tells the game to fill the slot with the last level up move available +`shinyMode` will default to SHINY_MODE_RANDOM doing random roll(s) to check if the mon is shiny. (The other possibile values for shinyMode are SHINY_MODE_ALWAYS and SHINY_MODE_NEVER to force the pokemon to be shiny or not be shiny respectively) +`gmaxFactor` default to FALSE +`teraType` will default to the value corresponding to the personality they will roll (in practice, it's random but it will have correlations with other if the pokemon parameters) +`dmaxLevel` will deafult to 0 + +### `givemon` + +`givemon` uses the same arguments as `createmon` minus `side` and `slot`. This is because `givemon` is almost equivalent to `createmon 0 6 ...` and just gives the mon to the player after generating it. The exception being that `givemon` interacts with the abilities `Synchronize` and `Cute Charm` slightly differently than `createmon` when `nature` or `gender` are not explicitly set. + +### `setwildbattle (species:req, level:req, item=ITEM_NONE, species2=SPECIES_NONE, level2=0, item2=ITEM_NONE)` + +`setwildbattle` is a much simpler way to generate a wildmon ready for the player to fight. +It only takes 3 arguments (or 6 if you want to make it a double wild battle) +`species` and `level` refers to the species id and the level of the pokemon you want to generate +`item` refers to the item the mon is holding. If the argument is missing, the mon won't be holding anything +`species2`, `level2`, and `item2` are relate to the species, level and item of the second mon generated in case you want to make a double wild battle +The other properties will like IVs and personality will be set random just like for a regular wild battle +Mons generated with `setwildbattle` will always be considered static encounters (STATIC_WILDMON_ORIGIN) and will thus be eligible to be affected by Synchronize and Cute Charm + +### Synchronize and Cute Charm + +`nature` and `gender` also accept `NATURE_MAY_SYNCHRONIZE` and `MON_GENDER_MAY_CUTE_CHARM` respectively as arguments +`NATURE_RANDOM` and `MON_GENDER_RANDOM` always return a random nature/gender and never check for Synchronize or Cute Charm. If you want the generated mon to have a chance to receive the effcets of Synchronize or Cute Charm, you need to use `NATURE_MAY_SYNCHRONIZE` and `MON_GENDER_MAY_CUTE_CHARM` respectively for nature and gender. +When you use `NATURE_MAY_SYNCHRONIZE` or `MON_GENDER_MAY_CUTE_CHARM`, you are telling the game can check if the player has a pokemon with Synchronize or Cute Charm in the first slot of its party and roll a die to see if the nature or gender should be fixed based on the ability or rolled normally. +The mon generated also need to be of the right "origin" to be eligible for Synchronize or Cute Charm. We don't want to "synchronize" a mon belonging to a trainer or change the gender of a gift mon with Cute Charm. So if a mon is generated for the player side, it will be considered a "gift mon" (GIFTMON_ORIGIN) and if a mon is generated on the enemy side, it will be considered a static wild encounter (STATIC_WILDMON_ORIGIN). +`givemon` will default to use `NATURE_MAY_SYNCHRONIZE` and `MON_GENDER_MAY_CUTE_CHARM` because we assume you will use givemon to create "gift mons" but if you don't want it to apply in a specific script, you can explicitly use `NATURE_RANDOM` and `MON_GENDER_RANDOM` instead +`createmon` on the other hand default to `NATURE_RANDOM` and `MON_GENDER_RANDOM` so you need to explicitly use `NATURE_MAY_SYNCHRONIZE` or `MON_GENDER_MAY_CUTE_CHARM` for the generated mon to be considered a gift mon or a static wild encounter +Static encounters and Gift mons eligibility to Synchronize vary through generations in the official games so you can use the config `OW_SYNCHRONIZE_NATURE` to match your preference or you can check the `src/ow_synchronize.c` to modify the Synchronize and Cute Charm eligibility of different origins however you like + +## Through Code + +A lot of places in the game generate mons, when you start a wild encounter, when a trainer generates its party from the data in trainers.party or even when you call one of the script described in the previous section. So let's go through some of the most common functions. +Note: None of the functions described here allocate memory for the Pokemon struct, they all expect a pointer they will fill the data with. +It means they are usually called with `&gPlayerParty[index]` or `&gEnemyParty[index]` because these are places in memory reserved for Pokemon struct. + +### The basics + +To generate a mon ready for battle, you usually need to go through these steps: +- generate a personality value +- fill the generic mon structure based on species, level and personality +- set IVs and EVs +- (re-)compute stats (this step does not happen if you are generating a `struct boxPokemon` instead of `struct Pokemon`) +- set the moves + +### `GetMonPersonality` + +`GetMonPersonality` is the easiest way to make a personality value. It takes 4 arguments `species`, `gender`, `nature` and `unownLetter` then it rolls random personality values until it finds one that match all the selected criteria. +For example, if you want a personality for Wally's male Ralts, you would write: +`personality = GetMonPersonality(SPECIES_RALTS, MON_MALE, NATURE_RANDOM, RANDOM_UNOWN_LETTER);` +and if you want a personality for a brave J Unown, you would write +`personality = GetMonPersonality(SPECIES_UNOWN, MON_GENDER_RANDOM, NATURE_BRAVE, 9);` (J is the 10th letter of alphabet but for unown A starts at 0 so B is 1 and J is 9) +As you can see, you can use either a specific value or a special value `MON_GENDER_RANDOM`, `NATURE_RANDOM` or `RANDOM_UNOWN_LETTER` to tell the function to return any pokemon matching the other properties. This means, writing: +`personality = GetMonPersonality(SPECIES_X, MON_GENDER_RANDOM, NATURE_RANDOM, RANDOM_UNOWN_LETTER);` +id equivalent to +`personality = Random32();` +which is why `Random32()` is used throughout to generate a personality value in certain circumstances + +### `CreateMon` and `CreateBoxMon` + +`CreateMon` is the most basic function to create a generic Pokemon struct. It takes 5 arguments: +`mon` is a pointer to the mon struct you want to set the data for +`species` and `level` are fairly explicit +`personality` is the personality value you want to use to create your mon, it will determine a lot of your mon properties and will usually be a number that you generated by following the instructions from the previous section +`trainerID` is a special type of struct that explain how the function should set up the otId of the pokemon. Usually you will want to use one of these 3 macros for the argument: +`OTID_STRUCT_PLAYER_ID` which means the pokemon should the player otId (used for wildmon so they the get the player id when captured) +`OTID_STRUCT_PRESET(value)` which set a specific otId that you pick/write yourself +`OTID_STRUCT_RANDOM_NO_SHINY` which picks a random otId and forces the mon to not be shiny even if the random otId and chosen personality would have made the mon shiny. It is used by NPC trainers. +Both `CreateMon` and `CreateBoxMon` erase the pokemon data in the pointer before they add the new data so every value they don't set will be zero-ed in some way. This is why they are considered "base" functions. +The values set by `CreateMon` and `CreateBoxMon` are the gender, ability num, tera type and nature (based on personality); the met info (location, level and game), the original trainer name, gender and language (always set to the player even for enemy trainer mons), the starting xp (based on the xp required to reach the level the pokemon is at), the starting friendship (based on the species info) and the shinyness. +`CreateMon` and `CreateBoxMon` are very similar. In fact, the `CreateMon` function calls `CreateBoxMon` to generate the boxmon part of its structure but it also sets the level and a mail object (to an empty value) + +### Setting IVs and EVs + +Usually, you will want to use `SetBoxMonIVs(mon->box, ivs)` to set the ivs of the pokemon you are generating. The reason is that not only can you quickly set all ivs of your mon to a single value: `SetBoxMonIVs(mon->box, 15)` will set all IVs to 15. You can also use the special argument `USE_RANDOM_IVS`. When used with `USE_RANDOM_IVS`, `SetBoxMonIvs` will not only pick a random value between 0 and 31 for each stat, it will also allocate some perfect iv if the species of the mon has a perfectIvCount set in the species data. For example if you are generating a legendary with a perfectIvCount of 3, using `SetBoxMonIVs(mon->box, USE_RANDOM_IVS)` will guarantee that at least 3 IVs are set to 31 +The other way to assign IV is to use `SetMonData`, for example: +`SetMonData(mon, MON_DATA_HP_IV, 15)` +with the stats being in order `MON_DATA_HP_IV`, `MON_DATA_ATK_IV`, `MON_DATA_DEF_IV`, `MON_DATA_SPEED_IV`, `MON_DATA_SPATK_IV` and `MON_DATA_SPDEF_IV` +You can also use a loop like this: +```c +for (i = 0; i < NUM_STATS; i++) + SetMonData(mon, MON_DATA_HP_IV + i, iv_array[i]) +``` +if you happen to have an array containing the values you want but be careful about the order if you use this method +EVs default to 0 when a mon is generated with `CreateMon` or `CreateBoxMon` so you may not need to anything if you want to keep it that way. If you want to change them, there are currently no utilities to set EVs at the moment so you will need to use the `SetMonData` method as well: +`SetMonData(mon, MON_DATA_HP_EV, 252)` +The stats have the same names as the IVs with I cheange into an E : `MON_DATA_HP_EV`, `MON_DATA_ATK_EV`, `MON_DATA_DEF_EV`, `MON_DATA_SPEED_EV`, `MON_DATA_SPATK_EV` and `MON_DATA_SPDEF_EV` so the loop method works here too +```c +for (i = 0; i < NUM_STATS; i++) + SetMonData(mon, MON_DATA_HP_EV + i, ev_array[i]) +``` +Just be careful when setting IVs and EVs with SetMonData because they are no check to make sure the IV and EV values you are setting are valid and this may cause some issues. + +### `CalculateMonStats` + +After all the IVs and EVs have been set for your mon, it's important to run `CalculateMonStats(mon)`. The function only has one argument so it's pretty simple but don't forget this step or you may have some isseus + +### Setting moves + +To set a move in slot `slot`, you would need to write: +```c +enum Move move = MOVE_X; +u32 pp = GetMovePP(move); +SetMonData(mon, MON_DATA_MOVE1 + slot, &move); +SetMonData(mon, MON_DATA_PP1 + slot, &spp); +``` +where slot can be between 0 and 3 to represent the 1st to 4th move +You can also call the function `GiveMonInitialMoveset(mon)` that will give your pokemon its last 4 level-up moves available similarly to the wild pokemon you might enciunter + +## Going from 1.14 to 1.15 + +When going from 1.14 to 1.15, Expansion deleted multiple functions related to mon generation that were not used in Expansion anymore. +The following functions were deleted +```c +void CreateMonWithNature(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 nature); +void CreateMonWithGenderNatureLetter(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 gender, u8 nature, u8 unownLetter); +void CreateMonWithIVsOTID(struct Pokemon *mon, u16 species, u8 level, u8 *ivs, u32 otId); +void CreateMonWithEVSpread(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 evSpread); +void CreateMonWithEVSpreadNatureOTID(struct Pokemon *mon, u16 species, u8 level, u8 nature, u8 fixedIV, u8 evSpread, u32 otId); +``` +If you add custom code relying on those functions, I would advise to simple recode them using the methods described in the previous section. If you follow the steps, you should be able to rewrite a function with the same effect easily. + +However the move to 1.15 also completely rewrote these two functions: +```c +void CreateMon(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId); +void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId); +``` +`CreateMon` and `CreateBoxMon` now have different arguments and do less things than their 1.14 counterparts. If you have code that used those functions, we recommend you use these legacy version of `CreateMon` and `CreateBoxMon`: +```c +void CreateMonLegacy(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId) +{ + u32 personality = hasFixedPersonality ? fixedPersonality : Random32(); + struct OriginalTrainerId otId = {otIdType, fixedOtId}; + CreateMonWithIVs(mon, species, level, personality, otId, fixedIV); + GiveMonInitialMoveset(mon); +} + +void CreateBoxMonLegacy(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId) +{ + u32 personality = hasFixedPersonality ? fixedPersonality : Random32(); + struct OriginalTrainerId otId = {otIdType, fixedOtId}; + CreateBoxMon(boxMon, species, level, personality, otId); + SetBoxMonIVs(boxMon, fixedIV); + GiveBoxMonInitialMoveset(boxMon); +} +``` +These two legacy functions use the 1.15 functions to recreate the 1.14 versions of `CreateMon` and `CreateBoxMon` with the same arguments and the same effects. Add these two legacy functions to your code then change your custom code that was relying on 1.14 `CreateMon` or `CreateBoxMon` to use `CreateMonLegacy` or `CreateBoxMonLegacy` instead and everything should work the same as before diff --git a/include/constants/moves.h b/include/constants/moves.h index d920bd00ac..01a7e15f79 100644 --- a/include/constants/moves.h +++ b/include/constants/moves.h @@ -1011,7 +1011,7 @@ enum __attribute__((packed)) Move MOVES_COUNT_ALL = MOVES_COUNT_DYNAMAX, }; -// Used for checks for moves affected by Disable, Mimic, etc. -#define MOVE_UNAVAILABLE 0xFFFF +#define MOVE_DEFAULT 0xFFFE // Used when creating a mon to be replaced by a level-up move +#define MOVE_UNAVAILABLE 0xFFFF // Used for checks for moves affected by Disable, Mimic, etc. #endif // GUARD_CONSTANTS_MOVES_H diff --git a/include/constants/pokemon.h b/include/constants/pokemon.h index 59bf27d823..5a3c9b8985 100644 --- a/include/constants/pokemon.h +++ b/include/constants/pokemon.h @@ -76,6 +76,9 @@ enum __attribute__((packed)) Type #define NATURE_QUIRKY 24 // Neutral #define NUM_NATURES 25 +#define NATURE_RANDOM NUM_NATURES +#define NATURE_MAY_SYNCHRONIZE NUM_NATURES + 1 + // Pokémon Stats enum __attribute__((packed)) Stat { @@ -154,9 +157,12 @@ enum __attribute__((packed)) Stat #define MAX_DYNAMAX_LEVEL 10 -#define OT_ID_PLAYER_ID 0 -#define OT_ID_PRESET 1 -#define OT_ID_RANDOM_NO_SHINY 2 +enum OtIdMethod +{ + OT_ID_PLAYER_ID, + OT_ID_PRESET, + OT_ID_RANDOM_NO_SHINY +}; #define MON_GIVEN_TO_PARTY 0 #define MON_GIVEN_TO_PC 1 @@ -179,6 +185,9 @@ enum __attribute__((packed)) Stat #define MON_FEMALE 0xFE #define MON_GENDERLESS 0xFF +#define MON_GENDER_RANDOM 0x01 +#define MON_GENDER_MAY_CUTE_CHARM 0x02 + // Constants for AdjustFriendship #define FRIENDSHIP_EVENT_GROW_LEVEL 0 #define FRIENDSHIP_EVENT_VITAMIN 1 // unused, handled by PokemonUseItemEffects diff --git a/include/global.h b/include/global.h index 59616de629..a0252e3a85 100644 --- a/include/global.h +++ b/include/global.h @@ -150,6 +150,8 @@ #define FEATURE_FLAG_ASSERT(flag, id) STATIC_ASSERT(flag > TEMP_FLAGS_END || flag == 0, id) +#define READ_OTID_FROM_SAVE T1_READ_32(gSaveBlock2Ptr->playerTrainerId) + // NOTE: This uses hardware timers 2 and 3; this will not work during active link connections or with the eReader static inline void CycleCountStart() { diff --git a/include/ow_synchronize.h b/include/ow_synchronize.h new file mode 100644 index 0000000000..83a7ed9c31 --- /dev/null +++ b/include/ow_synchronize.h @@ -0,0 +1,15 @@ +#ifndef GUARD_OW_SYNCHRONIZE_H +#define GUARD_OW_SYNCHRONIZE_H + +enum GeneratedMonOrigin +{ + WILDMON_ORIGIN, + STATIC_WILDMON_ORIGIN, + ROAMER_ORIGIN, + GIFTMON_ORIGIN +}; + +u32 GetSynchronizedNature(enum GeneratedMonOrigin origin, u32 species); +u32 GetSynchronizedGender(enum GeneratedMonOrigin origin, u32 species); + +#endif // GUARD_OW_SYNCHRONIZE_H diff --git a/include/pokemon.h b/include/pokemon.h index e7098f59de..868c41e1e5 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -658,6 +658,7 @@ extern const u16 gKyuremBlackSwapMoveTable[][2]; #endif //P_FUSION_FORMS #define NUM_UNOWN_FORMS 28 +#define RANDOM_UNOWN_LETTER 0 #define GET_UNOWN_LETTER(personality) (( \ (((personality) & 0x03000000) >> 18) \ @@ -668,6 +669,16 @@ extern const u16 gKyuremBlackSwapMoveTable[][2]; #define GET_SHINY_VALUE(otId, personality) (HIHALF(otId) ^ LOHALF(otId) ^ HIHALF(personality) ^ LOHALF(personality)) +struct OriginalTrainerId +{ + enum OtIdMethod method; + u32 value; +}; + +#define OTID_STRUCT_PLAYER_ID ((struct OriginalTrainerId) {OT_ID_PLAYER_ID, 0}) +#define OTID_STRUCT_PRESET(value) ((struct OriginalTrainerId) {OT_ID_PRESET, value}) +#define OTID_STRUCT_RANDOM_NO_SHINY ((struct OriginalTrainerId) {OT_ID_RANDOM_NO_SHINY, 0}) + extern u8 gPlayerPartyCount; extern struct Pokemon gPlayerParty[PARTY_SIZE]; extern u8 gEnemyPartyCount; @@ -696,18 +707,18 @@ void ZeroBoxMonData(struct BoxPokemon *boxMon); void ZeroMonData(struct Pokemon *mon); void ZeroPlayerPartyMons(void); void ZeroEnemyPartyMons(void); -void CreateMon(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId); -void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId); -void CreateMonWithNature(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 nature); -void CreateMonWithGenderNatureLetter(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 gender, u8 nature, u8 unownLetter); +u32 GetMonPersonality(u16 species, u8 gender, u8 nature, u8 unownLetter); +void CreateMon(struct Pokemon *mon, u16 species, u8 level, u32 personality, struct OriginalTrainerId); +void CreateRandomMon(struct Pokemon *mon, u16 species, u8 level); +void CreateRandomMonWithIVs(struct Pokemon *mon, u16 species, u8 level, u8 fixedIv); +void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u32 personality, struct OriginalTrainerId); +void CreateMonWithIVs(struct Pokemon *mon, u16 species, u8 level, u32 personality, struct OriginalTrainerId trainerId, u8 fixedIV); +void SetBoxMonIVs(struct BoxPokemon *mon, u8 fixedIV); void CreateMaleMon(struct Pokemon *mon, u16 species, u8 level); void CreateMonWithIVsPersonality(struct Pokemon *mon, u16 species, u8 level, u32 ivs, u32 personality); -void CreateMonWithIVsOTID(struct Pokemon *mon, u16 species, u8 level, u8 *ivs, u32 otId); -void CreateMonWithEVSpread(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 evSpread); void CreateBattleTowerMon(struct Pokemon *mon, struct BattleTowerPokemon *src); void CreateBattleTowerMon_HandleLevel(struct Pokemon *mon, struct BattleTowerPokemon *src, bool8 lvl50); void CreateApprenticeMon(struct Pokemon *mon, const struct Apprentice *src, u8 monId); -void CreateMonWithEVSpreadNatureOTID(struct Pokemon *mon, u16 species, u8 level, u8 nature, u8 fixedIV, u8 evSpread, u32 otId); void ConvertPokemonToBattleTowerPokemon(struct Pokemon *mon, struct BattleTowerPokemon *dest); enum TrainerPicID GetUnionRoomTrainerPic(void); enum TrainerClassID GetUnionRoomTrainerClass(void); @@ -723,6 +734,8 @@ void SetMonMoveSlot(struct Pokemon *mon, enum Move move, u8 slot); void SetBattleMonMoveSlot(struct BattlePokemon *mon, enum Move move, u8 slot); void GiveMonInitialMoveset(struct Pokemon *mon); void GiveBoxMonInitialMoveset(struct BoxPokemon *boxMon); +void GiveMonDefaultMove(struct Pokemon *mon, u32 slot); +void GiveBoxMonDefaultMove(struct BoxPokemon *boxMon, u32 slot); enum Move MonTryLearningNewMoveAtLevel(struct Pokemon *mon, bool32 firstMove, u32 level); enum Move MonTryLearningNewMove(struct Pokemon *mon, bool8 firstMove); void DeleteFirstMoveAndGiveMoveToMon(struct Pokemon *mon, enum Move move); @@ -753,7 +766,7 @@ u32 GetBoxMonData2(struct BoxPokemon *boxMon, s32 field); void SetMonData(struct Pokemon *mon, s32 field, const void *dataArg); void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg); void CopyMon(void *dest, void *src, size_t size); -u8 GiveMonToPlayer(struct Pokemon *mon); +u8 GiveCapturedMonToPlayer(struct Pokemon *mon); u8 CopyMonToPC(struct Pokemon *mon); u8 CalculatePlayerPartyCount(void); u8 CalculateEnemyPartyCount(void); @@ -907,5 +920,6 @@ struct Pokemon *GetSavedPlayerPartyMon(u32 index); u8 *GetSavedPlayerPartyCount(void); void SavePlayerPartyMon(u32 index, struct Pokemon *mon); bool32 IsSpeciesOfType(u32 species, enum Type type); +u32 GiveScriptedMonToPlayer(struct Pokemon *mon, u8 slot); #endif // GUARD_POKEMON_H diff --git a/include/pokemon_storage_system.h b/include/pokemon_storage_system.h index 774236dab5..52aa6719b6 100644 --- a/include/pokemon_storage_system.h +++ b/include/pokemon_storage_system.h @@ -49,7 +49,6 @@ void SetBoxMonNickAt(u8 boxId, u8 boxPosition, const u8 *nick); u32 GetAndCopyBoxMonDataAt(u8 boxId, u8 boxPosition, s32 request, void *dst); void SetBoxMonAt(u8 boxId, u8 boxPosition, struct BoxPokemon *src); void CopyBoxMonAt(u8 boxId, u8 boxPosition, struct BoxPokemon *dst); -void CreateBoxMonAt(u8 boxId, u8 boxPosition, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 personality, u8 otIDType, u32 otID); void ZeroBoxMonAt(u8 boxId, u8 boxPosition); void BoxMonAtToMon(u8 boxId, u8 boxPosition, struct Pokemon *dst); struct BoxPokemon *GetBoxedMonPtr(u8 boxId, u8 boxPosition); diff --git a/include/test/overworld_script.h b/include/test/overworld_script.h index a5bee797b2..650221499d 100644 --- a/include/test/overworld_script.h +++ b/include/test/overworld_script.h @@ -49,6 +49,8 @@ asm(".set FALSE, 0\n" ".set PARTY_SIZE, " STR(PARTY_SIZE) "\n" ".set VARS_START, " STR(VARS_START) "\n" ".set VARS_END, " STR(VARS_END) "\n" + ".set MON_GENDER_MAY_CUTE_CHARM, " STR(MON_GENDER_MAY_CUTE_CHARM) "\n" + ".set NATURE_MAY_SYNCHRONIZE, " STR(NATURE_MAY_SYNCHRONIZE) "\n" ".set SPECIAL_VARS_START, " STR(SPECIAL_VARS_START) "\n" ".set SPECIAL_VARS_END, " STR(SPECIAL_VARS_END) "\n"); diff --git a/include/wild_encounter.h b/include/wild_encounter.h index c8f60712f9..1c1f55f3b3 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -50,7 +50,6 @@ extern bool8 gIsSurfingEncounter; extern u8 gChainFishingDexNavStreak; void DisableWildEncounters(bool8 disabled); -u8 PickWildMonNature(void); bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior); bool8 SweetScentWildEncounter(void); bool8 DoesCurrentMapHaveFishingMons(void); diff --git a/src/battle_controllers.c b/src/battle_controllers.c index 977ed5d73c..02ff41e7ca 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -20,20 +20,21 @@ #include "overworld.h" #include "palette.h" #include "party_menu.h" +#include "pokemon_animation.h" #include "recorded_battle.h" #include "string_util.h" #include "sound.h" #include "task.h" #include "test_runner.h" -#include "util.h" #include "text.h" +#include "util.h" +#include "wild_encounter.h" #include "constants/abilities.h" #include "constants/item_effects.h" #include "constants/songs.h" #include "test/battle.h" #include "test/test.h" #include "test/test_runner_battle.h" -#include "pokemon_animation.h" static EWRAM_DATA u8 sLinkSendTaskId = 0; static EWRAM_DATA u8 sLinkReceiveTaskId = 0; @@ -152,12 +153,7 @@ void SetUpBattleVarsAndBirchZigzagoon(void) BattleAI_SetupFlags(); if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) - { - ZeroEnemyPartyMons(); - CreateMon(&gEnemyParty[0], SPECIES_ZIGZAGOON, 2, USE_RANDOM_IVS, 0, 0, OT_ID_PLAYER_ID, 0); - i = 0; - SetMonData(&gEnemyParty[0], MON_DATA_HELD_ITEM, &i); - } + CreateWildMon(SPECIES_ZIGZAGOON, 2); } void InitBattleControllers(void) diff --git a/src/battle_factory.c b/src/battle_factory.c index 2b9783f57e..fb7498203b 100644 --- a/src/battle_factory.c +++ b/src/battle_factory.c @@ -361,7 +361,7 @@ static void SetPlayerAndOpponentParties(void) monId = gSaveBlock2Ptr->frontier.rentalMons[i].monId; ivs = gSaveBlock2Ptr->frontier.rentalMons[i].ivs; - CreateFacilityMon(&gFacilityTrainerMons[monId], monLevel, ivs, OT_ID_PLAYER_ID, FLAG_FRONTIER_MON_FACTORY, &gPlayerParty[i]); + CreateFacilityMon(&gFacilityTrainerMons[monId], monLevel, ivs, READ_OTID_FROM_SAVE, FLAG_FRONTIER_MON_FACTORY, &gPlayerParty[i]); } } @@ -373,7 +373,7 @@ static void SetPlayerAndOpponentParties(void) { monId = gSaveBlock2Ptr->frontier.rentalMons[i + FRONTIER_PARTY_SIZE].monId; ivs = gSaveBlock2Ptr->frontier.rentalMons[i + FRONTIER_PARTY_SIZE].ivs; - CreateFacilityMon(&gFacilityTrainerMons[monId], monLevel, ivs, OT_ID_PLAYER_ID, FLAG_FRONTIER_MON_FACTORY, &gEnemyParty[i]); + CreateFacilityMon(&gFacilityTrainerMons[monId], monLevel, ivs, READ_OTID_FROM_SAVE, FLAG_FRONTIER_MON_FACTORY, &gEnemyParty[i]); } break; } @@ -666,7 +666,7 @@ void FillFactoryBrainParty(void) fixedIV = GetFactoryMonFixedIV(challengeNum + 2, FALSE); monLevel = SetFacilityPtrsGetLevel(); i = 0; - otId = T1_READ_32(gSaveBlock2Ptr->playerTrainerId); + otId = READ_OTID_FROM_SAVE; while (i != FRONTIER_PARTY_SIZE) { @@ -845,7 +845,7 @@ static void FillFactoryFrontierTrainerParty(u16 trainerId, u8 firstMonId) } level = SetFacilityPtrsGetLevel(); - otID = T1_READ_32(gSaveBlock2Ptr->playerTrainerId); + otID = READ_OTID_FROM_SAVE; for (i = 0; i < FRONTIER_PARTY_SIZE; i++) { u16 monId = gFrontierTempParty[i]; @@ -860,7 +860,7 @@ static void FillFactoryTentTrainerParty(u16 trainerId, u8 firstMonId) u8 i; u8 level = TENT_MIN_LEVEL; u8 fixedIV = 0; - u32 otID = T1_READ_32(gSaveBlock2Ptr->playerTrainerId); + u32 otID = READ_OTID_FROM_SAVE; for (i = 0; i < FRONTIER_PARTY_SIZE; i++) { diff --git a/src/battle_factory_screen.c b/src/battle_factory_screen.c index c787bc57c9..31bf9ab37e 100644 --- a/src/battle_factory_screen.c +++ b/src/battle_factory_screen.c @@ -1725,7 +1725,7 @@ static void CreateFrontierFactorySelectableMons(u8 firstMonId) level = FRONTIER_MAX_LEVEL_50; rentalRank = GetNumPastRentalsRank(battleMode, lvlMode); - otId = T1_READ_32(gSaveBlock2Ptr->playerTrainerId); + otId = READ_OTID_FROM_SAVE; for (i = 0; i < SELECTABLE_MONS_COUNT; i++) { @@ -1750,7 +1750,7 @@ static void CreateSlateportTentSelectableMons(u8 firstMonId) u32 otId = 0; gFacilityTrainerMons = gSlateportBattleTentMons; - otId = T1_READ_32(gSaveBlock2Ptr->playerTrainerId); + otId = READ_OTID_FROM_SAVE; for (i = 0; i < SELECTABLE_MONS_COUNT; i++) { diff --git a/src/battle_frontier.c b/src/battle_frontier.c index 873bdbb0d6..41116e52fa 100644 --- a/src/battle_frontier.c +++ b/src/battle_frontier.c @@ -322,7 +322,7 @@ void CreateFacilityMon(const struct TrainerMon *fmon, u16 level, u8 fixedIV, u32 } ModifyPersonalityForNature(&personality, fmon->nature); - CreateMon(dst, fmon->species, level, fixedIV, TRUE, personality, otID, OT_ID_PRESET); + CreateMonWithIVs(dst, fmon->species, level, personality, OTID_STRUCT_PRESET(otID), fixedIV); friendship = MAX_FRIENDSHIP; // Give the chosen Pokémon its specified moves. diff --git a/src/battle_main.c b/src/battle_main.c index 0ea3a2eb41..96a5503aed 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -1898,8 +1898,7 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer s32 ball = -1; u32 personalityHash = GeneratePartyHash(trainer, i); const struct TrainerMon *partyData = trainer->party; - u32 otIdType = OT_ID_RANDOM_NO_SHINY; - u32 fixedOtId = 0; + struct OriginalTrainerId otId = OTID_STRUCT_RANDOM_NO_SHINY; u32 abilityNum = 0; if (trainer->battleType != TRAINER_BATTLE_TYPE_SINGLES) @@ -1919,10 +1918,10 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer ModifyPersonalityForNature(&personalityValue, partyData[monIndex].nature); if (partyData[monIndex].isShiny) { - otIdType = OT_ID_PRESET; - fixedOtId = HIHALF(personalityValue) ^ LOHALF(personalityValue); + otId.method = OT_ID_PRESET; + otId.value = HIHALF(personalityValue) ^ LOHALF(personalityValue); } - CreateMon(&party[i], partyData[monIndex].species, partyData[monIndex].lvl, 0, TRUE, personalityValue, otIdType, fixedOtId); + 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]); diff --git a/src/battle_partner.c b/src/battle_partner.c index ea517d075e..5b07483dc7 100644 --- a/src/battle_partner.c +++ b/src/battle_partner.c @@ -71,7 +71,7 @@ void FillPartnerParty(u16 trainerId) else if (partyData[i].gender == TRAINER_MON_FEMALE) personality = (personality & 0xFFFFFF00) | GeneratePersonalityForGender(MON_FEMALE, partyData[i].species); ModifyPersonalityForNature(&personality, partyData[i].nature); - CreateMon(&gPlayerParty[i + 3], partyData[i].species, partyData[i].lvl, 0, TRUE, personality, OT_ID_PRESET, otID); + 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); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 3807cd1a37..6697fec26f 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -11544,7 +11544,7 @@ static void Cmd_givecaughtmon(void) SetMonData(caughtMon, MON_DATA_HELD_ITEM, &lostItem); // Restore non-berry items } - if (GiveMonToPlayer(caughtMon) != MON_GIVEN_TO_PARTY + if (GiveCapturedMonToPlayer(caughtMon) != MON_GIVEN_TO_PARTY && gBattleCommunication[MULTISTRING_CHOOSER] != B_MSG_SWAPPED_INTO_PARTY) { if (!ShouldShowBoxWasFullMessage()) diff --git a/src/daycare.c b/src/daycare.c index 49521521f4..0d12a2a714 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -1130,7 +1130,7 @@ void CreateEgg(struct Pokemon *mon, u16 species, bool8 setHotSpringsLocation) metloc_u8_t metLocation; u8 isEgg; - CreateMon(mon, species, EGG_HATCH_LEVEL, USE_RANDOM_IVS, FALSE, 0, OT_ID_PLAYER_ID, 0); + CreateRandomMonWithIVs(mon, species, EGG_HATCH_LEVEL, USE_RANDOM_IVS); metLevel = 0; ball = BALL_POKE; language = LANGUAGE_JAPANESE; @@ -1157,7 +1157,7 @@ static void SetInitialEggData(struct Pokemon *mon, u16 species, struct DayCare * u8 language; personality = daycare->offspringPersonality; - CreateMon(mon, species, EGG_HATCH_LEVEL, USE_RANDOM_IVS, TRUE, personality, OT_ID_PLAYER_ID, 0); + CreateMonWithIVs(mon, species, EGG_HATCH_LEVEL, personality, OTID_STRUCT_PLAYER_ID, USE_RANDOM_IVS); metLevel = 0; ball = BALL_POKE; language = LANGUAGE_JAPANESE; diff --git a/src/debug.c b/src/debug.c index 24d6616f5f..552ea65a4f 100644 --- a/src/debug.c +++ b/src/debug.c @@ -3009,7 +3009,10 @@ static void Debug_Display_Nature(u32 natureId, u32 digit, u8 windowId) StringCopy(gStringVar2, gText_DigitIndicator[digit]); ConvertIntToDecimalStringN(gStringVar3, natureId, STR_CONV_MODE_LEADING_ZEROS, 2); StringCopyPadded(gStringVar3, gStringVar3, CHAR_SPACE, 15); - StringCopy(gStringVar1, gNaturesInfo[natureId].name); + if (natureId == 0) + StringCopy(gStringVar1, COMPOUND_STRING("Random")); + else + StringCopy(gStringVar1, gNaturesInfo[natureId - 1].name); StringExpandPlaceholders(gStringVar4, COMPOUND_STRING("Nature ID: {STR_VAR_3}{CLEAR_TO 90}\n{STR_VAR_1}{CLEAR_TO 90}\n{CLEAR_TO 90}\n{STR_VAR_2}{CLEAR_TO 90}")); AddTextPrinterParameterized(windowId, DEBUG_MENU_FONT, gStringVar4, 0, 0, 0, NULL); } @@ -3059,8 +3062,8 @@ static void DebugAction_Give_Pokemon_SelectNature(u8 taskId) if (JOY_NEW(DPAD_UP)) { gTasks[taskId].tInput += sPowersOfTen[gTasks[taskId].tDigit]; - if (gTasks[taskId].tInput > NUM_NATURES - 1) - gTasks[taskId].tInput = NUM_NATURES - 1; + if (gTasks[taskId].tInput > NUM_NATURES) + gTasks[taskId].tInput = NUM_NATURES; } if (JOY_NEW(DPAD_DOWN)) { @@ -3074,7 +3077,10 @@ static void DebugAction_Give_Pokemon_SelectNature(u8 taskId) if (JOY_NEW(A_BUTTON)) { - sDebugMonData->nature = gTasks[taskId].tInput; + if (gTasks[taskId].tInput == 0) + sDebugMonData->nature = NATURE_RANDOM; + else + sDebugMonData->nature = gTasks[taskId].tInput - 1; gTasks[taskId].tInput = 0; gTasks[taskId].tDigit = 0; @@ -3319,7 +3325,11 @@ static u32 GetDebugPokemonTotalEV(void) static void Debug_Display_MoveInfo(enum Move moveId, u32 iteration, u32 digit, u8 windowId) { // Doesn't expand placeholdes so a 4th dynamic value can be shown. - u8 *end = StringCopy(gStringVar1, GetMoveName(moveId)); + u8 *end; + if (moveId == MOVES_COUNT) + end = StringCopy(gStringVar1, COMPOUND_STRING("Default")); + else + end = StringCopy(gStringVar1, GetMoveName(moveId)); WrapFontIdToFit(gStringVar1, end, DEBUG_MENU_FONT, WindowWidthPx(windowId)); StringCopyPadded(gStringVar1, gStringVar1, CHAR_SPACE, 15); StringCopy(gStringVar4, COMPOUND_STRING("Move ")); @@ -3399,7 +3409,7 @@ static void DebugAction_Give_Pokemon_Move(u8 taskId) if (JOY_NEW(DPAD_ANY)) { PlaySE(SE_SELECT); - Debug_HandleInput_Numeric(taskId, 0, MOVES_COUNT - 1, 4); + Debug_HandleInput_Numeric(taskId, 0, MOVES_COUNT, 4); Debug_Display_MoveInfo(gTasks[taskId].tInput, gTasks[taskId].tIterator, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId); } @@ -3407,8 +3417,10 @@ static void DebugAction_Give_Pokemon_Move(u8 taskId) if (JOY_NEW(A_BUTTON)) { // Set current value - sDebugMonData->monMoves[gTasks[taskId].tIterator] = gTasks[taskId].tInput; - + if (gTasks[taskId].tInput < MOVES_COUNT) + sDebugMonData->monMoves[gTasks[taskId].tIterator] = gTasks[taskId].tInput; + else + sDebugMonData->monMoves[gTasks[taskId].tIterator] = MOVE_DEFAULT; // If MOVE_NONE selected, stop asking for additional moves if (gTasks[taskId].tInput == MOVE_NONE) gTasks[taskId].tIterator = MAX_MON_MOVES; @@ -3442,8 +3454,6 @@ static void DebugAction_Give_Pokemon_Move(u8 taskId) static void DebugAction_Give_Pokemon_ComplexCreateMon(u8 taskId) //https://github.com/ghoulslash/pokeemerald/tree/custom-givemon { - enum NationalDexOrder nationalDexNum; - int sentToPc; struct Pokemon mon; u8 i; enum Move moves[MAX_MON_MOVES]; @@ -3470,9 +3480,8 @@ static void DebugAction_Give_Pokemon_ComplexCreateMon(u8 taskId) //https://githu } //Nature - if (nature == NUM_NATURES || nature == 0xFF) - nature = Random() % NUM_NATURES; - CreateMonWithNature(&mon, species, level, USE_RANDOM_IVS, nature); + u32 personality = GetMonPersonality(species, MON_GENDER_RANDOM, nature, RANDOM_UNOWN_LETTER); + CreateMon(&mon, species, level, personality, OTID_STRUCT_PLAYER_ID); //Shininess SetMonData(&mon, MON_DATA_IS_SHINY, &isShiny); @@ -3504,6 +3513,7 @@ static void DebugAction_Give_Pokemon_ComplexCreateMon(u8 taskId) //https://githu SetMonData(&mon, MON_DATA_HP_EV + i, &ev_val); } + GiveMonInitialMoveset(&mon); //Moves for (i = 0; i < MAX_MON_MOVES; i++) { @@ -3511,57 +3521,22 @@ static void DebugAction_Give_Pokemon_ComplexCreateMon(u8 taskId) //https://githu if (moves[0] != MOVE_NONE) SetMonMoveSlot(&mon, MOVE_NONE, i); - if (moves[i] == MOVE_NONE || moves[i] >= MOVES_COUNT) + if (moves[i] == MOVE_NONE) continue; - SetMonMoveSlot(&mon, moves[i], i); - } - - //Ability - if (abilityNum == 0xFF || GetAbilityBySpecies(species, abilityNum) == ABILITY_NONE) - { - do { - abilityNum = Random() % NUM_ABILITY_SLOTS; // includes hidden abilities - } while (GetAbilityBySpecies(species, abilityNum) == ABILITY_NONE); + if (moves[i] == MOVE_DEFAULT) + GiveMonDefaultMove(&mon, i); + else + SetMonMoveSlot(&mon, moves[i], i); } + // Ability SetMonData(&mon, MON_DATA_ABILITY_NUM, &abilityNum); //Update mon stats before giving it to the player CalculateMonStats(&mon); - // give player the mon - SetMonData(&mon, MON_DATA_OT_NAME, gSaveBlock2Ptr->playerName); - SetMonData(&mon, MON_DATA_OT_GENDER, &gSaveBlock2Ptr->playerGender); - for (i = 0; i < PARTY_SIZE; i++) - { - if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL) == SPECIES_NONE) - break; - } - - if (i >= PARTY_SIZE) - { - sentToPc = CopyMonToPC(&mon); - } - else - { - sentToPc = MON_GIVEN_TO_PARTY; - CopyMon(&gPlayerParty[i], &mon, sizeof(mon)); - gPlayerPartyCount = i + 1; - } - - //Pokedex entry - nationalDexNum = SpeciesToNationalPokedexNum(species); - switch(sentToPc) - { - case MON_GIVEN_TO_PARTY: - case MON_GIVEN_TO_PC: - GetSetPokedexFlag(nationalDexNum, FLAG_SET_SEEN); - GetSetPokedexFlag(nationalDexNum, FLAG_SET_CAUGHT); - break; - case MON_CANT_GIVE: - break; - } + GiveScriptedMonToPlayer(&mon, PARTY_SIZE); // Set flag for user convenience FlagSet(FLAG_SYS_POKEMON_GET); @@ -3728,14 +3703,12 @@ static void DebugAction_TimeMenu_ChangeWeekdays(u8 taskId) static void DebugAction_PCBag_Fill_PCBoxes_Fast(u8 taskId) //Credit: Sierraffinity { int boxId, boxPosition; - u32 personality; struct BoxPokemon boxMon; u16 species = SPECIES_BULBASAUR; u8 speciesName[POKEMON_NAME_LENGTH + 1]; - personality = Random32(); - - CreateBoxMon(&boxMon, species, 100, USE_RANDOM_IVS, FALSE, personality, OT_ID_PLAYER_ID, 0); + CreateBoxMon(&boxMon, species, 100, Random32(), OTID_STRUCT_PLAYER_ID); + //mons are created with 0 IVs for (boxId = 0; boxId < TOTAL_BOXES_COUNT; boxId++) { @@ -3773,7 +3746,9 @@ static void DebugAction_PCBag_Fill_PCBoxes_Slow(u8 taskId) { if (!spaceAvailable) PlayBGM(MUS_RG_MYSTERY_GIFT); - CreateBoxMon(&boxMon, species, 100, USE_RANDOM_IVS, FALSE, 0, OT_ID_PLAYER_ID, 0); + CreateBoxMon(&boxMon, species, 100, Random32(), OTID_STRUCT_PLAYER_ID); + SetBoxMonIVs(&boxMon, USE_RANDOM_IVS); + GiveBoxMonInitialMoveset(&boxMon); gPokemonStoragePtr->boxes[boxId][boxPosition] = boxMon; species = (species < NUM_SPECIES - 1) ? species + 1 : 1; spaceAvailable = TRUE; diff --git a/src/egg_hatch.c b/src/egg_hatch.c index 8a7ead73d7..79c2206a77 100644 --- a/src/egg_hatch.c +++ b/src/egg_hatch.c @@ -332,8 +332,7 @@ static void CreateHatchedMon(struct Pokemon *egg, struct Pokemon *temp) isModernFatefulEncounter = GetMonData(egg, MON_DATA_MODERN_FATEFUL_ENCOUNTER); ball = GetMonData(egg, MON_DATA_POKEBALL); - CreateMon(temp, species, EGG_HATCH_LEVEL, USE_RANDOM_IVS, TRUE, personality, OT_ID_PLAYER_ID, 0); - + CreateMonWithIVs(temp, species, EGG_HATCH_LEVEL, personality, OTID_STRUCT_PLAYER_ID, USE_RANDOM_IVS); for (i = 0; i < MAX_MON_MOVES; i++) SetMonData(temp, MON_DATA_MOVE1 + i, &moves[i]); diff --git a/src/field_specials.c b/src/field_specials.c index a75ffccd97..bf08bcdc0d 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -1490,7 +1490,7 @@ void SetShoalItemFlag(u16 unused) void LoadWallyZigzagoon(void) { u16 monData; - CreateMon(&gPlayerParty[0], SPECIES_ZIGZAGOON, 7, USE_RANDOM_IVS, FALSE, 0, OT_ID_PLAYER_ID, 0); + CreateRandomMon(&gPlayerParty[0], SPECIES_ZIGZAGOON, 7); monData = TRUE; SetMonData(&gPlayerParty[0], MON_DATA_ABILITY_NUM, &monData); monData = MOVE_TACKLE; diff --git a/src/frontier_util.c b/src/frontier_util.c index 61da211235..3e81d50ff4 100644 --- a/src/frontier_util.c +++ b/src/frontier_util.c @@ -2620,16 +2620,16 @@ void CreateFrontierBrainPokemon(void) if (!(selectedMonBits & 1)) continue; - do - { - j = Random32(); //should just be one while loop, but that doesn't match - } while (sFrontierBrainsMons[facility][symbol][i].nature != GetNatureFromPersonality(j)); - CreateMon(&gEnemyParty[monPartyId], + u32 personality = GetMonPersonality(sFrontierBrainsMons[facility][symbol][i].species, + MON_GENDER_RANDOM, + sFrontierBrainsMons[facility][symbol][i].nature, + RANDOM_UNOWN_LETTER); + CreateMonWithIVs(&gEnemyParty[monPartyId], sFrontierBrainsMons[facility][symbol][i].species, monLevel, - sFrontierBrainsMons[facility][symbol][i].fixedIV, - TRUE, j, - OT_ID_PRESET, FRONTIER_BRAIN_OTID); + personality, + OTID_STRUCT_PRESET(FRONTIER_BRAIN_OTID), + sFrontierBrainsMons[facility][symbol][i].fixedIV); SetMonData(&gEnemyParty[monPartyId], MON_DATA_HELD_ITEM, &sFrontierBrainsMons[facility][symbol][i].heldItem); for (j = 0; j < NUM_STATS; j++) SetMonData(&gEnemyParty[monPartyId], MON_DATA_HP_EV + j, &sFrontierBrainsMons[facility][symbol][i].evs[j]); diff --git a/src/link.c b/src/link.c index 50bcf555a0..af3b92d9e3 100644 --- a/src/link.c +++ b/src/link.c @@ -312,7 +312,7 @@ void SetLocalLinkPlayerId(u8 playerId) static void InitLocalLinkPlayer(void) { - gLocalLinkPlayer.trainerId = gSaveBlock2Ptr->playerTrainerId[0] | (gSaveBlock2Ptr->playerTrainerId[1] << 8) | (gSaveBlock2Ptr->playerTrainerId[2] << 16) | (gSaveBlock2Ptr->playerTrainerId[3] << 24); + gLocalLinkPlayer.trainerId = READ_OTID_FROM_SAVE; StringCopy(gLocalLinkPlayer.name, gSaveBlock2Ptr->playerName); gLocalLinkPlayer.gender = gSaveBlock2Ptr->playerGender; gLocalLinkPlayer.linkType = gLinkType; diff --git a/src/ow_synchronize.c b/src/ow_synchronize.c new file mode 100644 index 0000000000..e06c2e4958 --- /dev/null +++ b/src/ow_synchronize.c @@ -0,0 +1,114 @@ +#include "global.h" +#include "ow_synchronize.h" +#include "pokemon.h" +#include "random.h" +#include "constants/pokemon.h" + +static UNUSED bool32 HasHalfChance(u32 species); +static UNUSED bool32 HasTwoThirdsChance(u32 species); +static UNUSED bool32 IsFalse(u32 species); +static UNUSED bool32 IsTrue(u32 species); +static UNUSED bool32 IsTrueIfUndiscoveredEggGroup(u32 species); + +const static bool32 (*sSynchronizeModes[]) (u32) = +{ +#if OW_SYNCHRONIZE_NATURE == GEN_3 + [WILDMON_ORIGIN] = HasHalfChance, + [STATIC_WILDMON_ORIGIN] = IsFalse, + [ROAMER_ORIGIN] = IsFalse, + [GIFTMON_ORIGIN] = IsFalse, +#elif OW_SYNCHRONIZE_NATURE <= GEN_5 + [WILDMON_ORIGIN] = HasHalfChance, + [STATIC_WILDMON_ORIGIN] = HasHalfChance, + [ROAMER_ORIGIN] = IsFalse, + [GIFTMON_ORIGIN] = IsFalse, +#elif OW_SYNCHRONIZE_NATURE == GEN_6 + [WILDMON_ORIGIN] = HasHalfChance, + [STATIC_WILDMON_ORIGIN] = HasHalfChance, + [ROAMER_ORIGIN] = IsFalse, + [GIFTMON_ORIGIN] = IsTrueIfUndiscoveredEggGroup, +#elif OW_SYNCHRONIZE_NATURE == GEN_7 + [WILDMON_ORIGIN] = HasHalfChance, + [STATIC_WILDMON_ORIGIN] = HasHalfChance, + [ROAMER_ORIGIN] = IsFalse, + [GIFTMON_ORIGIN] = IsTrue, +#elif OW_SYNCHRONIZE_NATURE == GEN_8 + [WILDMON_ORIGIN] = IsTrue, + [STATIC_WILDMON_ORIGIN] = IsFalse, + [ROAMER_ORIGIN] = IsTrue, + [GIFTMON_ORIGIN] = IsFalse, +#else + [WILDMON_ORIGIN] = IsFalse, + [STATIC_WILDMON_ORIGIN] = IsFalse, + [ROAMER_ORIGIN] = IsFalse, + [GIFTMON_ORIGIN] = IsFalse, +#endif +}; + +const static bool32 (*sCuteCharmModes[]) (u32) = +{ + [WILDMON_ORIGIN] = HasTwoThirdsChance, + [STATIC_WILDMON_ORIGIN] = HasTwoThirdsChance, + [ROAMER_ORIGIN] = IsFalse, + [GIFTMON_ORIGIN] = IsFalse, +}; + +static UNUSED bool32 HasHalfChance(u32 species) +{ + return Random() % 2; +} + +static UNUSED bool32 HasTwoThirdsChance(u32 species) +{ + return Random() % 3; +} + +static UNUSED bool32 IsFalse(u32 species) +{ + return FALSE; +} + +static UNUSED bool32 IsTrue(u32 species) +{ + return TRUE; +} + +static UNUSED bool32 IsTrueIfUndiscoveredEggGroup(u32 species) +{ + return (gSpeciesInfo[species].eggGroups[0] == EGG_GROUP_NO_EGGS_DISCOVERED); +} + +static bool32 IsSynchronizeActive(void) +{ + return ((!GetMonData(&gPlayerParty[0], MON_DATA_SANITY_IS_EGG) + && GetMonAbility(&gPlayerParty[0]) == ABILITY_SYNCHRONIZE)); +} + +static bool32 IsCuteCharmActive(void) +{ + return ((!GetMonData(&gPlayerParty[0], MON_DATA_SANITY_IS_EGG) + && GetMonAbility(&gPlayerParty[0]) == ABILITY_CUTE_CHARM)); +} + +u32 GetSynchronizedNature(enum GeneratedMonOrigin origin, u32 species) +{ + if (!IsSynchronizeActive()) + return NATURE_RANDOM; + if (!(sSynchronizeModes[origin](species))) + return NATURE_RANDOM; + return GetMonData(&gPlayerParty[0], MON_DATA_PERSONALITY) % NUM_NATURES; +} + +u32 GetSynchronizedGender(enum GeneratedMonOrigin origin, u32 species) +{ + if (!IsCuteCharmActive()) + return MON_GENDER_RANDOM; + if (!(sCuteCharmModes[origin](species))) + return MON_GENDER_RANDOM; + u8 leadingMonGender = GetMonGender(&gPlayerParty[0]); + // misses mon is genderless check, although no genderless mon can have cute charm as ability + if (leadingMonGender == MON_FEMALE) + return MON_MALE; + else + return MON_FEMALE; +} diff --git a/src/pokemon.c b/src/pokemon.c index 0468f256ce..ff1c341ac4 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1020,48 +1020,108 @@ void ZeroEnemyPartyMons(void) ZeroMonData(&gEnemyParty[i]); } -void CreateMon(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId) +void CreateRandomMon(struct Pokemon *mon, u16 species, u8 level) +{ + CreateRandomMonWithIVs(mon, species, level, USE_RANDOM_IVS); +} + +void CreateRandomMonWithIVs(struct Pokemon *mon, u16 species, u8 level, u8 fixedIv) +{ + CreateMonWithIVs(mon, species, level, Random32(), OTID_STRUCT_PLAYER_ID, fixedIv); + GiveMonInitialMoveset(mon); +} + +void CreateMon(struct Pokemon *mon, u16 species, u8 level, u32 personality, struct OriginalTrainerId trainerId) { u32 mail; ZeroMonData(mon); - CreateBoxMon(&mon->box, species, level, fixedIV, hasFixedPersonality, fixedPersonality, otIdType, fixedOtId); + CreateBoxMon(&mon->box, species, level, personality, trainerId); SetMonData(mon, MON_DATA_LEVEL, &level); mail = MAIL_NONE; SetMonData(mon, MON_DATA_MAIL, &mail); +} + +void CreateMonWithIVs(struct Pokemon *mon, u16 species, u8 level, u32 personality, struct OriginalTrainerId trainerId, u8 fixedIV) +{ + CreateMon(mon, species, level, personality, trainerId); + SetBoxMonIVs(&mon->box, fixedIV); CalculateMonStats(mon); } -void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId) +void SetBoxMonIVs(struct BoxPokemon *mon, u8 fixedIV) { - u8 speciesName[POKEMON_NAME_LENGTH + 1]; - u32 personality = Random32(); - u32 value; - u16 checksum; - u8 i; + u32 i, value; enum Stat availableIVs[NUM_STATS]; enum Stat selectedIvs[NUM_STATS]; + + if (fixedIV < USE_RANDOM_IVS) + { + for (i = 0; i < NUM_STATS; i++) + SetBoxMonData(mon, MON_DATA_HP_IV + i, &fixedIV); + return; + } + + u32 iv; + u32 ivRandom = Random32(); + u32 species = GetBoxMonData(mon, MON_DATA_SPECIES); + value = (u16)ivRandom; + + iv = value & MAX_IV_MASK; + SetBoxMonData(mon, MON_DATA_HP_IV, &iv); + iv = (value & (MAX_IV_MASK << 5)) >> 5; + SetBoxMonData(mon, MON_DATA_ATK_IV, &iv); + iv = (value & (MAX_IV_MASK << 10)) >> 10; + SetBoxMonData(mon, MON_DATA_DEF_IV, &iv); + + value = (u16)(ivRandom >> 16); + + iv = value & MAX_IV_MASK; + SetBoxMonData(mon, MON_DATA_SPEED_IV, &iv); + iv = (value & (MAX_IV_MASK << 5)) >> 5; + SetBoxMonData(mon, MON_DATA_SPATK_IV, &iv); + iv = (value & (MAX_IV_MASK << 10)) >> 10; + SetBoxMonData(mon, MON_DATA_SPDEF_IV, &iv); + + if (gSpeciesInfo[species].perfectIVCount != 0) + { + iv = MAX_PER_STAT_IVS; + // Initialize a list of IV indices. + for (i = 0; i < NUM_STATS; i++) + availableIVs[i] = i; + + // Select the IVs that will be perfected. + for (i = 0; i < NUM_STATS && i < gSpeciesInfo[species].perfectIVCount; i++) + { + u8 index = Random() % (NUM_STATS - i); + selectedIvs[i] = availableIVs[index]; + RemoveIVIndexFromList(availableIVs, index); + SetBoxMonData(mon, MON_DATA_HP_IV + selectedIvs[i], &iv); + } + } +} + +void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u32 personality, struct OriginalTrainerId trainerId) +{ + u8 speciesName[POKEMON_NAME_LENGTH + 1]; + u32 value; + u16 checksum; bool32 isShiny; ZeroBoxMonData(boxMon); - // Determine original trainer ID - if (otIdType == OT_ID_RANDOM_NO_SHINY) + if (trainerId.method == OT_ID_RANDOM_NO_SHINY) { value = Random32(); isShiny = FALSE; } - else if (otIdType == OT_ID_PRESET) + else if (trainerId.method == OT_ID_PRESET) { - value = fixedOtId; - isShiny = GET_SHINY_VALUE(value, hasFixedPersonality ? fixedPersonality : personality) < SHINY_ODDS; + value = trainerId.value; + isShiny = GET_SHINY_VALUE(value, personality) < SHINY_ODDS; } else // Player is the OT { - value = gSaveBlock2Ptr->playerTrainerId[0] - | (gSaveBlock2Ptr->playerTrainerId[1] << 8) - | (gSaveBlock2Ptr->playerTrainerId[2] << 16) - | (gSaveBlock2Ptr->playerTrainerId[3] << 24); - + value = READ_OTID_FROM_SAVE; if (P_FLAG_FORCE_NO_SHINY != 0 && FlagGet(P_FLAG_FORCE_NO_SHINY)) { isShiny = FALSE; @@ -1089,19 +1149,17 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, if (gDexNavSpecies) totalRerolls += CalculateDexNavShinyRolls(); - while (GET_SHINY_VALUE(value, personality) >= SHINY_ODDS && totalRerolls > 0) + u32 shinyPersonality = personality; + while (GET_SHINY_VALUE(value, shinyPersonality) >= SHINY_ODDS && totalRerolls > 0) { - personality = Random32(); + shinyPersonality = Random32(); totalRerolls--; } - isShiny = GET_SHINY_VALUE(value, personality) < SHINY_ODDS; + isShiny = GET_SHINY_VALUE(value, shinyPersonality) < SHINY_ODDS; } } - if (hasFixedPersonality) - personality = fixedPersonality; - SetBoxMonData(boxMon, MON_DATA_PERSONALITY, &personality); SetBoxMonData(boxMon, MON_DATA_OT_ID, &value); @@ -1124,245 +1182,73 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, SetBoxMonData(boxMon, MON_DATA_POKEBALL, &value); SetBoxMonData(boxMon, MON_DATA_OT_GENDER, &gSaveBlock2Ptr->playerGender); - enum Type teraType = (boxMon->personality & 0x1) == 0 ? GetSpeciesType(species, 0) : GetSpeciesType(species, 1); + value = boxMon->personality & 0x1; + u32 teraType = value == 0 ? GetSpeciesType(species, 0) : GetSpeciesType(species, 1); SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, &teraType); - - if (fixedIV < USE_RANDOM_IVS) - { - SetBoxMonData(boxMon, MON_DATA_HP_IV, &fixedIV); - SetBoxMonData(boxMon, MON_DATA_ATK_IV, &fixedIV); - SetBoxMonData(boxMon, MON_DATA_DEF_IV, &fixedIV); - SetBoxMonData(boxMon, MON_DATA_SPEED_IV, &fixedIV); - SetBoxMonData(boxMon, MON_DATA_SPATK_IV, &fixedIV); - SetBoxMonData(boxMon, MON_DATA_SPDEF_IV, &fixedIV); - } - else - { - u32 iv; - u32 ivRandom = Random32(); - value = (u16)ivRandom; - - iv = value & MAX_IV_MASK; - SetBoxMonData(boxMon, MON_DATA_HP_IV, &iv); - iv = (value & (MAX_IV_MASK << 5)) >> 5; - SetBoxMonData(boxMon, MON_DATA_ATK_IV, &iv); - iv = (value & (MAX_IV_MASK << 10)) >> 10; - SetBoxMonData(boxMon, MON_DATA_DEF_IV, &iv); - - value = (u16)(ivRandom >> 16); - - iv = value & MAX_IV_MASK; - SetBoxMonData(boxMon, MON_DATA_SPEED_IV, &iv); - iv = (value & (MAX_IV_MASK << 5)) >> 5; - SetBoxMonData(boxMon, MON_DATA_SPATK_IV, &iv); - iv = (value & (MAX_IV_MASK << 10)) >> 10; - SetBoxMonData(boxMon, MON_DATA_SPDEF_IV, &iv); - - if (gSpeciesInfo[species].perfectIVCount != 0) - { - iv = MAX_PER_STAT_IVS; - // Initialize a list of IV indices. - for (i = 0; i < NUM_STATS; i++) - { - availableIVs[i] = i; - } - - // Select the IVs that will be perfected. - for (i = 0; i < NUM_STATS && i < gSpeciesInfo[species].perfectIVCount; i++) - { - u8 index = Random() % (NUM_STATS - i); - selectedIvs[i] = availableIVs[index]; - RemoveIVIndexFromList(availableIVs, index); - } - for (i = 0; i < NUM_STATS && i < gSpeciesInfo[species].perfectIVCount; i++) - { - switch (selectedIvs[i]) - { - case STAT_HP: - SetBoxMonData(boxMon, MON_DATA_HP_IV, &iv); - break; - case STAT_ATK: - SetBoxMonData(boxMon, MON_DATA_ATK_IV, &iv); - break; - case STAT_DEF: - SetBoxMonData(boxMon, MON_DATA_DEF_IV, &iv); - break; - case STAT_SPEED: - SetBoxMonData(boxMon, MON_DATA_SPEED_IV, &iv); - break; - case STAT_SPATK: - SetBoxMonData(boxMon, MON_DATA_SPATK_IV, &iv); - break; - case STAT_SPDEF: - SetBoxMonData(boxMon, MON_DATA_SPDEF_IV, &iv); - break; - default: - break; - } - } - } - } - + //using gen 3-4 ability formula, it was changed in later gens if (GetSpeciesAbility(species, 1)) - { - value = personality & 1; SetBoxMonData(boxMon, MON_DATA_ABILITY_NUM, &value); - } - - GiveBoxMonInitialMoveset(boxMon); } -void CreateMonWithNature(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 nature) -{ - u32 personality; - - do - { - personality = Random32(); - } - while (nature != GetNatureFromPersonality(personality)); - - CreateMon(mon, species, level, fixedIV, TRUE, personality, OT_ID_PLAYER_ID, 0); -} - -static bool32 GenderRatioCanBe(u32 genderRatio, u32 gender) +static bool32 IsValidGender(u32 gender) { switch (gender) { - case MON_MALE: - return genderRatio != MON_FEMALE && genderRatio != MON_GENDERLESS; - case MON_FEMALE: - return genderRatio != MON_MALE && genderRatio != MON_GENDERLESS; - case MON_GENDERLESS: - return genderRatio == MON_GENDERLESS; - default: - assertf(FALSE, "unknown gender: %d", gender); - return FALSE; + case MON_MALE: + case MON_FEMALE: + case MON_GENDERLESS: + case MON_GENDER_RANDOM: + return TRUE; + default: + return FALSE; } } -void CreateMonWithGenderNatureLetter(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 gender, u8 nature, u8 unownLetter) +u32 GetMonPersonality(u16 species, u8 gender, u8 nature, u8 unownLetter) { - u32 personality; - u32 genderRatio = gSpeciesInfo[species].genderRatio; + u32 personality, actualLetter; - if ((u8)(unownLetter - 1) < NUM_UNOWN_FORMS) + assertf(IsValidGender(gender), "invalid gender: %d", gender) { - u16 actualLetter; - - while (TRUE) - { - personality = Random32(); - actualLetter = GET_UNOWN_LETTER(personality); - - assertf(GenderRatioCanBe(genderRatio, gender), "genderRatio %d can't be gender %d", genderRatio, gender) - { - break; - } - - if (nature == GetNatureFromPersonality(personality) - && gender == GetGenderFromSpeciesAndPersonality(species, personality) - && actualLetter == unownLetter - 1) - { - break; - } - } - } - else - { - while (TRUE) - { - personality = Random32(); - - assertf(GenderRatioCanBe(genderRatio, gender), "genderRatio %d can't be gender %d", genderRatio, gender) - { - break; - } - - if (nature == GetNatureFromPersonality(personality) - && gender == GetGenderFromSpeciesAndPersonality(species, personality)) - { - break; - } - } + gender = MON_GENDER_RANDOM; } - CreateMon(mon, species, level, fixedIV, TRUE, personality, OT_ID_PLAYER_ID, 0); + assertf(nature <= NATURE_RANDOM, "invalid nature: %d", nature) + { + nature = NATURE_RANDOM; + } + + assertf(unownLetter <= NUM_UNOWN_FORMS, "invalid letter: %d", unownLetter) + { + unownLetter = RANDOM_UNOWN_LETTER; + } + + //gender outside valid gender ratios for species is not asserted because it could be triggered by cute charm + do + { + personality = Random32(); + actualLetter = GET_UNOWN_LETTER(personality); + } + while ((nature != GetNatureFromPersonality(personality) && nature != NATURE_RANDOM) + || (gender != GetGenderFromSpeciesAndPersonality(species, personality) && gender != MON_GENDER_RANDOM) + || ((actualLetter != unownLetter - 1) && unownLetter > 0)); + return personality; } // This is only used to create Wally's Ralts. void CreateMaleMon(struct Pokemon *mon, u16 species, u8 level) { - u32 personality; - u32 otId; - u32 genderRatio = gSpeciesInfo[species].genderRatio; - - while (TRUE) - { - otId = Random32(); - personality = Random32(); - - assertf(GenderRatioCanBe(genderRatio, MON_MALE), "genderRatio %d can't be MON_MALE", genderRatio) - { - break; - } - - if (GetGenderFromSpeciesAndPersonality(species, personality) == MON_MALE) - break; - } - - CreateMon(mon, species, level, USE_RANDOM_IVS, TRUE, personality, OT_ID_PRESET, otId); + 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, u16 species, u8 level, u32 ivs, u32 personality) { - CreateMon(mon, species, level, 0, TRUE, personality, OT_ID_PLAYER_ID, 0); + CreateMon(mon, species, level, personality, OTID_STRUCT_PLAYER_ID); SetMonData(mon, MON_DATA_IVS, &ivs); CalculateMonStats(mon); -} - -void CreateMonWithIVsOTID(struct Pokemon *mon, u16 species, u8 level, u8 *ivs, u32 otId) -{ - CreateMon(mon, species, level, 0, FALSE, 0, OT_ID_PRESET, otId); - SetMonData(mon, MON_DATA_HP_IV, &ivs[STAT_HP]); - SetMonData(mon, MON_DATA_ATK_IV, &ivs[STAT_ATK]); - SetMonData(mon, MON_DATA_DEF_IV, &ivs[STAT_DEF]); - SetMonData(mon, MON_DATA_SPEED_IV, &ivs[STAT_SPEED]); - SetMonData(mon, MON_DATA_SPATK_IV, &ivs[STAT_SPATK]); - SetMonData(mon, MON_DATA_SPDEF_IV, &ivs[STAT_SPDEF]); - CalculateMonStats(mon); -} - -void CreateMonWithEVSpread(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 evSpread) -{ - s32 i; - s32 statCount = 0; - u16 evAmount; - u8 evsBits; - - CreateMon(mon, species, level, fixedIV, FALSE, 0, OT_ID_PLAYER_ID, 0); - - evsBits = evSpread; - - for (i = 0; i < NUM_STATS; i++) - { - if (evsBits & 1) - statCount++; - evsBits >>= 1; - } - - evAmount = MAX_TOTAL_EVS / statCount; - - evsBits = 1; - - for (i = 0; i < NUM_STATS; i++) - { - if (evSpread & evsBits) - SetMonData(mon, MON_DATA_HP_EV + i, &evAmount); - evsBits <<= 1; - } - - CalculateMonStats(mon); + GiveMonInitialMoveset(mon); } void CreateBattleTowerMon(struct Pokemon *mon, struct BattleTowerPokemon *src) @@ -1372,7 +1258,7 @@ void CreateBattleTowerMon(struct Pokemon *mon, struct BattleTowerPokemon *src) u8 language; u8 value; - CreateMon(mon, src->species, src->level, 0, TRUE, src->personality, OT_ID_PRESET, src->otId); + CreateMon(mon, src->species, src->level, src->personality, OTID_STRUCT_PRESET(src->otId)); for (i = 0; i < MAX_MON_MOVES; i++) SetMonMoveSlot(mon, src->moves[i], i); @@ -1434,7 +1320,7 @@ void CreateBattleTowerMon_HandleLevel(struct Pokemon *mon, struct BattleTowerPok else level = src->level; - CreateMon(mon, src->species, level, 0, TRUE, src->personality, OT_ID_PRESET, src->otId); + CreateMon(mon, src->species, level, src->personality, OTID_STRUCT_PRESET(src->otId)); for (i = 0; i < MAX_MON_MOVES; i++) SetMonMoveSlot(mon, src->moves[i], i); @@ -1490,15 +1376,12 @@ void CreateApprenticeMon(struct Pokemon *mon, const struct Apprentice *src, u8 m u32 personality = ((gApprentices[src->id].otId >> 8) | ((gApprentices[src->id].otId & 0xFF) << 8)) + src->party[monId].species + src->number; - CreateMon(mon, + CreateMonWithIVs(mon, src->party[monId].species, GetFrontierEnemyMonLevel(src->lvlMode - 1), - MAX_PER_STAT_IVS, - TRUE, personality, - OT_ID_PRESET, - otId); - + OTID_STRUCT_PRESET(otId), + MAX_PER_STAT_IVS); SetMonData(mon, MON_DATA_HELD_ITEM, &src->party[monId].item); for (i = 0; i < MAX_MON_MOVES; i++) SetMonMoveSlot(mon, src->party[monId].moves[i], i); @@ -1513,40 +1396,6 @@ void CreateApprenticeMon(struct Pokemon *mon, const struct Apprentice *src, u8 m CalculateMonStats(mon); } -void CreateMonWithEVSpreadNatureOTID(struct Pokemon *mon, u16 species, u8 level, u8 nature, u8 fixedIV, u8 evSpread, u32 otId) -{ - s32 i; - s32 statCount = 0; - u8 evsBits; - u16 evAmount; - - // i is reused as personality value - do - { - i = Random32(); - } while (nature != GetNatureFromPersonality(i)); - - CreateMon(mon, species, level, fixedIV, TRUE, i, OT_ID_PRESET, otId); - evsBits = evSpread; - for (i = 0; i < NUM_STATS; i++) - { - if (evsBits & 1) - statCount++; - evsBits >>= 1; - } - - evAmount = MAX_TOTAL_EVS / statCount; - evsBits = 1; - for (i = 0; i < NUM_STATS; i++) - { - if (evSpread & evsBits) - SetMonData(mon, MON_DATA_HP_EV + i, &evAmount); - evsBits <<= 1; - } - - CalculateMonStats(mon); -} - void ConvertPokemonToBattleTowerPokemon(struct Pokemon *mon, struct BattleTowerPokemon *dest) { s32 i; @@ -1584,11 +1433,11 @@ void ConvertPokemonToBattleTowerPokemon(struct Pokemon *mon, struct BattleTowerP GetMonData(mon, MON_DATA_NICKNAME10, dest->nickname); } -static void CreateEventMon(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId) +static void CreateEventMon(struct Pokemon *mon, u16 species, u8 level, u32 personality, struct OriginalTrainerId otId) { bool32 isModernFatefulEncounter = TRUE; - CreateMon(mon, species, level, fixedIV, hasFixedPersonality, fixedPersonality, otIdType, fixedOtId); + CreateMon(mon, species, level, personality, otId); SetMonData(mon, MON_DATA_MODERN_FATEFUL_ENCOUNTER, &isModernFatefulEncounter); } @@ -1629,7 +1478,10 @@ void CreateEnemyEventMon(void) s32 itemId = gSpecialVar_0x8006; ZeroEnemyPartyMons(); - CreateEventMon(&gEnemyParty[0], species, level, USE_RANDOM_IVS, FALSE, 0, OT_ID_PLAYER_ID, 0); + + CreateEventMon(&gEnemyParty[0], species, level, Random32(), OTID_STRUCT_PLAYER_ID); + SetBoxMonIVs(&gEnemyParty[0].box, USE_RANDOM_IVS); + GiveMonInitialMoveset(&gEnemyParty[0]); if (itemId) { u8 heldItem[2]; @@ -1914,6 +1766,44 @@ void GiveBoxMonInitialMoveset(struct BoxPokemon *boxMon) //Credit: AsparagusEdua } } +void GiveMonDefaultMove(struct Pokemon *mon, u32 slot) +{ + GiveBoxMonDefaultMove(&mon->box, slot); +} + +void GiveBoxMonDefaultMove(struct BoxPokemon *boxMon, u32 slot) +{ + enum Move move = MOVE_NONE; + u32 species = GetBoxMonData(boxMon, MON_DATA_SPECIES); + const struct LevelUpMove *learnset = GetSpeciesLevelUpLearnset(species); + s32 level = GetLevelFromBoxMonExp(boxMon); + for (u32 i = 0; learnset[i].move != LEVEL_UP_MOVE_END; i++) + { + s32 j; + bool32 alreadyKnown = FALSE; + + if (learnset[i].level > level) + break; + if (learnset[i].level == 0) + continue; + + for (j = 0; j < slot; j++) + { + if (GetBoxMonData(boxMon, MON_DATA_MOVE1 + j) == learnset[i].move) + { + alreadyKnown = TRUE; + break; + } + } + if (!alreadyKnown) + move = learnset[i].move; + } + + SetBoxMonData(boxMon, MON_DATA_MOVE1 + slot, &move); + u32 pp = GetMovePP(move); + SetBoxMonData(boxMon, MON_DATA_PP1 + slot, &pp); +} + enum Move MonTryLearningNewMoveAtLevel(struct Pokemon *mon, bool32 firstMove, u32 level) { enum Move retVal = MOVE_NONE; @@ -3232,7 +3122,7 @@ void CopyMon(void *dest, void *src, size_t size) memcpy(dest, src, size); } -u8 GiveMonToPlayer(struct Pokemon *mon) +u8 GiveCapturedMonToPlayer(struct Pokemon *mon) { s32 i; @@ -3421,15 +3311,12 @@ void CreateSecretBaseEnemyParty(struct SecretBase *secretBaseRecord) { if (gBattleResources->secretBase->party.species[i]) { - CreateMon(&gEnemyParty[i], + CreateMonWithIVs(&gEnemyParty[i], gBattleResources->secretBase->party.species[i], gBattleResources->secretBase->party.levels[i], - 15, - TRUE, gBattleResources->secretBase->party.personality[i], - OT_ID_RANDOM_NO_SHINY, - 0); - + OTID_STRUCT_RANDOM_NO_SHINY, + 15); SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gBattleResources->secretBase->party.heldItems[i]); for (j = 0; j < NUM_STATS; j++) @@ -6296,11 +6183,7 @@ bool8 IsTradedMon(struct Pokemon *mon) bool8 IsOtherTrainer(u32 otId, u8 *otName) { - if (otId == - (gSaveBlock2Ptr->playerTrainerId[0] - | (gSaveBlock2Ptr->playerTrainerId[1] << 8) - | (gSaveBlock2Ptr->playerTrainerId[2] << 16) - | (gSaveBlock2Ptr->playerTrainerId[3] << 24))) + if (otId == READ_OTID_FROM_SAVE) { int i; for (i = 0; otName[i] != EOS; i++) @@ -7518,3 +7401,38 @@ bool32 IsSpeciesOfType(u32 species, enum Type type) return TRUE; return FALSE; } + +u32 GiveScriptedMonToPlayer(struct Pokemon *mon, u8 slot) +{ + u32 sentToPc; + u32 i = 0; + if (slot < PARTY_SIZE) + { + CopyMon(&gPlayerParty[slot], mon, sizeof(struct Pokemon)); + sentToPc = MON_GIVEN_TO_PARTY; + } + else + { + for (i = 0; i < PARTY_SIZE; i++) + { + if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL) == SPECIES_NONE) + break; + } + if (i >= PARTY_SIZE) + { + sentToPc = CopyMonToPC(mon); + } + else + { + sentToPc = MON_GIVEN_TO_PARTY; + CopyMon(&gPlayerParty[i], mon, sizeof(struct Pokemon)); + gPlayerPartyCount = i + 1; + } + } + if (sentToPc != MON_CANT_GIVE) + { + HandleSetPokedexFlagFromMon(mon, FLAG_SET_SEEN); + HandleSetPokedexFlagFromMon(mon, FLAG_SET_CAUGHT); + } + return sentToPc; +} diff --git a/src/pokemon_storage_system.c b/src/pokemon_storage_system.c index a7cc1e348a..4b92c7aab7 100644 --- a/src/pokemon_storage_system.c +++ b/src/pokemon_storage_system.c @@ -9599,19 +9599,6 @@ void CopyBoxMonAt(u8 boxId, u8 boxPosition, struct BoxPokemon *dst) *dst = gPokemonStoragePtr->boxes[boxId][boxPosition]; } -void CreateBoxMonAt(u8 boxId, u8 boxPosition, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 personality, u8 otIDType, u32 otID) -{ - if (boxId < TOTAL_BOXES_COUNT && boxPosition < IN_BOX_COUNT) - { - CreateBoxMon(&gPokemonStoragePtr->boxes[boxId][boxPosition], - species, - level, - fixedIV, - hasFixedPersonality, personality, - otIDType, otID); - } -} - void ZeroBoxMonAt(u8 boxId, u8 boxPosition) { if (boxId < TOTAL_BOXES_COUNT && boxPosition < IN_BOX_COUNT) diff --git a/src/recorded_battle.c b/src/recorded_battle.c index 1b08f73532..e5739b9ac3 100644 --- a/src/recorded_battle.c +++ b/src/recorded_battle.c @@ -140,10 +140,7 @@ void RecordedBattle_SetTrainerInfo(void) else { // Local battle, just record own info - sPlayers[0].trainerId = (gSaveBlock2Ptr->playerTrainerId[0]) - | (gSaveBlock2Ptr->playerTrainerId[1] << 8) - | (gSaveBlock2Ptr->playerTrainerId[2] << 16) - | (gSaveBlock2Ptr->playerTrainerId[3] << 24); + sPlayers[0].trainerId = READ_OTID_FROM_SAVE; sPlayers[0].gender = gSaveBlock2Ptr->playerGender; sPlayers[0].battler = 0; diff --git a/src/roamer.c b/src/roamer.c index 0fc86b6711..2dce1dc5ed 100644 --- a/src/roamer.c +++ b/src/roamer.c @@ -1,5 +1,6 @@ #include "global.h" #include "event_data.h" +#include "ow_synchronize.h" #include "pokemon.h" #include "random.h" #include "roamer.h" @@ -100,7 +101,12 @@ void MoveAllRoamers(void) static void CreateInitialRoamerMon(u8 index, u16 species, u8 level) { ClearRoamerLocationHistory(index); - CreateMon(&gEnemyParty[0], species, level, USE_RANDOM_IVS, FALSE, 0, OT_ID_PLAYER_ID, 0); + u32 personality = GetMonPersonality(species, + GetSynchronizedGender(ROAMER_ORIGIN, species), + GetSynchronizedNature(ROAMER_ORIGIN, species), + RANDOM_UNOWN_LETTER); + CreateMonWithIVs(&gEnemyParty[0], species, level, personality, OTID_STRUCT_PLAYER_ID, USE_RANDOM_IVS); + GiveMonInitialMoveset(&gEnemyParty[0]); ROAMER(index)->ivs = GetMonData(&gEnemyParty[0], MON_DATA_IVS); ROAMER(index)->personality = GetMonData(&gEnemyParty[0], MON_DATA_PERSONALITY); ROAMER(index)->species = species; diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index 9283bb9b4d..1320f16bc7 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -2,6 +2,7 @@ #include "battle.h" #include "battle_gfx_sfx_util.h" #include "berry.h" +#include "caps.h" #include "data.h" #include "daycare.h" #include "decompress.h" @@ -13,6 +14,7 @@ #include "main.h" #include "menu.h" #include "overworld.h" +#include "ow_synchronize.h" #include "palette.h" #include "party_menu.h" #include "pokedex.h" @@ -70,7 +72,7 @@ u8 ScriptGiveEgg(u16 species) isEgg = TRUE; SetMonData(&mon, MON_DATA_IS_EGG, &isEgg); - return GiveMonToPlayer(&mon); + return GiveCapturedMonToPlayer(&mon); } void HasEnoughMonsForDoubleBattle(void) @@ -116,10 +118,12 @@ void CreateScriptedWildMon(u16 species, u8 level, u16 item) u8 heldItem[2]; ZeroEnemyPartyMons(); - if (OW_SYNCHRONIZE_NATURE > GEN_3) - CreateMonWithNature(&gEnemyParty[0], species, level, USE_RANDOM_IVS, PickWildMonNature()); - else - CreateMon(&gEnemyParty[0], species, level, USE_RANDOM_IVS, 0, 0, OT_ID_PLAYER_ID, 0); + 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]); if (item) { heldItem[0] = item; @@ -133,11 +137,12 @@ void CreateScriptedDoubleWildMon(u16 species1, u8 level1, u16 item1, u16 species u8 heldItem2[2]; ZeroEnemyPartyMons(); - - if (OW_SYNCHRONIZE_NATURE > GEN_3) - CreateMonWithNature(&gEnemyParty[0], species1, level1, 32, PickWildMonNature()); - else - CreateMon(&gEnemyParty[0], species1, level1, 32, 0, 0, OT_ID_PLAYER_ID, 0); + 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; @@ -145,10 +150,12 @@ void CreateScriptedDoubleWildMon(u16 species1, u8 level1, u16 item1, u16 species SetMonData(&gEnemyParty[0], MON_DATA_HELD_ITEM, heldItem1); } - if (OW_SYNCHRONIZE_NATURE > GEN_3) - CreateMonWithNature(&gEnemyParty[1], species2, level2, 32, PickWildMonNature()); - else - CreateMon(&gEnemyParty[1], species2, level2, 32, 0, 0, OT_ID_PLAYER_ID, 0); + 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; @@ -351,32 +358,15 @@ void SetTeraType(struct ScriptContext *ctx) * if side/slot are assigned, it will create the mon at the assigned party location * if slot == PARTY_SIZE, it will give the mon to first available party or storage slot */ -static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u16 item, enum PokeBall ball, u8 nature, u8 abilityNum, u8 gender, u8 *evs, u8 *ivs, u16 *moves, enum ShinyMode shinyMode, bool8 gmaxFactor, enum Type teraType, u8 dmaxLevel) +static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u16 item, enum PokeBall ball, u8 nature, u8 abilityNum, u8 gender, u16 *evs, u16 *ivs, u16 *moves, enum ShinyMode shinyMode, bool8 gmaxFactor, enum Type teraType, u8 dmaxLevel) { - enum NationalDexOrder nationalDexNum; - int sentToPc; struct Pokemon mon; u32 i; u16 targetSpecies; bool32 isShiny; - // check whether to use a specific nature or a random one - if (nature >= NUM_NATURES) - { - if (OW_SYNCHRONIZE_NATURE >= GEN_6 - && (gSpeciesInfo[species].eggGroups[0] == EGG_GROUP_NO_EGGS_DISCOVERED || OW_SYNCHRONIZE_NATURE == GEN_7)) - nature = PickWildMonNature(); - else - nature = Random() % NUM_NATURES; - } - - // create a Pokémon with basic data - // TODO: Use another value for "any gender" so that we can report an - // error if genderless. - if (gender != MON_GENDERLESS) - CreateMonWithGenderNatureLetter(&mon, species, level, 32, gender, nature, 0); - else - CreateMonWithNature(&mon, species, level, 32, nature); + u32 personality = GetMonPersonality(species, gender, nature, RANDOM_UNOWN_LETTER); + CreateMon(&mon, species, level, personality, OTID_STRUCT_PLAYER_ID); // shininess if (shinyMode == SHINY_MODE_ALWAYS || (P_FLAG_FORCE_SHINY != 0 && FlagGet(P_FLAG_FORCE_SHINY))) @@ -415,25 +405,36 @@ static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u // moves for (i = 0; i < MAX_MON_MOVES; i++) { - if (moves[0] == MOVE_NONE) + if (moves[i] == MOVE_NONE) break; - if (moves[i] >= MOVES_COUNT) + if (moves[i] < MOVES_COUNT) + { + SetMonMoveSlot(&mon, moves[i], i); + } + else if (moves[i] == MOVE_DEFAULT) + { + GiveMonDefaultMove(&mon, slot); continue; - SetMonMoveSlot(&mon, moves[i], i); + } + else + { + assertf(FALSE, "invalid move: %d", moves[i]) {} + } } // ability - if (abilityNum == NUM_ABILITY_PERSONALITY) + if (abilityNum != NUM_ABILITY_PERSONALITY) { - abilityNum = GetMonData(&mon, MON_DATA_PERSONALITY) & 1; + assertf(abilityNum < NUM_ABILITY_SLOTS && GetAbilityBySpecies(species, abilityNum) != ABILITY_NONE, + "invalid ability num %d for species %d", abilityNum, species) + { + // If the ability num is invalid, we loop to find a valid one + do { + abilityNum = Random() % NUM_ABILITY_SLOTS; // includes hidden abilities + } while (GetAbilityBySpecies(species, abilityNum) == ABILITY_NONE); + } + SetMonData(&mon, MON_DATA_ABILITY_NUM, &abilityNum); } - else if (abilityNum > NUM_NORMAL_ABILITY_SLOTS || GetAbilityBySpecies(species, abilityNum) == ABILITY_NONE) - { - do { - abilityNum = Random() % NUM_ABILITY_SLOTS; // includes hidden abilities - } while (GetAbilityBySpecies(species, abilityNum) == ABILITY_NONE); - } - SetMonData(&mon, MON_DATA_ABILITY_NUM, &abilityNum); // ball if (ball > POKEBALL_COUNT) @@ -448,72 +449,53 @@ static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u if (targetSpecies != SPECIES_NONE) SetMonData(&mon, MON_DATA_SPECIES, &targetSpecies); - // assign OT name and gender - SetMonData(&mon, MON_DATA_OT_NAME, gSaveBlock2Ptr->playerName); - SetMonData(&mon, MON_DATA_OT_GENDER, &gSaveBlock2Ptr->playerGender); - if (side == B_SIDE_PLAYER) - { - if (slot < PARTY_SIZE) - { - CopyMon(&gPlayerParty[slot], &mon, sizeof(struct Pokemon)); - sentToPc = MON_GIVEN_TO_PARTY; - } - else - { - // find empty party slot to decide whether the Pokémon goes to the Player's party or the storage system. - for (i = 0; i < PARTY_SIZE; i++) - { - if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL) == SPECIES_NONE) - break; - } - if (i >= PARTY_SIZE) - { - sentToPc = CopyMonToPC(&mon); - } - else - { - sentToPc = MON_GIVEN_TO_PARTY; - CopyMon(&gPlayerParty[i], &mon, sizeof(mon)); - gPlayerPartyCount = i + 1; - } - } + return GiveScriptedMonToPlayer(&mon, slot); - // set pokédex flags - nationalDexNum = SpeciesToNationalPokedexNum(species); - if (sentToPc != MON_CANT_GIVE) - { - GetSetPokedexFlag(nationalDexNum, FLAG_SET_SEEN); - GetSetPokedexFlag(nationalDexNum, FLAG_SET_CAUGHT); - } - } - else + assertf(slot < PARTY_SIZE, "invalid slot: %d", slot) { - assertf(slot < PARTY_SIZE, "invalid slot: %d", slot) - { - return MON_CANT_GIVE; - } - CopyMon(&gEnemyParty[slot], &mon, sizeof(struct Pokemon)); - sentToPc = MON_GIVEN_TO_PARTY; + return MON_CANT_GIVE; } - - return sentToPc; + CopyMon(&gEnemyParty[slot], &mon, sizeof(struct Pokemon)); + return MON_GIVEN_TO_PARTY; } u32 ScriptGiveMon(u16 species, u8 level, u16 item) { - u8 evs[NUM_STATS] = {0, 0, 0, 0, 0, 0}; - u8 ivs[NUM_STATS] = {MAX_PER_STAT_IVS + 1, MAX_PER_STAT_IVS + 1, MAX_PER_STAT_IVS + 1, // We pass "MAX_PER_STAT_IVS + 1" here to ensure that - MAX_PER_STAT_IVS + 1, MAX_PER_STAT_IVS + 1, MAX_PER_STAT_IVS + 1}; // ScriptGiveMonParameterized won't touch the stats' IV. - enum Move moves[MAX_MON_MOVES] = {MOVE_NONE, MOVE_NONE, MOVE_NONE, MOVE_NONE}; + struct Pokemon mon; + u8 heldItem[2]; - return ScriptGiveMonParameterized(0, PARTY_SIZE, species, level, item, ITEM_POKE_BALL, NUM_NATURES, NUM_ABILITY_PERSONALITY, MON_GENDERLESS, evs, ivs, moves, SHINY_MODE_RANDOM, FALSE, NUMBER_OF_MON_TYPES, 0); + CreateRandomMon(&mon, species, level); + if (item) + { + heldItem[0] = item; + heldItem[1] = item >> 8; + SetMonData(&mon, MON_DATA_HELD_ITEM, heldItem); + } + + return GiveScriptedMonToPlayer(&mon, PARTY_SIZE); } #define PARSE_FLAG(n, default_) (flags & (1 << (n))) ? VarGet(ScriptReadHalfword(ctx)) : (default_) +#define ADD_MOVE_IF_NOT_DEFAULT(i, move) \ + if (move && move != MOVE_DEFAULT) \ + { \ + moves[i] = move; \ + i++; \ + } + +#define ADD_MOVE_IF_DEFAULT(i, move) \ + if (moves[i] == MOVE_NONE && move == MOVE_DEFAULT) \ + { \ + moves[i] = MOVE_DEFAULT; \ + i++; \ + } + /* Give or create a mon to either player or opponent */ + + void ScrCmd_createmon(struct ScriptContext *ctx) { u8 side = ScriptReadByte(ctx); @@ -524,76 +506,102 @@ void ScrCmd_createmon(struct ScriptContext *ctx) u32 flags = ScriptReadWord(ctx); u16 item = PARSE_FLAG(0, ITEM_NONE); u8 ball = PARSE_FLAG(1, ITEM_POKE_BALL); - u8 nature = PARSE_FLAG(2, NUM_NATURES); + u8 nature = PARSE_FLAG(2, NATURE_RANDOM); u8 abilityNum = PARSE_FLAG(3, NUM_ABILITY_PERSONALITY); - u8 gender = PARSE_FLAG(4, MON_GENDERLESS); // TODO: Find a better way to assign a random gender. - u8 hpEv = PARSE_FLAG(5, 0); - u8 atkEv = PARSE_FLAG(6, 0); - u8 defEv = PARSE_FLAG(7, 0); - u8 speedEv = PARSE_FLAG(8, 0); - u8 spAtkEv = PARSE_FLAG(9, 0); - u8 spDefEv = PARSE_FLAG(10, 0); - u8 hpIv = Random() % (MAX_PER_STAT_IVS + 1); - u8 atkIv = Random() % (MAX_PER_STAT_IVS + 1); - u8 defIv = Random() % (MAX_PER_STAT_IVS + 1); - u8 speedIv = Random() % (MAX_PER_STAT_IVS + 1); - u8 spAtkIv = Random() % (MAX_PER_STAT_IVS + 1); - u8 spDefIv = Random() % (MAX_PER_STAT_IVS + 1); + u8 gender = PARSE_FLAG(4, MON_GENDER_RANDOM); - // Perfect IV calculation u32 i; + u16 evs[NUM_STATS]; + u32 evTotal = 0; + u32 evCap = GetCurrentEVCap(); + for (i = 0; i < NUM_STATS; i++) + { + evs[i] = PARSE_FLAG(5 + i, 0); + assertf(evs[i] <= MAX_PER_STAT_EVS, "invalid ev value of %d above maximum of %d", evs[i], MAX_PER_STAT_EVS) + { + evs[i] = 0; + } + evTotal += evs[i]; + } + assertf(evTotal <= evCap, "total ev count of %d above maximum of %d", evTotal, evCap) + { + for (i = 0; i < NUM_STATS; i++) + evs[i] = 0; + } + + u16 ivs[NUM_STATS]; + u32 nonFixedIvCount = 0; enum Stat availableIVs[NUM_STATS]; enum Stat selectedIvs[NUM_STATS]; + for (i = 0; i < NUM_STATS; i++) + { + ivs[i] = PARSE_FLAG(11 + i, USE_RANDOM_IVS); + if (ivs[i] > USE_RANDOM_IVS) + errorf("invalid iv value of %d above maximum of %d", ivs[i], MAX_PER_STAT_IVS); + if (ivs[i] == USE_RANDOM_IVS) + { + availableIVs[nonFixedIvCount] = i; + ivs[i] = Random() % (MAX_PER_STAT_IVS + 1); + nonFixedIvCount++; + } + } + + // Perfect IV calculation if (gSpeciesInfo[species].perfectIVCount != 0) { - // Initialize a list of IV indices. - for (i = 0; i < NUM_STATS; i++) - availableIVs[i] = i; - // Select the IVs that will be perfected. - for (i = 0; i < NUM_STATS && i < gSpeciesInfo[species].perfectIVCount; i++) + for (i = 0; i < nonFixedIvCount && i < gSpeciesInfo[species].perfectIVCount; i++) { - u8 index = Random() % (NUM_STATS - i); + u8 index = Random() % (nonFixedIvCount - i); selectedIvs[i] = availableIVs[index]; RemoveIVIndexFromList(availableIVs, index); } - for (i = 0; i < NUM_STATS && i < gSpeciesInfo[species].perfectIVCount; i++) + for (i = 0; i < nonFixedIvCount && i < gSpeciesInfo[species].perfectIVCount; i++) { - switch (selectedIvs[i]) - { - case STAT_HP: hpIv = MAX_PER_STAT_IVS; break; - case STAT_ATK: atkIv = MAX_PER_STAT_IVS; break; - case STAT_DEF: defIv = MAX_PER_STAT_IVS; break; - case STAT_SPEED: speedIv = MAX_PER_STAT_IVS; break; - case STAT_SPATK: spAtkIv = MAX_PER_STAT_IVS; break; - case STAT_SPDEF: spDefIv = MAX_PER_STAT_IVS; break; - default: break; - } + ivs[selectedIvs[i]] = MAX_PER_STAT_IVS; } } - hpIv = PARSE_FLAG(11, hpIv); - atkIv = PARSE_FLAG(12, atkIv); - defIv = PARSE_FLAG(13, defIv); - speedIv = PARSE_FLAG(14, speedIv); - spAtkIv = PARSE_FLAG(15, spAtkIv); - spDefIv = PARSE_FLAG(16, spDefIv); - enum Move move1 = PARSE_FLAG(17, MOVE_NONE); - enum Move move2 = PARSE_FLAG(18, MOVE_NONE); - enum Move move3 = PARSE_FLAG(19, MOVE_NONE); - enum Move move4 = PARSE_FLAG(20, MOVE_NONE); + + enum Move move1 = PARSE_FLAG(17, MOVE_DEFAULT); + enum Move move2 = PARSE_FLAG(18, MOVE_DEFAULT); + enum Move move3 = PARSE_FLAG(19, MOVE_DEFAULT); + enum Move move4 = PARSE_FLAG(20, MOVE_DEFAULT); enum ShinyMode shinyMode = PARSE_FLAG(21, SHINY_MODE_RANDOM); bool8 gmaxFactor = PARSE_FLAG(22, FALSE); enum Type teraType = PARSE_FLAG(23, NUMBER_OF_MON_TYPES); u8 dmaxLevel = PARSE_FLAG(24, 0); - u8 evs[NUM_STATS] = {hpEv, atkEv, defEv, speedEv, spAtkEv, spDefEv}; - u8 ivs[NUM_STATS] = {hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv}; - enum Move moves[MAX_MON_MOVES] = {move1, move2, move3, move4}; + enum Move moves[MAX_MON_MOVES]; + for (i = 0; i < MAX_MON_MOVES; i++) + moves[i] = MOVE_NONE; + i = 0; + //Reorder moves to put non-default moves first, default moves second and empty moves last + ADD_MOVE_IF_NOT_DEFAULT(i, move1) + ADD_MOVE_IF_NOT_DEFAULT(i, move2) + ADD_MOVE_IF_NOT_DEFAULT(i, move3) + ADD_MOVE_IF_NOT_DEFAULT(i, move4) + ADD_MOVE_IF_DEFAULT(i, move1) + ADD_MOVE_IF_DEFAULT(i, move2) + ADD_MOVE_IF_DEFAULT(i, move3) + ADD_MOVE_IF_DEFAULT(i, move4) + + enum GeneratedMonOrigin origin; if (side == 0) + { Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + origin = GIFTMON_ORIGIN; + } else + { Script_RequestEffects(SCREFF_V1); + origin = STATIC_WILDMON_ORIGIN; + } + + if (gender == MON_GENDER_MAY_CUTE_CHARM) + gender = GetSynchronizedGender(origin, species); + if (nature == NATURE_MAY_SYNCHRONIZE) + nature = GetSynchronizedNature(origin, species); gSpecialVar_Result = ScriptGiveMonParameterized(side, slot, species, level, item, ball, nature, abilityNum, gender, evs, ivs, moves, shinyMode, gmaxFactor, teraType, dmaxLevel); } diff --git a/src/trade.c b/src/trade.c index a350a30bba..6e7dcdc421 100644 --- a/src/trade.c +++ b/src/trade.c @@ -465,7 +465,7 @@ static void CB2_CreateTradeMenu(void) gPaletteFade.bufferTransferDisabled = FALSE; for (i = 0; i < PARTY_SIZE; i++) - CreateMon(&gEnemyParty[i], SPECIES_NONE, 0, USE_RANDOM_IVS, FALSE, 0, OT_ID_PLAYER_ID, 0); + CreateRandomMon(&gEnemyParty[i], SPECIES_NONE, 0); PrintTradeMessage(MSG_STANDBY); ShowBg(0); @@ -4565,7 +4565,7 @@ static void CreateInGameTradePokemonInternal(u8 whichPlayerMon, u8 whichInGameTr u8 mailNum; struct Pokemon *pokemon = &gEnemyParty[0]; - CreateMon(pokemon, inGameTrade->species, level, USE_RANDOM_IVS, TRUE, inGameTrade->personality, OT_ID_PRESET, inGameTrade->otId); + CreateMon(pokemon, inGameTrade->species, level, inGameTrade->personality, OTID_STRUCT_PRESET(inGameTrade->otId)); SetMonData(pokemon, MON_DATA_HP_IV, &inGameTrade->ivs[0]); SetMonData(pokemon, MON_DATA_ATK_IV, &inGameTrade->ivs[1]); diff --git a/src/trainer_pools.c b/src/trainer_pools.c index 9603dc1f8c..032edf582a 100644 --- a/src/trainer_pools.c +++ b/src/trainer_pools.c @@ -248,7 +248,7 @@ static u32 GetPoolSeed(const struct Trainer *trainer) if (B_POOL_SETTING_USE_FIXED_SEED) seed = B_POOL_SETTING_FIXED_SEED; else - seed = gSaveBlock2Ptr->playerTrainerId[0] + (gSaveBlock2Ptr->playerTrainerId[1] << 8) + (gSaveBlock2Ptr->playerTrainerId[2] << 16) + (gSaveBlock2Ptr->playerTrainerId[3] << 24); + seed = READ_OTID_FROM_SAVE; seed ^= (u32)trainer; return seed; } diff --git a/src/tv.c b/src/tv.c index 1902f4d559..44c4625397 100644 --- a/src/tv.c +++ b/src/tv.c @@ -3346,7 +3346,7 @@ static u8 GetTVGroupByShowId(u8 kind) u32 GetPlayerIDAsU32(void) { - return (gSaveBlock2Ptr->playerTrainerId[3] << 24) | (gSaveBlock2Ptr->playerTrainerId[2] << 16) | (gSaveBlock2Ptr->playerTrainerId[1] << 8) | gSaveBlock2Ptr->playerTrainerId[0]; + return READ_OTID_FROM_SAVE; } u8 CheckForPlayersHouseNews(void) diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 4c81fa371c..26b796c365 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -11,6 +11,7 @@ #include "link.h" #include "metatile_behavior.h" #include "overworld.h" +#include "ow_synchronize.h" #include "pokeblock.h" #include "pokemon.h" #include "random.h" @@ -443,7 +444,7 @@ enum TimeOfDay GetTimeOfDayForEncounters(u32 headerId, enum WildPokemonArea area return GenConfigTimeOfDay(timeOfDay); } -u8 PickWildMonNature(void) +static u8 PickWildMonNature(u32 species) { u8 i; struct Pokeblock *safariPokeblock; @@ -464,54 +465,18 @@ u8 PickWildMonNature(void) } } } - // check synchronize for a Pokémon with the same ability - if (!GetMonData(&gPlayerParty[0], MON_DATA_SANITY_IS_EGG) - && GetMonAbility(&gPlayerParty[0]) == ABILITY_SYNCHRONIZE - && (OW_SYNCHRONIZE_NATURE >= GEN_8 || Random() % 2 == 0)) - { - return GetMonData(&gPlayerParty[0], MON_DATA_PERSONALITY) % NUM_NATURES; - } - // random nature - return Random() % NUM_NATURES; + return GetSynchronizedNature(WILDMON_ORIGIN, species); } void CreateWildMon(u16 species, u8 level) { - bool32 checkCuteCharm = TRUE; - ZeroEnemyPartyMons(); - - switch (gSpeciesInfo[species].genderRatio) - { - case MON_MALE: - case MON_FEMALE: - case MON_GENDERLESS: - checkCuteCharm = FALSE; - break; - } - - if (checkCuteCharm - && !GetMonData(&gPlayerParty[0], MON_DATA_SANITY_IS_EGG) - && GetMonAbility(&gPlayerParty[0]) == ABILITY_CUTE_CHARM - && Random() % 3 != 0) - { - u16 leadingMonSpecies = GetMonData(&gPlayerParty[0], MON_DATA_SPECIES); - u32 leadingMonPersonality = GetMonData(&gPlayerParty[0], MON_DATA_PERSONALITY); - u8 gender = GetGenderFromSpeciesAndPersonality(leadingMonSpecies, leadingMonPersonality); - - // misses mon is genderless check, although no genderless mon can have cute charm as ability - if (gender == MON_FEMALE) - gender = MON_MALE; - else - gender = MON_FEMALE; - - CreateMonWithGenderNatureLetter(&gEnemyParty[0], species, level, USE_RANDOM_IVS, gender, PickWildMonNature(), 0); - return; - } - - CreateMonWithNature(&gEnemyParty[0], species, level, USE_RANDOM_IVS, PickWildMonNature()); + u32 personality = GetMonPersonality(species, GetSynchronizedGender(WILDMON_ORIGIN, species), PickWildMonNature(species), RANDOM_UNOWN_LETTER); + CreateMonWithIVs(&gEnemyParty[0], species, level, personality, OTID_STRUCT_PLAYER_ID, USE_RANDOM_IVS); + GiveMonInitialMoveset(&gEnemyParty[0]); } + #ifdef BUGFIX #define TRY_GET_ABILITY_INFLUENCED_WILD_MON_INDEX(wildPokemon, type, ability, ptr, count) TryGetAbilityInfluencedWildMonIndex(wildPokemon, type, ability, ptr, count) #else diff --git a/test/pokemon.c b/test/pokemon.c index c902e56e45..1598fb3cf7 100644 --- a/test/pokemon.c +++ b/test/pokemon.c @@ -18,7 +18,9 @@ TEST("Nature independent from Hidden Nature") PARAMETRIZE { nature = i; hiddenNature = j; } } } - CreateMonWithNature(&mon, SPECIES_WOBBUFFET, 100, 0, nature); + u32 species = SPECIES_WOBBUFFET; + u32 personality = GetMonPersonality(species, MON_GENDER_RANDOM, nature, RANDOM_UNOWN_LETTER); + CreateMon(&mon, species, 100, personality, OTID_STRUCT_PLAYER_ID); SetMonData(&mon, MON_DATA_HIDDEN_NATURE, &hiddenNature); EXPECT_EQ(GetNature(&mon), nature); EXPECT_EQ(GetMonData(&mon, MON_DATA_HIDDEN_NATURE), hiddenNature); @@ -30,7 +32,7 @@ TEST("Terastallization type defaults to primary or secondary type") enum Type teraType; struct Pokemon mon; for (i = 0; i < 128; i++) PARAMETRIZE {} - CreateMon(&mon, SPECIES_PIDGEY, 100, 0, FALSE, 0, OT_ID_PRESET, 0); + CreateRandomMonWithIVs(&mon, SPECIES_PIDGEY, 100, 0); teraType = GetMonData(&mon, MON_DATA_TERA_TYPE); EXPECT(teraType == GetSpeciesType(SPECIES_PIDGEY, 0) || teraType == GetSpeciesType(SPECIES_PIDGEY, 1)); @@ -45,7 +47,7 @@ TEST("Terastallization type can be set to any type except TYPE_NONE") { PARAMETRIZE { teraType = i; } } - CreateMon(&mon, SPECIES_WOBBUFFET, 100, 0, FALSE, 0, OT_ID_PRESET, 0); + CreateRandomMonWithIVs(&mon, SPECIES_WOBBUFFET, 100, 0); SetMonData(&mon, MON_DATA_TERA_TYPE, &teraType); EXPECT_EQ(teraType, GetMonData(&mon, MON_DATA_TERA_TYPE)); } @@ -59,7 +61,7 @@ TEST("Terastallization type is reset to the default types when setting Tera Type { PARAMETRIZE { teraType = i; typeNone = TYPE_NONE; } } - CreateMon(&mon, SPECIES_PIDGEY, 100, 0, FALSE, 0, OT_ID_PRESET, 0); + CreateRandomMonWithIVs(&mon, SPECIES_PIDGEY, 100, 0); SetMonData(&mon, MON_DATA_TERA_TYPE, &teraType); EXPECT_EQ(teraType, GetMonData(&mon, MON_DATA_TERA_TYPE)); if (typeNone == TYPE_NONE) @@ -76,7 +78,7 @@ TEST("Shininess independent from PID and OTID") bool32 isShiny; struct Pokemon mon; PARAMETRIZE { pid = 0; otId = 0; } - CreateMon(&mon, SPECIES_WOBBUFFET, 100, 0, TRUE, pid, OT_ID_PRESET, otId); + CreateMon(&mon, SPECIES_WOBBUFFET, 100, pid, OTID_STRUCT_PRESET(otId)); isShiny = IsMonShiny(&mon); data = !isShiny; SetMonData(&mon, MON_DATA_IS_SHINY, &data); @@ -89,8 +91,7 @@ TEST("Hyper Training increases stats without affecting IVs") { u32 data, hp, atk, def, speed, spatk, spdef, friendship = 0; struct Pokemon mon; - CreateMon(&mon, SPECIES_WOBBUFFET, 100, 3, TRUE, 0, OT_ID_PRESET, 0); - + CreateMonWithIVs(&mon, SPECIES_WOBBUFFET, 100, 0, OTID_STRUCT_PRESET(0), 3); // Consider B_FRIENDSHIP_BOOST. SetMonData(&mon, MON_DATA_FRIENDSHIP, &friendship); CalculateMonStats(&mon); @@ -143,7 +144,7 @@ TEST("Status1 round-trips through BoxPokemon") PARAMETRIZE { status1 = STATUS1_PARALYSIS; } PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; } PARAMETRIZE { status1 = STATUS1_FROSTBITE; } - CreateMon(&mon1, SPECIES_WOBBUFFET, 100, 0, FALSE, 0, OT_ID_PRESET, 0); + CreateRandomMonWithIVs(&mon1, SPECIES_WOBBUFFET, 100, 0); SetMonData(&mon1, MON_DATA_STATUS, &status1); BoxMonToMon(&mon1.box, &mon2); EXPECT_EQ(GetMonData(&mon2, MON_DATA_STATUS), status1); @@ -152,7 +153,7 @@ TEST("Status1 round-trips through BoxPokemon") TEST("canhypertrain/hypertrain affect MON_DATA_HYPER_TRAINED_* and recalculate stats") { u32 atk, friendship = 0; - CreateMon(&gPlayerParty[0], SPECIES_WOBBUFFET, 100, 0, FALSE, 0, OT_ID_PRESET, 0); + CreateRandomMonWithIVs(&gPlayerParty[0], SPECIES_WOBBUFFET, 100, 0); // Consider B_FRIENDSHIP_BOOST. SetMonData(&gPlayerParty[0], MON_DATA_FRIENDSHIP, &friendship); @@ -176,7 +177,7 @@ TEST("canhypertrain/hypertrain affect MON_DATA_HYPER_TRAINED_* and recalculate s TEST("hasgigantamaxfactor/togglegigantamaxfactor affect MON_DATA_GIGANTAMAX_FACTOR") { - CreateMon(&gPlayerParty[0], SPECIES_WOBBUFFET, 100, 0, FALSE, 0, OT_ID_PRESET, 0); + CreateRandomMonWithIVs(&gPlayerParty[0], SPECIES_WOBBUFFET, 100, 0); RUN_OVERWORLD_SCRIPT( hasgigantamaxfactor 0; @@ -200,7 +201,7 @@ TEST("hasgigantamaxfactor/togglegigantamaxfactor affect MON_DATA_GIGANTAMAX_FACT TEST("togglegigantamaxfactor fails for Melmetal") { - CreateMon(&gPlayerParty[0], SPECIES_MELMETAL, 100, 0, FALSE, 0, OT_ID_PRESET, 0); + CreateRandomMonWithIVs(&gPlayerParty[0], SPECIES_MELMETAL, 100, 0); RUN_OVERWORLD_SCRIPT( hasgigantamaxfactor 0; @@ -270,6 +271,40 @@ TEST("givemon respects perfectIVCount") } } +TEST("givemon respects perfectIVCount but does overwrite fixed IVs (1)") +{ + ZeroPlayerPartyMons(); + + ASSUME(gSpeciesInfo[SPECIES_MEW].perfectIVCount == 3); + RUN_OVERWORLD_SCRIPT( + givemon SPECIES_MEW, 100, hpIv=7, atkIv=8, defIv=9, speedIv=10, spAtkIv=11, spDefIv=12 + ); + + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_HP_IV), 7); + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_ATK_IV), 8); + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_DEF_IV), 9); + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPEED_IV), 10); + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPATK_IV), 11); + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPDEF_IV), 12); +} + +TEST("givemon respects perfectIVCount but does overwrite fixed IVs (2)") +{ + ZeroPlayerPartyMons(); + + ASSUME(gSpeciesInfo[SPECIES_MEW].perfectIVCount == 3); + RUN_OVERWORLD_SCRIPT( + givemon SPECIES_MEW, 100, hpIv=7, atkIv=8, defIv=9 + ); + + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_HP_IV), 7); + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_ATK_IV), 8); + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_DEF_IV), 9); + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPEED_IV), MAX_PER_STAT_IVS); + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPATK_IV), MAX_PER_STAT_IVS); + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPDEF_IV), MAX_PER_STAT_IVS); +} + TEST("givemon respects FORM_CHANGE_ITEM_HOLD") { ZeroPlayerPartyMons(); @@ -407,7 +442,7 @@ TEST("givemon [vars]") TEST("checkteratype/setteratype work") { - CreateMon(&gPlayerParty[0], SPECIES_WOBBUFFET, 100, 0, FALSE, 0, OT_ID_PRESET, 0); + CreateRandomMonWithIVs(&gPlayerParty[0], SPECIES_WOBBUFFET, 100, 0); RUN_OVERWORLD_SCRIPT( checkteratype 0; @@ -458,7 +493,7 @@ TEST("Pokémon level up learnsets fit within MAX_LEVEL_UP_MOVES and MAX_RELEARNE TEST("Optimised GetMonData") { - CreateMon(&gPlayerParty[0], SPECIES_WOBBUFFET, 5, 0, FALSE, 0, OT_ID_PRESET, 0x12345678); + CreateMon(&gPlayerParty[0], SPECIES_WOBBUFFET, 5, Random32(), OTID_STRUCT_PRESET(0x12345678)); u32 exp = 0x123456; SetMonData(&gPlayerParty[0], MON_DATA_EXP, &exp); struct Benchmark optimised, @@ -471,7 +506,7 @@ TEST("Optimised GetMonData") TEST("Optimised SetMonData") { - CreateMon(&gPlayerParty[0], SPECIES_WOBBUFFET, 5, 0, FALSE, 0, OT_ID_PRESET, 0x12345678); + CreateMon(&gPlayerParty[0], SPECIES_WOBBUFFET, 5, Random32(), OTID_STRUCT_PRESET(0x12345678)); u32 exp = 0x123456; struct Benchmark optimised, vanilla = (struct Benchmark) { .ticks = 205 }; // From prior testing diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 6f3302e7d1..3ca2ddbe02 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1982,17 +1982,18 @@ void OpenPokemon(u32 sourceLine, enum BattlerPosition position, u32 species) DATA.nature = NATURE_HARDY; (*partySize)++; - CreateMon(DATA.currentMon, species, 100, 0, TRUE, 0, OT_ID_PRESET, 0); - data = MOVE_NONE; + CreateMon(DATA.currentMon, species, 100, 0, OTID_STRUCT_PRESET(0)); for (i = 0; i < MAX_MON_MOVES; i++) - SetMonData(DATA.currentMon, MON_DATA_MOVE1 + i, &data); - data = 0; - if (B_FRIENDSHIP_BOOST) { - // This way, we avoid the boost affecting tests unless explicitly stated. - SetMonData(DATA.currentMon, MON_DATA_FRIENDSHIP, &data); - CalculateMonStats(DATA.currentMon); + data = MOVE_NONE; + SetMonData(DATA.currentMon, MON_DATA_MOVE1 + i, &data); + data = 0x7F; // Max PP possible + SetMonData(DATA.currentMon, MON_DATA_PP1 + i, &data); } + data = 0; + if (B_FRIENDSHIP_BOOST) // This way, we avoid the boost affecting tests unless explicitly stated. + SetMonData(DATA.currentMon, MON_DATA_FRIENDSHIP, &data); + CalculateMonStats(DATA.currentMon); } void OpenPokemonMulti(u32 sourceLine, enum BattlerPosition position, u32 species) @@ -2036,7 +2037,8 @@ void OpenPokemonMulti(u32 sourceLine, enum BattlerPosition position, u32 species DATA.isShiny = FALSE; (*partySize)++; - CreateMon(DATA.currentMon, species, 100, 0, TRUE, 0, OT_ID_PRESET, 0); + CreateMon(DATA.currentMon, species, 100, 0, OTID_STRUCT_PRESET(0)); + // Reset move IDs, but force PP to be non-zero. This is a safeguard against test species that only learn 1 move having test moves with 0 PP for (i = 0; i < MAX_MON_MOVES; i++) { @@ -2046,12 +2048,9 @@ void OpenPokemonMulti(u32 sourceLine, enum BattlerPosition position, u32 species SetMonData(DATA.currentMon, MON_DATA_PP1 + i, &data); } data = 0; - if (B_FRIENDSHIP_BOOST) - { - // This way, we avoid the boost affecting tests unless explicitly stated. + if (B_FRIENDSHIP_BOOST) // This way, we avoid the boost affecting tests unless explicitly stated. SetMonData(DATA.currentMon, MON_DATA_FRIENDSHIP, &data); - CalculateMonStats(DATA.currentMon); - } + CalculateMonStats(DATA.currentMon); } // (sNaturePersonalities[i] % NUM_NATURES) == i