From 3ad66028e9291ab3dc91340ea40758c32a9df949 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Wed, 27 Dec 2023 16:48:17 +0000 Subject: [PATCH] =?UTF-8?q?Backwards-compatible=20BoxPok=C3=A9mon=20Refact?= =?UTF-8?q?or=20(#3438)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Check progress in non-battle PARAMETRIZEd tests * Overworld Script Tests * Backward-compatible BoxPokemon Refactor Reuses space that contains zeros to provide space for: - HP/status in the box - 12-character nicknames - Up to 63 PokéBalls - Shininess separate from PID - Hidden Nature - Hyper Training - Dynamax Level - Gigantamax Factor - Terastallization Types - Shadow Implements: - OW_PC_HEAL to switch between Gen7- and Gen8+ behavior - Nature Mints - Dynamax Candy - Hyper Training commands (canhypertrain/hypertrain) - Gigantamax Factor commands (hasgigantamaxfactor/togglegigantamaxfactor) - Terastallization Type on the summary screen - Prevents Gigantamax Factor Pokémon from evolving into a species without a Gigantamax form * fixup! Backward-compatible BoxPokemon Refactor * displaydexinfo fix from Jasper --- asm/macros/event.inc | 30 ++ include/battle.h | 4 +- include/battle_anim.h | 2 +- include/config/overworld.h | 1 + include/config/pokemon.h | 1 + include/constants/battle.h | 5 +- include/constants/global.h | 2 + include/constants/moves.h | 2 + include/constants/pokemon.h | 2 + include/contest.h | 6 +- include/data.h | 2 + include/global.h | 7 +- include/item_use.h | 2 + include/party_menu.h | 2 + include/pokedex.h | 2 +- include/pokemon.h | 195 ++++++----- include/test/battle.h | 8 + include/test/overworld_script.h | 48 +++ include/test/test.h | 5 +- include/trainer_pokemon_sprites.h | 4 +- src/battle_anim_effects_1.c | 2 +- src/battle_anim_effects_3.c | 22 +- src/battle_anim_mons.c | 6 +- src/battle_anim_throw.c | 11 +- src/battle_controllers.c | 2 +- src/battle_debug.c | 4 +- src/battle_dynamax.c | 10 +- src/battle_factory_screen.c | 33 +- src/battle_gfx_sfx_util.c | 26 +- src/battle_main.c | 17 +- src/battle_script_commands.c | 7 +- src/battle_tower.c | 10 +- src/battle_util.c | 5 +- src/contest.c | 11 +- src/contest_painting.c | 2 +- src/contest_util.c | 13 +- src/data/items.h | 109 ++++--- src/daycare.c | 1 - src/debug.c | 24 +- src/evolution_scene.c | 31 +- src/field_effect.c | 16 +- src/frontier_util.c | 7 +- src/hall_of_fame.c | 10 +- src/item_use.c | 12 + src/load_save.c | 10 + src/main_menu.c | 2 +- src/menu_specialized.c | 4 +- src/party_menu.c | 162 +++++++++ src/pokeblock_feed.c | 7 +- src/pokedex.c | 18 +- src/pokemon.c | 523 +++++++++++++++++++++++------- src/pokemon_jump.c | 11 +- src/pokemon_storage_system.c | 18 +- src/pokemon_summary_screen.c | 13 +- src/pokenav_conditions.c | 7 +- src/pokenav_ribbons_summary.c | 18 +- src/script_pokemon_util.c | 109 +++++-- src/starter_choose.c | 2 +- src/trade.c | 2 +- src/trainer_pokemon_sprites.c | 44 +-- test/battle/trainer_control.c | 6 +- test/dynamax.c | 51 +-- test/pokemon.c | 182 +++++++++++ test/species.c | 2 + test/test_runner.c | 10 + test/test_runner_battle.c | 29 +- 66 files changed, 1437 insertions(+), 514 deletions(-) create mode 100644 include/test/overworld_script.h create mode 100644 test/pokemon.c diff --git a/asm/macros/event.inc b/asm/macros/event.inc index d2d8f8ca6f..62743b6f7c 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2025,3 +2025,33 @@ callnative CreateTrainerPartyForPlayer trainerbattle_no_intro \trainer2, NULL .endm + + @ Sets VAR_RESULT to TRUE if stat can be hyper trained, or to + @ FALSE otherwise. + .macro canhypertrain stat:req, slot:req + callnative CanHyperTrain + .byte \stat + .2byte \slot + .endm + + @ Hyper Trains a stat. + .macro hypertrain stat:req, slot:req + callnative HyperTrain + .byte \stat + .2byte \slot + .endm + + @ Sets VAR_RESULT to TRUE if the Pokemon has the Gigantamax Factor, + @ or to FALSE otherwise. + .macro hasgigantamaxfactor slot:req + callnative HasGigantamaxFactor + .2byte \slot + .endm + + @ Toggles the Gigantamax Factor for a Pokemon. + @ Fails for Melmetal (vanilla behavior). + @ Sets VAR_RESULT to TRUE if it succeeds, and FALSE otherwise. + .macro togglegigantamaxfactor slot:req + callnative ToggleGigantamaxFactor + .2byte \slot + .endm diff --git a/include/battle.h b/include/battle.h index 3fe22a4377..ecf8250774 100644 --- a/include/battle.h +++ b/include/battle.h @@ -65,7 +65,7 @@ struct ResourceFlags struct DisableStruct { u32 transformedMonPersonality; - u32 transformedMonOtId; + bool8 transformedMonShininess; u16 disabledMove; u16 encoredMove; u8 protectUses:4; @@ -1066,7 +1066,7 @@ extern u8 gBattlerStatusSummaryTaskId[MAX_BATTLERS_COUNT]; extern u8 gBattlerInMenuId; extern bool8 gDoingBattleAnim; extern u32 gTransformedPersonalities[MAX_BATTLERS_COUNT]; -extern u32 gTransformedOtIds[MAX_BATTLERS_COUNT]; +extern bool8 gTransformedShininess[MAX_BATTLERS_COUNT]; extern u8 gPlayerDpadHoldFrames; extern struct BattleSpriteData *gBattleSpritesDataPtr; extern struct MonSpritesGfx *gMonSpritesGfxPtr; diff --git a/include/battle_anim.h b/include/battle_anim.h index 672b640cc9..c41d94eb09 100644 --- a/include/battle_anim.h +++ b/include/battle_anim.h @@ -159,7 +159,7 @@ void PrepareAffineAnimInTaskData(struct Task *task, u8 spriteId, const union Aff bool8 RunAffineAnimFromTaskData(struct Task *task); void AnimThrowProjectile(struct Sprite *sprite); void GetBgDataForTransform(struct BattleAnimBgData *dest, u8 battlerId); -u8 CreateAdditionalMonSpriteForMoveAnim(u16 species, bool8 isBackpic, u8 id, s16 x, s16 y, u8 subpriority, u32 personality, u32 trainerId, u32 battlerId); +u8 CreateAdditionalMonSpriteForMoveAnim(u16 species, bool8 isBackpic, u8 id, s16 x, s16 y, u8 subpriority, u32 personality, bool8 isShiny, u32 battlerId); void ResetSpriteRotScale_PreserveAffine(struct Sprite *sprite); void Trade_MoveSelectedMonToTarget(struct Sprite *sprite); void DestroyAnimVisualTaskAndDisableBlend(u8 taskId); diff --git a/include/config/overworld.h b/include/config/overworld.h index 3488064421..513eb36723 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -11,6 +11,7 @@ // PC settings #define OW_PC_PRESS_B GEN_LATEST // In Gen4, pressing B when holding a Pokémon is equivalent to placing it. In Gen3, it gives the "You're holding a Pokémon!" error. #define OW_PC_JAPAN_WALDA_ICONS TRUE // In the US release of Emerald, the Cross, Bolt, and Plusle icons for Walda's wallpapers were left blank from the Japan release. Setting this to TRUE will restore them. +#define OW_PC_HEAL GEN_LATEST // In Gen8+, Pokémon are not healed when deposited in the PC. // Out-of-battle Ability effects #define OW_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8, if a Pokémon with Synchronize is leading the party, it's 100% guaranteed that wild Pokémon will have the same Nature, as opposed to 50% previously. Stationary Pokémon are excluded in Gen3. In Gen6, all No Eggs Discovered gift Pokémon will have the same Nature, while in Gen7 all gift Pokémon will, regardless of Egg Group - In Gen 8, no gift Pokémon are affected. In Gen9, this ability has no out-of-battle effect. diff --git a/include/config/pokemon.h b/include/config/pokemon.h index abdb728ac1..58c740f079 100644 --- a/include/config/pokemon.h +++ b/include/config/pokemon.h @@ -32,6 +32,7 @@ #define P_LEGENDARY_PERFECT_IVS GEN_LATEST // Since Gen 6, Legendaries, Mythicals and Ultra Beasts found in the wild or given through gifts have at least 3 perfect IVs. #define P_EV_CAP GEN_LATEST // Since Gen 6, the max EVs per stat is 252 instead of 255. #define P_CATCH_CURVE GEN_LATEST // Since Gen 6, the capture rate curve was changed to make pokeballs more effective on lower level pokemon +#define P_SHOW_TERA_TYPE GEN_LATEST // Since Gen 9, the Tera Type is shown on the summary screen. // Flag settings // To use the following features in scripting, replace the 0s with the flag ID you're assigning it to. diff --git a/include/constants/battle.h b/include/constants/battle.h index 35addb937b..92688026f8 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -104,7 +104,10 @@ #define B_OUTCOME_LINK_BATTLE_RAN (1 << 7) // 128 // Non-volatile status conditions -// These persist remain outside of battle and after switching out +// These remain outside of battle and after switching out. +// If a new STATUS1 is added here, it should also be added to +// sCompressedStatuses in src/pokemon.c or else it will be lost outside +// of battle. #define STATUS1_NONE 0 #define STATUS1_SLEEP (1 << 0 | 1 << 1 | 1 << 2) // First 3 bits (Number of turns to sleep) #define STATUS1_SLEEP_TURN(num) ((num) << 0) // Just for readability (or if rearranging statuses) diff --git a/include/constants/global.h b/include/constants/global.h index 7f4d8ccd3a..f9b7241ef6 100644 --- a/include/constants/global.h +++ b/include/constants/global.h @@ -24,6 +24,8 @@ #define VERSION_PLATINUM 12 #define VERSION_GAMECUBE 15 +#define NUM_VERSIONS 15 + #define LANGUAGE_JAPANESE 1 #define LANGUAGE_ENGLISH 2 #define LANGUAGE_FRENCH 3 diff --git a/include/constants/moves.h b/include/constants/moves.h index a8fcb01654..54b366e18b 100644 --- a/include/constants/moves.h +++ b/include/constants/moves.h @@ -1014,6 +1014,8 @@ #define MOVES_COUNT_DYNAMAX (LAST_MAX_MOVE + 1) +#define MOVES_COUNT_ALL MOVES_COUNT_DYNAMAX + // Used for checks for moves affected by Disable, Mimic, etc. #define MOVE_UNAVAILABLE 0xFFFF diff --git a/include/constants/pokemon.h b/include/constants/pokemon.h index 67053a2819..06da4a179e 100644 --- a/include/constants/pokemon.h +++ b/include/constants/pokemon.h @@ -146,6 +146,8 @@ #define MIN_LEVEL 1 #define MAX_LEVEL 100 +#define MAX_DYNAMAX_LEVEL 10 + #define OT_ID_PLAYER_ID 0 #define OT_ID_PRESET 1 #define OT_ID_RANDOM_NO_SHINY 2 diff --git a/include/contest.h b/include/contest.h index 7a67fecd55..9f2fafaf25 100644 --- a/include/contest.h +++ b/include/contest.h @@ -107,7 +107,9 @@ struct ContestPokemon u8 sheen; u8 highestRank; bool8 gameCleared; - u8 unused[10]; + u8 isShiny:1; + u8 unused1:7; + u8 unused2[9]; u32 personality; u32 otId; }; @@ -125,6 +127,8 @@ struct ContestMoveAnimData u16 species; u16 targetSpecies; bool8 hasTargetAnim:1; + u8 isShiny:1; + u8 targetIsShiny:1; u8 contestant; u32 personality; u32 otId; diff --git a/include/data.h b/include/data.h index a5918fcb98..6351b6eb16 100644 --- a/include/data.h +++ b/include/data.h @@ -66,6 +66,8 @@ struct TrainerMon u8 nature : 5; bool8 gender : 2; bool8 isShiny : 1; + u8 dynamaxLevel : 4; + bool8 gigantamaxFactor : 1; }; #define TRAINER_PARTY(partyArray) partyArray, .partySize = ARRAY_COUNT(partyArray) diff --git a/include/global.h b/include/global.h index 60abf094af..84d5cafa7c 100644 --- a/include/global.h +++ b/include/global.h @@ -129,8 +129,8 @@ #define CAT(a, b) CAT_(a, b) #define CAT_(a, b) a ## b -#define STR(a) STR_(a) -#define STR_(a) #a +#define STR(...) STR_(__VA_ARGS__) +#define STR_(...) #__VA_ARGS__ // Converts a string to a compound literal, essentially making it a pointer to const u8 #define COMPOUND_STRING(str) (const u8[]) _(str) @@ -738,7 +738,8 @@ struct ContestWinner u8 contestCategory; u8 monName[POKEMON_NAME_LENGTH + 1]; u8 trainerName[PLAYER_NAME_LENGTH + 1]; - u8 contestRank; + u8 contestRank:7; + bool8 isShiny:1; //u8 padding; }; diff --git a/include/item_use.h b/include/item_use.h index 3117e603d5..d073e9d3ea 100644 --- a/include/item_use.h +++ b/include/item_use.h @@ -13,12 +13,14 @@ void ItemUseOutOfBattle_WailmerPail(u8); void ItemUseOutOfBattle_Medicine(u8); void ItemUseOutOfBattle_AbilityCapsule(u8); void ItemUseOutOfBattle_AbilityPatch(u8); +void ItemUseOutOfBattle_Mint(u8); void ItemUseOutOfBattle_ResetEVs(u8); void ItemUseOutOfBattle_ReduceEV(u8); void ItemUseOutOfBattle_SacredAsh(u8); void ItemUseOutOfBattle_PPRecovery(u8); void ItemUseOutOfBattle_PPUp(u8); void ItemUseOutOfBattle_RareCandy(u8); +void ItemUseOutOfBattle_DynamaxCandy(u8); void ItemUseOutOfBattle_TMHM(u8); void ItemUseOutOfBattle_Repel(u8); void ItemUseOutOfBattle_Lure(u8); diff --git a/include/party_menu.h b/include/party_menu.h index c50abc276a..1352f54a43 100644 --- a/include/party_menu.h +++ b/include/party_menu.h @@ -53,6 +53,7 @@ void ItemUseCB_BattleChooseMove(u8 taskId, TaskFunc task); void ItemUseCB_Medicine(u8 taskId, TaskFunc task); void ItemUseCB_AbilityCapsule(u8 taskId, TaskFunc task); void ItemUseCB_AbilityPatch(u8 taskId, TaskFunc task); +void ItemUseCB_Mint(u8 taskId, TaskFunc task); void ItemUseCB_ResetEVs(u8 taskId, TaskFunc task); void ItemUseCB_ReduceEV(u8 taskId, TaskFunc task); void ItemUseCB_PPRecovery(u8 taskId, TaskFunc task); @@ -62,6 +63,7 @@ bool8 MonKnowsMove(struct Pokemon *mon, u16 move); bool8 BoxMonKnowsMove(struct BoxPokemon *boxMon, u16 move); void ItemUseCB_TMHM(u8 taskId, TaskFunc task); void ItemUseCB_RareCandy(u8 taskId, TaskFunc task); +void ItemUseCB_DynamaxCandy(u8 taskId, TaskFunc task); void ItemUseCB_SacredAsh(u8 taskId, TaskFunc task); void ItemUseCB_EvolutionStone(u8 taskId, TaskFunc task); void ItemUseCB_FormChange(u8 taskId, TaskFunc task); diff --git a/include/pokedex.h b/include/pokedex.h index 39b45fc5ff..c2169c6994 100644 --- a/include/pokedex.h +++ b/include/pokedex.h @@ -21,7 +21,7 @@ enum void ResetPokedex(void); u16 GetNationalPokedexCount(u8); u16 GetHoennPokedexCount(u8); -u8 DisplayCaughtMonDexPage(u16 species, u32 otId, u32 personality); +u8 DisplayCaughtMonDexPage(u16 species, bool32 isShiny, u32 personality); s8 GetSetPokedexFlag(u16 nationalNum, u8 caseId); u16 CreateMonSpriteFromNationalDexNumber(u16, s16, s16, u16); bool16 HasAllHoennMons(void); diff --git a/include/pokemon.h b/include/pokemon.h index b081a86fef..dda784629a 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -12,8 +12,8 @@ // Property labels for Get(Box)MonData / Set(Box)MonData enum { MON_DATA_PERSONALITY, + MON_DATA_STATUS, MON_DATA_OT_ID, - MON_DATA_NICKNAME, MON_DATA_LANGUAGE, MON_DATA_SANITY_IS_BAD_EGG, MON_DATA_SANITY_HAS_SPECIES, @@ -21,7 +21,12 @@ enum { MON_DATA_OT_NAME, MON_DATA_MARKINGS, MON_DATA_CHECKSUM, + MON_DATA_HP, + MON_DATA_IS_SHINY, + MON_DATA_HIDDEN_NATURE, + MON_DATA_HP_LOST, MON_DATA_ENCRYPT_SEPARATOR, + MON_DATA_NICKNAME, MON_DATA_SPECIES, MON_DATA_HELD_ITEM, MON_DATA_MOVE1, @@ -66,9 +71,7 @@ enum { MON_DATA_CUTE_RIBBON, MON_DATA_SMART_RIBBON, MON_DATA_TOUGH_RIBBON, - MON_DATA_STATUS, MON_DATA_LEVEL, - MON_DATA_HP, MON_DATA_MAX_HP, MON_DATA_ATK, MON_DATA_DEF, @@ -90,7 +93,6 @@ enum { MON_DATA_NATIONAL_RIBBON, MON_DATA_EARTH_RIBBON, MON_DATA_WORLD_RIBBON, - MON_DATA_UNUSED_RIBBONS, MON_DATA_MODERN_FATEFUL_ENCOUNTER, MON_DATA_KNOWN_MOVES, MON_DATA_RIBBON_COUNT, @@ -100,87 +102,116 @@ enum { MON_DATA_SPEED2, MON_DATA_SPATK2, MON_DATA_SPDEF2, + MON_DATA_HYPER_TRAINED_HP, + MON_DATA_HYPER_TRAINED_ATK, + MON_DATA_HYPER_TRAINED_DEF, + MON_DATA_HYPER_TRAINED_SPEED, + MON_DATA_HYPER_TRAINED_SPATK, + MON_DATA_HYPER_TRAINED_SPDEF, + MON_DATA_IS_SHADOW, + MON_DATA_DYNAMAX_LEVEL, + MON_DATA_GIGANTAMAX_FACTOR, + MON_DATA_TERA_TYPE, }; struct PokemonSubstruct0 { - /*0x00*/ u16 species; - /*0x02*/ u16 heldItem; - /*0x04*/ u32 experience; - /*0x08*/ u8 ppBonuses; - /*0x09*/ u8 friendship; - /*0x0A*/ u16 pokeball:5; //31 balls - u16 filler:11; -}; /* size = 12 */ + u16 species:11; // 2047 species. + u16 teraType:5; // 30 types. + u16 heldItem:10; // 1023 items. + u16 unused_02:6; + u32 experience:21; + u32 nickname11:8; // 11th character of nickname. + u32 unused_04:3; + u8 ppBonuses; + u8 friendship; + u16 pokeball:6; // 63 balls. + u16 nickname12:8; // 12th character of nickname. + u16 unused_0A:2; +}; struct PokemonSubstruct1 { - /*0x00*/ u16 moves[MAX_MON_MOVES]; - /*0x08*/ u8 pp[MAX_MON_MOVES]; -}; /* size = 12 */ + u16 move1:11; // 2047 moves. + u16 unused_00:5; + u16 move2:11; // 2047 moves. + u16 unused_02:5; + u16 move3:11; // 2047 moves. + u16 unused_04:5; + u16 move4:11; // 2047 moves. + u16 unused_06:3; + u16 hyperTrainedHP:1; + u16 hyperTrainedAttack:1; + u8 pp1:7; // 127 PP. + u8 hyperTrainedDefense:1; + u8 pp2:7; // 127 PP. + u8 hyperTrainedSpeed:1; + u8 pp3:7; // 127 PP. + u8 hyperTrainedSpAttack:1; + u8 pp4:7; // 127 PP. + u8 hyperTrainedSpDefense:1; +}; struct PokemonSubstruct2 { - /*0x00*/ u8 hpEV; - /*0x01*/ u8 attackEV; - /*0x02*/ u8 defenseEV; - /*0x03*/ u8 speedEV; - /*0x04*/ u8 spAttackEV; - /*0x05*/ u8 spDefenseEV; - /*0x06*/ u8 cool; - /*0x07*/ u8 beauty; - /*0x08*/ u8 cute; - /*0x09*/ u8 smart; - /*0x0A*/ u8 tough; - /*0x0B*/ u8 sheen; -}; /* size = 12 */ + u8 hpEV; + u8 attackEV; + u8 defenseEV; + u8 speedEV; + u8 spAttackEV; + u8 spDefenseEV; + u8 cool; + u8 beauty; + u8 cute; + u8 smart; + u8 tough; + u8 sheen; +}; struct PokemonSubstruct3 { - /* 0x00 */ u8 pokerus; - /* 0x01 */ u8 metLocation; + u8 pokerus; + u8 metLocation; + u16 metLevel:7; + u16 metGame:4; + u16 dynamaxLevel:4; + u16 otGender:1; + u32 hpIV:5; + u32 attackIV:5; + u32 defenseIV:5; + u32 speedIV:5; + u32 spAttackIV:5; + u32 spDefenseIV:5; + u32 isEgg:1; + u32 gigantamaxFactor:1; + u32 coolRibbon:3; // Stores the highest contest rank achieved in the Cool category. + u32 beautyRibbon:3; // Stores the highest contest rank achieved in the Beauty category. + u32 cuteRibbon:3; // Stores the highest contest rank achieved in the Cute category. + u32 smartRibbon:3; // Stores the highest contest rank achieved in the Smart category. + u32 toughRibbon:3; // Stores the highest contest rank achieved in the Tough category. + u32 championRibbon:1; // Given when defeating the Champion. Because both RSE and FRLG use it, later generations don't specify from which region it comes from. + u32 winningRibbon:1; // Given at the Battle Tower's Level 50 challenge by winning a set of seven battles that extends the current streak to 56 or more. + u32 victoryRibbon:1; // Given at the Battle Tower's Level 100 challenge by winning a set of seven battles that extends the current streak to 56 or more. + u32 artistRibbon:1; // Given at the Contest Hall by winning a Master Rank contest with at least 800 points, and agreeing to have the Pokémon's portrait placed in the museum after being offered. + u32 effortRibbon:1; // Given at Slateport's market to Pokémon with maximum EVs. + u32 marineRibbon:1; // Never distributed. + u32 landRibbon:1; // Never distributed. + u32 skyRibbon:1; // Never distributed. + u32 countryRibbon:1; // Distributed during Pokémon Festa '04 and '05 to tournament winners. + u32 nationalRibbon:1; // Given to purified Shadow Pokémon in Colosseum/XD. + u32 earthRibbon:1; // Given to teams that have beaten Mt. Battle's 100-battle challenge in Colosseum/XD. + u32 worldRibbon:1; // Distributed during Pokémon Festa '04 and '05 to tournament winners. + u32 isShadow:1; + u32 unused_0B:1; + u32 abilityNum:2; - /* 0x02 */ u16 metLevel:7; - /* 0x02 */ u16 metGame:4; - /* 0x03 */ u16 unused1:4; - /* 0x03 */ u16 otGender:1; - - /* 0x04 */ u32 hpIV:5; - /* 0x04 */ u32 attackIV:5; - /* 0x05 */ u32 defenseIV:5; - /* 0x05 */ u32 speedIV:5; - /* 0x05 */ u32 spAttackIV:5; - /* 0x06 */ u32 spDefenseIV:5; - /* 0x07 */ u32 isEgg:1; - /* 0x07 */ u32 unused2:1; - - /* 0x08 */ u32 coolRibbon:3; // Stores the highest contest rank achieved in the Cool category. - /* 0x08 */ u32 beautyRibbon:3; // Stores the highest contest rank achieved in the Beauty category. - /* 0x08 */ u32 cuteRibbon:3; // Stores the highest contest rank achieved in the Cute category. - /* 0x09 */ u32 smartRibbon:3; // Stores the highest contest rank achieved in the Smart category. - /* 0x09 */ u32 toughRibbon:3; // Stores the highest contest rank achieved in the Tough category. - /* 0x09 */ u32 championRibbon:1; // Given when defeating the Champion. Because both RSE and FRLG use it, later generations don't specify from which region it comes from. - /* 0x0A */ u32 winningRibbon:1; // Given at the Battle Tower's Level 50 challenge by winning a set of seven battles that extends the current streak to 56 or more. - /* 0x0A */ u32 victoryRibbon:1; // Given at the Battle Tower's Level 100 challenge by winning a set of seven battles that extends the current streak to 56 or more. - /* 0x0A */ u32 artistRibbon:1; // Given at the Contest Hall by winning a Master Rank contest with at least 800 points, and agreeing to have the Pokémon's portrait placed in the museum after being offered. - /* 0x0A */ u32 effortRibbon:1; // Given at Slateport's market to Pokémon with maximum EVs. - /* 0x0A */ u32 marineRibbon:1; // Never distributed. - /* 0x0A */ u32 landRibbon:1; // Never distributed. - /* 0x0A */ u32 skyRibbon:1; // Never distributed. - /* 0x0A */ u32 countryRibbon:1; // Distributed during Pokémon Festa '04 and '05 to tournament winners. - /* 0x0B */ u32 nationalRibbon:1; // Given to purified Shadow Pokémon in Colosseum/XD. - /* 0x0B */ u32 earthRibbon:1; // Given to teams that have beaten Mt. Battle's 100-battle challenge in Colosseum/XD. - /* 0x0B */ u32 worldRibbon:1; // Distributed during Pokémon Festa '04 and '05 to tournament winners. - /* 0x0B */ u32 unusedRibbons:2; // Discarded in Gen 4. - /* 0x0B */ u32 abilityNum:2; - - // The functionality of this bit changed in FRLG: - // In RS, this bit does nothing, is never set, & is accidentally unset when hatching Eggs. - // In FRLG & Emerald, this controls Mew & Deoxys obedience and whether they can be traded. - // If set, a Pokémon is a fateful encounter in FRLG's summary screen if hatched & for all Pokémon in Gen 4+ summary screens. - // Set for in-game event island legendaries, events distributed after a certain date, & Pokémon from XD: Gale of Darkness. - // Not to be confused with METLOC_FATEFUL_ENCOUNTER. - /* 0x0B */ u32 modernFatefulEncounter:1; + // The functionality of this bit changed in FRLG: + // In RS, this bit does nothing, is never set, & is accidentally unset when hatching Eggs. + // In FRLG & Emerald, this controls Mew & Deoxys obedience and whether they can be traded. + // If set, a Pokémon is a fateful encounter in FRLG's summary screen if hatched & for all Pokémon in Gen 4+ summary screens. + // Set for in-game event island legendaries, events distributed after a certain date, & Pokémon from XD: Gale of Darkness. + // Not to be confused with METLOC_FATEFUL_ENCOUNTER. + u32 modernFatefulEncounter:1; }; // Number of bytes in the largest Pokémon substruct. @@ -205,17 +236,21 @@ struct BoxPokemon { u32 personality; u32 otId; - u8 nickname[POKEMON_NAME_LENGTH]; - u8 language; + u8 nickname[min(10, POKEMON_NAME_LENGTH)]; + u8 language:3; + u8 hiddenNatureModifier:5; // 31 natures. u8 isBadEgg:1; u8 hasSpecies:1; u8 isEgg:1; - u8 blockBoxRS:1; // Unused, but Pokémon Box Ruby & Sapphire will refuse to deposit a Pokémon with this flag set - u8 unused:4; + u8 blockBoxRS:1; // Unused, but Pokémon Box Ruby & Sapphire will refuse to deposit a Pokémon with this flag set. + u8 unused_13:4; u8 otName[PLAYER_NAME_LENGTH]; - u8 markings; + u8 markings:4; + u8 compressedStatus:4; u16 checksum; - u16 unknown; + u16 hpLost:14; // 16383 HP. + u16 shinyModifier:1; + u16 unused_1E:1; union { @@ -301,6 +336,7 @@ struct BattlePokemon /*0x51*/ u32 status2; /*0x55*/ u32 otId; /*0x59*/ u8 metLevel; + /*0x5A*/ bool8 isShiny; }; struct Evolution @@ -673,7 +709,7 @@ void PlayBattleBGM(void); void PlayMapChosenOrBattleBGM(u16 songId); void CreateTask_PlayMapChosenOrBattleBGM(u16 songId); const u32 *GetMonFrontSpritePal(struct Pokemon *mon); -const u32 *GetMonSpritePalFromSpeciesAndPersonality(u16 species, u32 otId, u32 personality); +const u32 *GetMonSpritePalFromSpeciesAndPersonality(u16 species, bool32 isShiny, u32 personality); bool8 IsMoveHM(u16 move); bool8 IsMonSpriteNotFlipped(u16 species); s8 GetMonFlavorRelation(struct Pokemon *mon, u8 flavor); @@ -685,7 +721,6 @@ void BoxMonRestorePP(struct BoxPokemon *boxMon); void SetMonPreventsSwitchingString(void); void SetWildMonHeldItem(void); bool8 IsMonShiny(struct Pokemon *mon); -bool8 IsShinyOtIdPersonality(u32 otId, u32 personality); const u8 *GetTrainerPartnerName(void); void BattleAnimateFrontSprite(struct Sprite *sprite, u16 species, bool8 noCry, u8 panMode); void DoMonFrontSpriteAnimation(struct Sprite *sprite, u16 species, bool8 noCry, u8 panModeAnimFlag); @@ -719,5 +754,7 @@ u16 SanitizeSpeciesId(u16 species); bool32 IsSpeciesEnabled(u16 species); u16 GetCryIdBySpecies(u16 species); u16 GetSpeciesPreEvolution(u16 species); +void HealPokemon(struct Pokemon *mon); +void HealBoxPokemon(struct BoxPokemon *boxMon); #endif // GUARD_POKEMON_H diff --git a/include/test/battle.h b/include/test/battle.h index ee41032102..581546f87b 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -849,6 +849,10 @@ struct moveWithPP { #define Friendship(friendship) Friendship_(__LINE__, friendship) #define Status1(status1) Status1_(__LINE__, status1) #define OTName(otName) do {static const u8 otName_[] = _(otName); OTName_(__LINE__, otName_);} while (0) +#define DynamaxLevel(dynamaxLevel) DynamaxLevel_(__LINE__, dynamaxLevel) +#define GigantamaxFactor(gigantamaxFactor) GigantamaxFactor_(__LINE__, gigantamaxFactor) +#define TeraType(teraType) TeraType_(__LINE__, teraType) +#define Shadow(isShadow) Shadow_(__LINE__, shadow) void SetFlagForTest(u32 sourceLine, u16 flagId); void ClearFlagAfterTest(void); @@ -875,6 +879,10 @@ void MovesWithPP_(u32 sourceLine, struct moveWithPP moveWithPP[MAX_MON_MOVES]); void Friendship_(u32 sourceLine, u32 friendship); void Status1_(u32 sourceLine, u32 status1); void OTName_(u32 sourceLine, const u8 *otName); +void DynamaxLevel_(u32 sourceLine, u32 dynamaxLevel); +void GigantamaxFactor_(u32 sourceLine, bool32 gigantamaxFactor); +void TeraType_(u32 sourceLine, u32 teraType); +void Shadow_(u32 sourceLine, bool32 isShadow); // Created for easy use of EXPECT_MOVES, so the user can provide 1, 2, 3 or 4 moves for AI which can pass the test. struct FourMoves diff --git a/include/test/overworld_script.h b/include/test/overworld_script.h new file mode 100644 index 0000000000..e2f65930f5 --- /dev/null +++ b/include/test/overworld_script.h @@ -0,0 +1,48 @@ +/* Embedded DSL for testing overworld scripts in isolation. + * The overworld is not available, so it is only possible to test + * commands which don't affect the overworld itself, e.g. givemon can + * be tested because it only alters gPlayerParty, but addobject cannot + * because it affects object events (which aren't loaded). + * + * OVERWORLD_SCRIPT(instructions...) + * Returns a pointer to a compiled overworld script. Cannot be used to + * initialize global const data, although the pointer IS to const data. + * Note that each script command must be followed by a ;, e.g.: + * const u8 *myScript = OVERWORLD_SCRIPT( + * random 2; + * addvar VAR_RESULT, 1; + * ); + * + * RUN_OVERWORLD_SCRIPT(instructions...) + * Runs an overworld script in the immediate script context, which means + * that commands like waitstate are not supported. + * RUN_OVERWORLD_SCRIPT( + * setvar VAR_RESULT, 3; + * ); + * EXPECT_EQ(GetVar(VAR_RESULT), 3); */ +#ifndef GUARD_TEST_OVERWORLD_SCRIPT +#define GUARD_TEST_OVERWORLD_SCRIPT + +#include "script.h" +#include "test/test.h" + +#define OVERWORLD_SCRIPT(...) \ + ({ \ + const u8 *_script; \ + asm("mov %0, pc\n" \ + "b .Lend" STR(__LINE__) "\n" \ + STR(__VA_ARGS__) \ + "\n" \ + "end\n" \ + ".balign 2\n" \ + ".Lend" STR(__LINE__) ":\n" \ + : "=r" (_script)); \ + _script; \ + }) + +#define RUN_OVERWORLD_SCRIPT(...) RunScriptImmediately(OVERWORLD_SCRIPT(__VA_ARGS__)) + +// Make overworld script macros available. +asm(".include \"asm/macros/event.inc\"\n"); + +#endif diff --git a/include/test/test.h b/include/test/test.h index e9c920bd0b..74e10ec691 100644 --- a/include/test/test.h +++ b/include/test/test.h @@ -59,8 +59,9 @@ extern const struct TestRunner gAssumptionsRunner; struct FunctionTestRunnerState { - u8 parameters; - u8 runParameter; + u16 parameters; + u16 runParameter; + u16 checkProgressParameter; }; extern const struct TestRunner gFunctionTestRunner; diff --git a/include/trainer_pokemon_sprites.h b/include/trainer_pokemon_sprites.h index e56ed0ffa5..7c83ca62ee 100644 --- a/include/trainer_pokemon_sprites.h +++ b/include/trainer_pokemon_sprites.h @@ -8,8 +8,8 @@ #define F_MON_PIC_NO_AFFINE (1 << 7) bool16 ResetAllPicSprites(void); -u16 CreateMonPicSprite_Affine(u16 species, u32 otId, u32 personality, u8 flags, s16 x, s16 y, u8 paletteSlot, u16 paletteTag); -u16 CreateMonPicSprite(u16 species, u32 otId, u32 personality, bool8 isFrontPic, s16 x, s16 y, u8 paletteSlot, u16 paletteTag); +u16 CreateMonPicSprite_Affine(u16 species, bool8 isShiny, u32 personality, u8 flags, s16 x, s16 y, u8 paletteSlot, u16 paletteTag); +u16 CreateMonPicSprite(u16 species, bool8 isShiny, u32 personality, bool8 isFrontPic, s16 x, s16 y, u8 paletteSlot, u16 paletteTag); u16 FreeAndDestroyMonPicSprite(u16 spriteId); u16 CreateTrainerPicSprite(u16 species, bool8 isFrontPic, s16 x, s16 y, u8 paletteSlot, u16 paletteTag); u16 FreeAndDestroyTrainerPicSprite(u16 spriteId); diff --git a/src/battle_anim_effects_1.c b/src/battle_anim_effects_1.c index 188ec915d6..d14005b1ae 100644 --- a/src/battle_anim_effects_1.c +++ b/src/battle_anim_effects_1.c @@ -6576,7 +6576,7 @@ static void AnimTask_AllySwitchDataSwap(u8 taskId) SwapStructData(&gBattleSpritesDataPtr->battlerData[battlerAtk], &gBattleSpritesDataPtr->battlerData[battlerPartner], data, sizeof(struct BattleSpriteInfo)); SWAP(gTransformedPersonalities[battlerAtk], gTransformedPersonalities[battlerPartner], temp); - SWAP(gTransformedOtIds[battlerAtk], gTransformedOtIds[battlerPartner], temp); + SWAP(gTransformedShininess[battlerAtk], gTransformedShininess[battlerPartner], temp); SWAP(gStatuses3[battlerAtk], gStatuses3[battlerPartner], temp); SWAP(gStatuses4[battlerAtk], gStatuses4[battlerPartner], temp); SWAP(gBattleStruct->chosenMovePositions[battlerAtk], gBattleStruct->chosenMovePositions[battlerPartner], temp); diff --git a/src/battle_anim_effects_3.c b/src/battle_anim_effects_3.c index 2c8572cc8c..26fb3ef2ec 100644 --- a/src/battle_anim_effects_3.c +++ b/src/battle_anim_effects_3.c @@ -3255,9 +3255,8 @@ static void AnimReversalOrb_Step(struct Sprite *sprite) // Copies the target mon's sprite, and makes a white silhouette that shrinks away. void AnimTask_RolePlaySilhouette(u8 taskId) { - bool8 isBackPic; + bool8 isBackPic, isShiny; u32 personality; - u32 otId; u16 species; s16 xOffset; u32 priority; @@ -3269,7 +3268,7 @@ void AnimTask_RolePlaySilhouette(u8 taskId) { isBackPic = TRUE; personality = gContestResources->moveAnim->targetPersonality; - otId = gContestResources->moveAnim->otId; + isShiny = gContestResources->moveAnim->targetIsShiny; species = gContestResources->moveAnim->targetSpecies; xOffset = 20; priority = GetBattlerSpriteBGPriority(gBattleAnimAttacker); @@ -3280,7 +3279,7 @@ void AnimTask_RolePlaySilhouette(u8 taskId) { isBackPic = FALSE; personality = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattleAnimTarget]], MON_DATA_PERSONALITY); - otId = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattleAnimTarget]], MON_DATA_OT_ID); + isShiny = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattleAnimTarget]], MON_DATA_IS_SHINY); if (gBattleSpritesDataPtr->battlerData[gBattleAnimTarget].transformSpecies == SPECIES_NONE) { if (GetBattlerSide(gBattleAnimTarget) == B_SIDE_PLAYER) @@ -3300,7 +3299,7 @@ void AnimTask_RolePlaySilhouette(u8 taskId) { isBackPic = TRUE; personality = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattleAnimTarget]], MON_DATA_PERSONALITY); - otId = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattleAnimTarget]], MON_DATA_OT_ID); + isShiny = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattleAnimTarget]], MON_DATA_IS_SHINY); if (gBattleSpritesDataPtr->battlerData[gBattleAnimTarget].transformSpecies == SPECIES_NONE) { if (GetBattlerSide(gBattleAnimTarget) == B_SIDE_PLAYER) @@ -3320,7 +3319,7 @@ void AnimTask_RolePlaySilhouette(u8 taskId) coord1 = GetBattlerSpriteCoord(gBattleAnimAttacker, BATTLER_COORD_X); coord2 = GetBattlerSpriteCoord(gBattleAnimAttacker, BATTLER_COORD_Y); - spriteId = CreateAdditionalMonSpriteForMoveAnim(species, isBackPic, 0, coord1 + xOffset, coord2, 5, personality, otId, gBattleAnimTarget); + spriteId = CreateAdditionalMonSpriteForMoveAnim(species, isBackPic, 0, coord1 + xOffset, coord2, 5, personality, isShiny, gBattleAnimTarget); gSprites[spriteId].oam.priority = priority; gSprites[spriteId].oam.objMode = ST_OAM_OBJ_BLEND; @@ -5162,10 +5161,9 @@ void AnimTask_SnatchOpposingMonMove(u8 taskId) { u8 spriteId, spriteId2; int personality; - int otId; u16 species; u8 subpriority; - bool8 isBackPic; + bool8 isBackPic, isShiny; s16 x; switch (gTasks[taskId].data[0]) @@ -5190,7 +5188,7 @@ void AnimTask_SnatchOpposingMonMove(u8 taskId) if (IsContest()) { personality = gContestResources->moveAnim->personality; - otId = gContestResources->moveAnim->otId; + isShiny = gContestResources->moveAnim->isShiny; species = gContestResources->moveAnim->species; subpriority = GetBattlerSpriteSubpriority(gBattleAnimAttacker); isBackPic = FALSE; @@ -5201,7 +5199,7 @@ void AnimTask_SnatchOpposingMonMove(u8 taskId) if (GetBattlerSide(gBattleAnimAttacker) == B_SIDE_PLAYER) { personality = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattleAnimAttacker]], MON_DATA_PERSONALITY); - otId = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattleAnimAttacker]], MON_DATA_OT_ID); + isShiny = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattleAnimAttacker]], MON_DATA_IS_SHINY); if (gBattleSpritesDataPtr->battlerData[gBattleAnimAttacker].transformSpecies == SPECIES_NONE) species = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattleAnimAttacker]], MON_DATA_SPECIES); else @@ -5214,7 +5212,7 @@ void AnimTask_SnatchOpposingMonMove(u8 taskId) else { personality = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattleAnimAttacker]], MON_DATA_PERSONALITY); - otId = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattleAnimAttacker]], MON_DATA_OT_ID); + isShiny = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattleAnimAttacker]], MON_DATA_IS_SHINY); if (gBattleSpritesDataPtr->battlerData[gBattleAnimAttacker].transformSpecies == SPECIES_NONE) species = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattleAnimAttacker]], MON_DATA_SPECIES); else @@ -5226,7 +5224,7 @@ void AnimTask_SnatchOpposingMonMove(u8 taskId) } } - spriteId2 = CreateAdditionalMonSpriteForMoveAnim(species, isBackPic, 0, x, GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_Y), subpriority, personality, otId, gBattleAnimAttacker); + spriteId2 = CreateAdditionalMonSpriteForMoveAnim(species, isBackPic, 0, x, GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_Y), subpriority, personality, isShiny, gBattleAnimAttacker); if (gBattleSpritesDataPtr->battlerData[gBattleAnimAttacker].transformSpecies != SPECIES_NONE) BlendPalette(OBJ_PLTT_ID(gSprites[spriteId2].oam.paletteNum), 16, 6, RGB_WHITE); diff --git a/src/battle_anim_mons.c b/src/battle_anim_mons.c index 78e1a0a445..891fa402f5 100644 --- a/src/battle_anim_mons.c +++ b/src/battle_anim_mons.c @@ -2053,7 +2053,7 @@ u8 GetBattlerSpriteBGPriorityRank(u8 battlerId) } // Create pokemon sprite to be used for a move animation effect (e.g. Role Play / Snatch) -u8 CreateAdditionalMonSpriteForMoveAnim(u16 species, bool8 isBackpic, u8 id, s16 x, s16 y, u8 subpriority, u32 personality, u32 trainerId, u32 battlerId) +u8 CreateAdditionalMonSpriteForMoveAnim(u16 species, bool8 isBackpic, u8 id, s16 x, s16 y, u8 subpriority, u32 personality, bool8 isShiny, u32 battlerId) { u8 spriteId; u16 sheet = LoadSpriteSheet(&sSpriteSheets_MoveEffectMons[id]); @@ -2063,7 +2063,7 @@ u8 CreateAdditionalMonSpriteForMoveAnim(u16 species, bool8 isBackpic, u8 id, s16 gMonSpritesGfxPtr->buffer = AllocZeroed(MON_PIC_SIZE * MAX_MON_PIC_FRAMES); if (!isBackpic) { - LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(species, trainerId, personality), OBJ_PLTT_ID(palette), PLTT_SIZE_4BPP); + LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(species, isShiny, personality), OBJ_PLTT_ID(palette), PLTT_SIZE_4BPP); LoadSpecialPokePic(gMonSpritesGfxPtr->buffer, species, personality, @@ -2071,7 +2071,7 @@ u8 CreateAdditionalMonSpriteForMoveAnim(u16 species, bool8 isBackpic, u8 id, s16 } else { - LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(species, trainerId, personality), OBJ_PLTT_ID(palette), PLTT_SIZE_4BPP); + LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(species, isShiny, personality), OBJ_PLTT_ID(palette), PLTT_SIZE_4BPP); LoadSpecialPokePic(gMonSpritesGfxPtr->buffer, species, personality, diff --git a/src/battle_anim_throw.c b/src/battle_anim_throw.c index 4d2873a92a..6a23807a13 100644 --- a/src/battle_anim_throw.c +++ b/src/battle_anim_throw.c @@ -2481,26 +2481,17 @@ void AnimTask_SetTargetToEffectBattler(u8 taskId) void TryShinyAnimation(u8 battler, struct Pokemon *mon) { bool8 isShiny; - u32 otId, personality; - u32 shinyValue; u8 taskCirc, taskDgnl; struct Pokemon* illusionMon; - isShiny = FALSE; + isShiny = GetMonData(mon, MON_DATA_IS_SHINY); gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = TRUE; illusionMon = GetIllusionMonPtr(battler); if (illusionMon != NULL) mon = illusionMon; - otId = GetMonData(mon, MON_DATA_OT_ID); - personality = GetMonData(mon, MON_DATA_PERSONALITY); - if (IsBattlerSpriteVisible(battler) && IsValidForBattle(mon)) { - shinyValue = GET_SHINY_VALUE(otId, personality); - if (shinyValue < SHINY_ODDS) - isShiny = TRUE; - if (isShiny) { if (GetSpriteTileStartByTag(ANIM_TAG_GOLD_STARS) == 0xFFFF) diff --git a/src/battle_controllers.c b/src/battle_controllers.c index 632d7e50d8..d6389f02cc 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -2649,7 +2649,7 @@ void BtlController_HandleMoveAnimation(u32 battler, bool32 updateTvData) gWeatherMoveAnim = gBattleResources->bufferA[battler][12] | (gBattleResources->bufferA[battler][13] << 8); gAnimDisableStructPtr = (struct DisableStruct *)&gBattleResources->bufferA[battler][16]; gTransformedPersonalities[battler] = gAnimDisableStructPtr->transformedMonPersonality; - gTransformedOtIds[battler] = gAnimDisableStructPtr->transformedMonOtId; + gTransformedShininess[battler] = gAnimDisableStructPtr->transformedMonShininess; gBattleSpritesDataPtr->healthBoxesData[battler].animationState = 0; gBattlerControllerFuncs[battler] = Controller_DoMoveAnimation; if (updateTvData) diff --git a/src/battle_debug.c b/src/battle_debug.c index 1b172046ba..24ec2d13cd 100644 --- a/src/battle_debug.c +++ b/src/battle_debug.c @@ -801,7 +801,7 @@ static void Task_ShowAiPoints(u8 taskId) } } data->aiMonSpriteId = CreateMonPicSprite(gBattleMons[data->aiBattlerId].species, - gBattleMons[data->aiBattlerId].otId, + gBattleMons[data->aiBattlerId].isShiny, gBattleMons[data->aiBattlerId].personality, TRUE, 39, 130, 15, TAG_NONE); @@ -958,7 +958,7 @@ static void Task_ShowAiKnowledge(u8 taskId) } } data->aiMonSpriteId = CreateMonPicSprite(gBattleMons[data->aiBattlerId].species, - gBattleMons[data->aiBattlerId].otId, + gBattleMons[data->aiBattlerId].isShiny, gBattleMons[data->aiBattlerId].personality, TRUE, 39, 130, 15, TAG_NONE); diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index a44bb3aeb9..bf223e5002 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -149,8 +149,8 @@ bool32 CanDynamax(u16 battlerId) // Returns whether a battler is transformed into a Gigantamax form. bool32 IsGigantamaxed(u16 battlerId) { - // TODO: Incorporate Gigantamax factor. - if ((gSpeciesInfo[gBattleMons[battlerId].species].isGigantamax)) + struct Pokemon *mon = &GetSideParty(GetBattlerSide(battlerId))[gBattlerPartyIndexes[battlerId]]; + if ((gSpeciesInfo[gBattleMons[battlerId].species].isGigantamax) && GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR)) return TRUE; return FALSE; } @@ -162,9 +162,9 @@ void ApplyDynamaxHPMultiplier(u32 battler, struct Pokemon* mon) return; else { - u16 mult = UQ_4_12(1.5); // placeholder - u16 hp = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_HP) * mult) + UQ_4_12_ROUND); - u16 maxHP = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_MAX_HP) * mult) + UQ_4_12_ROUND); + u32 scale = 150 + 5 * GetMonData(mon, MON_DATA_DYNAMAX_LEVEL); + u32 hp = (GetMonData(mon, MON_DATA_HP) * scale + 99) / 100; + u32 maxHP = (GetMonData(mon, MON_DATA_MAX_HP) * scale + 99) / 100; SetMonData(mon, MON_DATA_HP, &hp); SetMonData(mon, MON_DATA_MAX_HP, &maxHP); } diff --git a/src/battle_factory_screen.c b/src/battle_factory_screen.c index 66ff6bcb03..d010ec53c8 100644 --- a/src/battle_factory_screen.c +++ b/src/battle_factory_screen.c @@ -2017,9 +2017,9 @@ static void Select_CreateMonSprite(void) struct Pokemon *mon = &sFactorySelectScreen->mons[monId].monData; u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL); u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, NULL); - u32 otId = GetMonData(mon, MON_DATA_OT_ID, NULL); + bool8 isShiny = GetMonData(mon, MON_DATA_IS_SHINY, NULL); - sFactorySelectScreen->monPics[1].monSpriteId = CreateMonPicSprite(species, otId, personality, TRUE, 88, 32, 15, TAG_NONE); + sFactorySelectScreen->monPics[1].monSpriteId = CreateMonPicSprite(species, isShiny, personality, TRUE, 88, 32, 15, TAG_NONE); gSprites[sFactorySelectScreen->monPics[1].monSpriteId].centerToCornerVecX = 0; gSprites[sFactorySelectScreen->monPics[1].monSpriteId].centerToCornerVecY = 0; @@ -2035,7 +2035,8 @@ static void Select_ReshowMonSprite(void) { struct Pokemon *mon; u16 species; - u32 personality, otId; + u32 personality; + bool8 isShiny; sFactorySelectScreen->monPics[1].bgSpriteId = CreateSprite(&sSpriteTemplate_Select_MonPicBgAnim, 120, 64, 1); StartSpriteAffineAnim(&gSprites[sFactorySelectScreen->monPics[1].bgSpriteId], 2); @@ -2043,9 +2044,9 @@ static void Select_ReshowMonSprite(void) mon = &sFactorySelectScreen->mons[sFactorySelectScreen->cursorPos].monData; species = GetMonData(mon, MON_DATA_SPECIES, NULL); personality = GetMonData(mon, MON_DATA_PERSONALITY, NULL); - otId = GetMonData(mon, MON_DATA_OT_ID, NULL); + isShiny = GetMonData(mon, MON_DATA_IS_SHINY, NULL); - sFactorySelectScreen->monPics[1].monSpriteId = CreateMonPicSprite(species, otId, personality, TRUE, 88, 32, 15, TAG_NONE); + sFactorySelectScreen->monPics[1].monSpriteId = CreateMonPicSprite(species, isShiny, personality, TRUE, 88, 32, 15, TAG_NONE); gSprites[sFactorySelectScreen->monPics[1].monSpriteId].centerToCornerVecX = 0; gSprites[sFactorySelectScreen->monPics[1].monSpriteId].centerToCornerVecY = 0; @@ -2065,9 +2066,9 @@ static void Select_CreateChosenMonsSprites(void) struct Pokemon *mon = &sFactorySelectScreen->mons[j].monData; u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL); u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, NULL); - u32 otId = GetMonData(mon, MON_DATA_OT_ID, NULL); + bool8 isShiny = GetMonData(mon, MON_DATA_IS_SHINY, NULL); - sFactorySelectScreen->monPics[i].monSpriteId = CreateMonPicSprite(species, otId, personality, TRUE, (i * 72) + 16, 32, i + 13, TAG_NONE); + sFactorySelectScreen->monPics[i].monSpriteId = CreateMonPicSprite(species, isShiny, personality, TRUE, (i * 72) + 16, 32, i + 13, TAG_NONE); gSprites[sFactorySelectScreen->monPics[i].monSpriteId].centerToCornerVecX = 0; gSprites[sFactorySelectScreen->monPics[i].monSpriteId].centerToCornerVecY = 0; break; @@ -4076,7 +4077,8 @@ static void Swap_ShowSummaryMonSprite(void) { struct Pokemon *mon; u16 species; - u32 personality, otId; + u32 personality; + bool8 isShiny; sFactorySwapScreen->monPic.bgSpriteId = CreateSprite(&sSpriteTemplate_Swap_MonPicBgAnim, 120, 64, 1); StartSpriteAffineAnim(&gSprites[sFactorySwapScreen->monPic.bgSpriteId], 2); @@ -4084,13 +4086,9 @@ static void Swap_ShowSummaryMonSprite(void) mon = &gPlayerParty[sFactorySwapScreen->cursorPos]; species = GetMonData(mon, MON_DATA_SPECIES, NULL); personality = GetMonData(mon, MON_DATA_PERSONALITY, NULL); - otId = GetMonData(mon, MON_DATA_OT_ID, NULL); + isShiny = GetMonData(mon, MON_DATA_IS_SHINY, NULL); -#ifdef BUGFIX - sFactorySwapScreen->monPic.monSpriteId = CreateMonPicSprite(species, otId, personality, TRUE, 88, 32, 15, TAG_NONE); -#else - sFactorySwapScreen->monPic.monSpriteId = CreateMonPicSprite(species, personality, otId, TRUE, 88, 32, 15, TAG_NONE); -#endif + sFactorySwapScreen->monPic.monSpriteId = CreateMonPicSprite(species, isShiny, personality, TRUE, 88, 32, 15, TAG_NONE); gSprites[sFactorySwapScreen->monPic.monSpriteId].centerToCornerVecX = 0; gSprites[sFactorySwapScreen->monPic.monSpriteId].centerToCornerVecY = 0; @@ -4295,7 +4293,8 @@ static void Swap_CreateMonSprite(void) { struct Pokemon *mon; u16 species; - u32 personality, otId; + u32 personality; + bool8 isShiny; if (!sFactorySwapScreen->inEnemyScreen) mon = &gPlayerParty[sFactorySwapScreen->cursorPos]; @@ -4304,9 +4303,9 @@ static void Swap_CreateMonSprite(void) species = GetMonData(mon, MON_DATA_SPECIES, NULL); personality = GetMonData(mon, MON_DATA_PERSONALITY, NULL); - otId = GetMonData(mon, MON_DATA_OT_ID, NULL); + isShiny = GetMonData(mon, MON_DATA_IS_SHINY, NULL); - sFactorySwapScreen->monPic.monSpriteId = CreateMonPicSprite(species, otId, personality, TRUE, 88, 32, 15, TAG_NONE); + sFactorySwapScreen->monPic.monSpriteId = CreateMonPicSprite(species, isShiny, personality, TRUE, 88, 32, 15, TAG_NONE); gSprites[sFactorySwapScreen->monPic.monSpriteId].centerToCornerVecX = 0; gSprites[sFactorySwapScreen->monPic.monSpriteId].centerToCornerVecY = 0; diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index b38ccf495b..06deedaaa5 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -570,7 +570,7 @@ bool8 IsBattleSEPlaying(u8 battler) void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battler) { - u32 monsPersonality, currentPersonality, otId, currentOtId, species, paletteOffset, position; + u32 monsPersonality, currentPersonality, isShiny, species, paletteOffset, position; const void *lzPaletteData; struct Pokemon *illusionMon = GetIllusionMonPtr(battler); if (illusionMon != NULL) @@ -580,13 +580,12 @@ void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battler) return; monsPersonality = GetMonData(mon, MON_DATA_PERSONALITY); - otId = GetMonData(mon, MON_DATA_OT_ID); + isShiny = GetMonData(mon, MON_DATA_IS_SHINY); if (gBattleSpritesDataPtr->battlerData[battler].transformSpecies == SPECIES_NONE) { species = GetMonData(mon, MON_DATA_SPECIES); currentPersonality = monsPersonality; - currentOtId = otId; } else { @@ -594,12 +593,10 @@ void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battler) if (B_TRANSFORM_SHINY >= GEN_4) { currentPersonality = gTransformedPersonalities[battler]; - currentOtId = gTransformedOtIds[battler]; } else { currentPersonality = monsPersonality; - currentOtId = otId; } } @@ -622,7 +619,7 @@ void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battler) if (gBattleSpritesDataPtr->battlerData[battler].transformSpecies == SPECIES_NONE) lzPaletteData = GetMonFrontSpritePal(mon); else - lzPaletteData = GetMonSpritePalFromSpeciesAndPersonality(species, currentOtId, currentPersonality); + lzPaletteData = GetMonSpritePalFromSpeciesAndPersonality(species, isShiny, currentPersonality); LZDecompressWram(lzPaletteData, gDecompressionBuffer); LoadPalette(gDecompressionBuffer, paletteOffset, PLTT_SIZE_4BPP); @@ -867,7 +864,8 @@ void CopyBattleSpriteInvisibility(u8 battler) void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bool8 trackEnemyPersonality) { - u32 personalityValue, otId, position, paletteOffset, targetSpecies; + u32 personalityValue, position, paletteOffset, targetSpecies; + bool8 isShiny; const void *lzPaletteData, *src; void *dst; @@ -876,7 +874,7 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo position = B_POSITION_PLAYER_LEFT; targetSpecies = gContestResources->moveAnim->targetSpecies; personalityValue = gContestResources->moveAnim->personality; - otId = gContestResources->moveAnim->otId; + isShiny = gContestResources->moveAnim->isShiny; HandleLoadSpecialPokePic(FALSE, gMonSpritesGfxPtr->sprites.ptr[position], @@ -897,14 +895,13 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo if (B_TRANSFORM_SHINY >= GEN_4 && trackEnemyPersonality) { personalityValue = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_PERSONALITY); - otId = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_OT_ID); + isShiny = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_IS_SHINY); } else { personalityValue = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_PERSONALITY); - otId = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_OT_ID); + isShiny = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_IS_SHINY); } - otId = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_OT_ID); HandleLoadSpecialPokePic(FALSE, gMonSpritesGfxPtr->sprites.ptr[position], @@ -916,13 +913,12 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo if (B_TRANSFORM_SHINY >= GEN_4 && trackEnemyPersonality) { personalityValue = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_PERSONALITY); - otId = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_OT_ID); - + isShiny = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_IS_SHINY); } else { personalityValue = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_PERSONALITY); - otId = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_OT_ID); + isShiny = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_IS_SHINY); } HandleLoadSpecialPokePic(TRUE, @@ -935,7 +931,7 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo dst = (void *)(OBJ_VRAM0 + gSprites[gBattlerSpriteIds[battlerAtk]].oam.tileNum * 32); DmaCopy32(3, src, dst, MON_PIC_SIZE); paletteOffset = OBJ_PLTT_ID(battlerAtk); - lzPaletteData = GetMonSpritePalFromSpeciesAndPersonality(targetSpecies, otId, personalityValue); + lzPaletteData = GetMonSpritePalFromSpeciesAndPersonality(targetSpecies, isShiny, personalityValue); LZDecompressWram(lzPaletteData, gDecompressionBuffer); LoadPalette(gDecompressionBuffer, paletteOffset, PLTT_SIZE_4BPP); diff --git a/src/battle_main.c b/src/battle_main.c index 6a1d6eb6ab..d5186b7ef2 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -214,7 +214,7 @@ EWRAM_DATA u8 gBattlerStatusSummaryTaskId[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u8 gBattlerInMenuId = 0; EWRAM_DATA bool8 gDoingBattleAnim = FALSE; EWRAM_DATA u32 gTransformedPersonalities[MAX_BATTLERS_COUNT] = {0}; -EWRAM_DATA u32 gTransformedOtIds[MAX_BATTLERS_COUNT] = {0}; +EWRAM_DATA bool8 gTransformedShininess[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u8 gPlayerDpadHoldFrames = 0; EWRAM_DATA struct BattleSpriteData *gBattleSpritesDataPtr = NULL; EWRAM_DATA struct MonSpritesGfx *gMonSpritesGfxPtr = NULL; @@ -2018,6 +2018,21 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer { SetMonData(&party[i], MON_DATA_NICKNAME, partyData[i].nickname); } + if (partyData[i].isShiny) + { + u32 data = TRUE; + SetMonData(&party[i], MON_DATA_IS_SHINY, &data); + } + if (partyData[i].dynamaxLevel > 0) + { + u32 data = partyData[i].dynamaxLevel; + SetMonData(&party[i], MON_DATA_DYNAMAX_LEVEL, &data); + } + if (partyData[i].gigantamaxFactor) + { + u32 data = partyData[i].gigantamaxFactor; + SetMonData(&party[i], MON_DATA_GIGANTAMAX_FACTOR, &data); + } CalculateMonStats(&party[i]); #if B_TRAINER_CLASS_POKE_BALLS >= GEN_7 diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 0272aa4108..290895a80b 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -12308,7 +12308,7 @@ static void Cmd_transformdataexecution(void) gDisableStructs[gBattlerAttacker].disabledMove = MOVE_NONE; gDisableStructs[gBattlerAttacker].disableTimer = 0; gDisableStructs[gBattlerAttacker].transformedMonPersonality = gBattleMons[gBattlerTarget].personality; - gDisableStructs[gBattlerAttacker].transformedMonOtId = gBattleMons[gBattlerTarget].otId; + gDisableStructs[gBattlerAttacker].transformedMonShininess = gBattleMons[gBattlerTarget].isShiny; gDisableStructs[gBattlerAttacker].mimickedMoves = 0; gDisableStructs[gBattlerAttacker].usedMoves = 0; @@ -15171,10 +15171,11 @@ static void Cmd_displaydexinfo(void) case 1: if (!gPaletteFade.active) { + struct Pokemon *mon = &gEnemyParty[gBattlerPartyIndexes[GetCatchingBattler()]]; FreeAllWindowBuffers(); gBattleCommunication[TASK_ID] = DisplayCaughtMonDexPage(species, - gBattleMons[GetCatchingBattler()].otId, - gBattleMons[GetCatchingBattler()].personality); + GetMonData(mon, MON_DATA_IS_SHINY), + GetMonData(mon, MON_DATA_PERSONALITY)); gBattleCommunication[0]++; } break; diff --git a/src/battle_tower.c b/src/battle_tower.c index 6dba9a63ed..53ed39f236 100644 --- a/src/battle_tower.c +++ b/src/battle_tower.c @@ -2990,21 +2990,17 @@ static void FillPartnerParty(u16 trainerId) else otID = ((firstIdPart % 72) * 1000) + ((secondIdPart % 23) * 10) + (thirdIdPart % 37) % 65536; - do - { - personality = Random32(); - } while (IsShinyOtIdPersonality(otID, personality)); - + personality = Random32(); if (partyData[i].gender == TRAINER_MON_MALE) personality = (personality & 0xFFFFFF00) | GeneratePersonalityForGender(MON_MALE, partyData[i].species); else if (partyData[i].gender == TRAINER_MON_FEMALE) personality = (personality & 0xFFFFFF00) | GeneratePersonalityForGender(MON_FEMALE, partyData[i].species); if (partyData[i].nature != 0) ModifyPersonalityForNature(&personality, partyData[i].nature - 1); - if (partyData[i].isShiny) - otID ^= GET_SHINY_VALUE(otID, personality) << 16; CreateMon(&gPlayerParty[i + 3], partyData[i].species, partyData[i].lvl, 0, TRUE, personality, OT_ID_PRESET, otID); + j = partyData[i].isShiny; + SetMonData(&gPlayerParty[i + 3], MON_DATA_IS_SHINY, &j); SetMonData(&gPlayerParty[i + 3], MON_DATA_HELD_ITEM, &partyData[i].heldItem); CustomTrainerPartyAssignMoves(&gPlayerParty[i + 3], &partyData[i]); diff --git a/src/battle_util.c b/src/battle_util.c index da42b40010..fa34fc7460 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -10555,6 +10555,7 @@ u16 GetBattleFormChangeTargetSpecies(u32 battler, u16 method) u16 targetSpecies = SPECIES_NONE; u16 species = gBattleMons[battler].species; const struct FormChange *formChanges = GetSpeciesFormChanges(species); + struct Pokemon *mon = &GetBattlerParty(battler)[gBattlerPartyIndexes[battler]]; u16 heldItem; if (formChanges != NULL) @@ -10602,8 +10603,8 @@ u16 GetBattleFormChangeTargetSpecies(u32 battler, u16 method) } break; case FORM_CHANGE_BATTLE_GIGANTAMAX: - // TODO: check Gigantamax factor - targetSpecies = formChanges[i].targetSpecies; + if (GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR)) + targetSpecies = formChanges[i].targetSpecies; break; case FORM_CHANGE_BATTLE_WEATHER: // Check if there is a required ability and if the battler's ability does not match it diff --git a/src/contest.c b/src/contest.c index d4cc73677c..6b7322335c 100644 --- a/src/contest.c +++ b/src/contest.c @@ -98,7 +98,7 @@ static void PrintContestantMonName(u8); static void PrintContestantMonNameWithColor(u8, u8); static u8 CreateJudgeSprite(void); static u8 CreateJudgeSpeechBubbleSprite(void); -static u8 CreateContestantSprite(u16, u32, u32, u32); +static u8 CreateContestantSprite(u16, bool8, u32, u32); static void PrintContestMoveDescription(u16); static u16 SanitizeSpecies(u16); static void ContestClearGeneralTextWindow(void); @@ -1781,7 +1781,7 @@ static void Task_DoAppeals(u8 taskId) SetMoveAnimAttackerData(eContest.currentContestant); spriteId = CreateContestantSprite( gContestMons[eContest.currentContestant].species, - gContestMons[eContest.currentContestant].otId, + gContestMons[eContest.currentContestant].isShiny, gContestMons[eContest.currentContestant].personality, eContest.currentContestant); gSprites[spriteId].x2 = 120; @@ -2811,6 +2811,7 @@ void CreateContestMonFromParty(u8 partyIndex) gContestMons[gContestPlayerMonIndex].moves[3] = GetMonData(&gPlayerParty[partyIndex], MON_DATA_MOVE4); gContestMons[gContestPlayerMonIndex].personality = GetMonData(&gPlayerParty[partyIndex], MON_DATA_PERSONALITY); gContestMons[gContestPlayerMonIndex].otId = GetMonData(&gPlayerParty[partyIndex], MON_DATA_OT_ID); + gContestMons[gContestPlayerMonIndex].isShiny = GetMonData(&gPlayerParty[partyIndex], MON_DATA_IS_SHINY); heldItem = GetMonData(&gPlayerParty[partyIndex], MON_DATA_HELD_ITEM); cool = gContestMons[gContestPlayerMonIndex].cool; @@ -3114,14 +3115,14 @@ static u8 CreateJudgeSpeechBubbleSprite(void) return spriteId; } -static u8 CreateContestantSprite(u16 species, u32 otId, u32 personality, u32 index) +static u8 CreateContestantSprite(u16 species, bool8 isShiny, u32 personality, u32 index) { u8 spriteId; species = SanitizeSpecies(species); HandleLoadSpecialPokePic(FALSE, gMonSpritesGfxPtr->sprites.ptr[B_POSITION_PLAYER_LEFT], species, personality); - LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(species, otId, personality), OBJ_PLTT_ID(2), PLTT_SIZE_4BPP); + LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(species, isShiny, personality), OBJ_PLTT_ID(2), PLTT_SIZE_4BPP); SetMultiuseSpriteTemplateToPokemon(species, B_POSITION_PLAYER_LEFT); spriteId = CreateSprite(&gMultiuseSpriteTemplate, 0x70, GetBattlerSpriteFinal_Y(2, species, FALSE), 30); @@ -5354,6 +5355,7 @@ static void SetMoveAnimAttackerData(u8 contestant) gContestResources->moveAnim->species = SanitizeSpecies(gContestMons[contestant].species); gContestResources->moveAnim->personality = gContestMons[contestant].personality; gContestResources->moveAnim->otId = gContestMons[contestant].otId; + gContestResources->moveAnim->isShiny = gContestMons[contestant].isShiny; } static void CreateInvisibleBattleTargetSprite(void) @@ -5565,6 +5567,7 @@ bool8 SaveContestWinner(u8 rank) { // Set the most recent winner so the artist can show the player their painting gCurContestWinner.personality = gContestMons[i].personality; + gCurContestWinner.isShiny = gContestMons[i].isShiny; gCurContestWinner.trainerId = gContestMons[i].otId; gCurContestWinner.species = gContestMons[i].species; StringCopy(gCurContestWinner.monName, gContestMons[i].nickname); diff --git a/src/contest_painting.c b/src/contest_painting.c index 00bacb5f48..529330a522 100644 --- a/src/contest_painting.c +++ b/src/contest_painting.c @@ -363,7 +363,7 @@ static void VBlankCB_ContestPainting(void) static void InitContestMonPixels(u16 species, bool8 backPic) { - const void *pal = GetMonSpritePalFromSpeciesAndPersonality(species, gContestPaintingWinner->trainerId, gContestPaintingWinner->personality); + const void *pal = GetMonSpritePalFromSpeciesAndPersonality(species, gContestPaintingWinner->isShiny, gContestPaintingWinner->personality); LZDecompressVram(pal, gContestPaintingMonPalette); if (!backPic) { diff --git a/src/contest_util.c b/src/contest_util.c index 32588a8c00..7de4923037 100644 --- a/src/contest_util.c +++ b/src/contest_util.c @@ -879,7 +879,7 @@ static void Task_ShowWinnerMonBanner(u8 taskId) int i; u8 spriteId; u16 species; - u32 otId; + bool8 isShiny; u32 personality; switch (gTasks[taskId].tState) @@ -891,13 +891,13 @@ static void Task_ShowWinnerMonBanner(u8 taskId) GET_CONTEST_WINNER_ID(i); species = gContestMons[i].species; personality = gContestMons[i].personality; - otId = gContestMons[i].otId; + isShiny = gContestMons[i].isShiny; HandleLoadSpecialPokePic(TRUE, gMonSpritesGfxPtr->sprites.ptr[B_POSITION_OPPONENT_LEFT], species, personality); - LoadCompressedSpritePaletteWithTag(GetMonSpritePalFromSpeciesAndPersonality(species, otId, personality), species); + LoadCompressedSpritePaletteWithTag(GetMonSpritePalFromSpeciesAndPersonality(species, isShiny, personality), species); SetMultiuseSpriteTemplateToPokemon(species, B_POSITION_OPPONENT_LEFT); gMultiuseSpriteTemplate.paletteTag = species; spriteId = CreateSprite(&gMultiuseSpriteTemplate, DISPLAY_WIDTH + 32, DISPLAY_HEIGHT / 2, 10); @@ -2552,11 +2552,12 @@ bool8 IsContestDebugActive(void) void ShowContestEntryMonPic(void) { - u32 personality, otId; + u32 personality; u16 species; u8 spriteId; u8 taskId; u8 left, top; + bool32 isShiny; if (FindTaskIdByFunc(Task_ShowContestEntryMonPic) == TASK_NONE) { @@ -2565,13 +2566,13 @@ void ShowContestEntryMonPic(void) top = 3; species = gContestMons[gSpecialVar_0x8006].species; personality = gContestMons[gSpecialVar_0x8006].personality; - otId = gContestMons[gSpecialVar_0x8006].otId; + isShiny = gContestMons[gSpecialVar_0x8006].isShiny; taskId = CreateTask(Task_ShowContestEntryMonPic, 0x50); gTasks[taskId].data[0] = 0; gTasks[taskId].data[1] = species; HandleLoadSpecialPokePic(TRUE, gMonSpritesGfxPtr->sprites.ptr[B_POSITION_OPPONENT_LEFT], species, personality); - LoadCompressedSpritePaletteWithTag(GetMonSpritePalFromSpeciesAndPersonality(species, otId, personality), species); + LoadCompressedSpritePaletteWithTag(GetMonSpritePalFromSpeciesAndPersonality(species, isShiny, personality), species); SetMultiuseSpriteTemplateToPokemon(species, B_POSITION_OPPONENT_LEFT); gMultiuseSpriteTemplate.paletteTag = species; spriteId = CreateSprite(&gMultiuseSpriteTemplate, (left + 1) * 8 + 32, (top * 8) + 40, 0); diff --git a/src/data/items.h b/src/data/items.h index 2114d1a4bd..7c1f3e2472 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -1272,8 +1272,9 @@ const struct Item gItems[] = "ups Attack, but\n" "reduces Defense."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_LONELY, .flingPower = 10, }, @@ -1285,8 +1286,9 @@ const struct Item gItems[] = "ups Attack, but\n" "reduces Sp. Atk."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_ADAMANT, .flingPower = 10, }, @@ -1298,8 +1300,9 @@ const struct Item gItems[] = "ups Attack, but\n" "reduces Sp. Def."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_NAUGHTY, .flingPower = 10, }, @@ -1311,8 +1314,9 @@ const struct Item gItems[] = "ups Attack, but\n" "reduces Speed."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_BRAVE, .flingPower = 10, }, @@ -1324,8 +1328,9 @@ const struct Item gItems[] = "ups Defense, but\n" "reduces Attack."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_BOLD, .flingPower = 10, }, @@ -1337,8 +1342,9 @@ const struct Item gItems[] = "ups Defense, but\n" "reduces Sp. Atk."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_IMPISH, .flingPower = 10, }, @@ -1350,8 +1356,9 @@ const struct Item gItems[] = "ups Defense, but\n" "reduces Sp. Def."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_LAX, .flingPower = 10, }, @@ -1363,8 +1370,9 @@ const struct Item gItems[] = "ups Defense, but\n" "reduces Speed."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_RELAXED, .flingPower = 10, }, @@ -1376,8 +1384,9 @@ const struct Item gItems[] = "ups Sp. Atk, but\n" "reduces Attack."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_MODEST, .flingPower = 10, }, @@ -1389,8 +1398,9 @@ const struct Item gItems[] = "ups Sp. Atk, but\n" "reduces Defense."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_MILD, .flingPower = 10, }, @@ -1402,8 +1412,9 @@ const struct Item gItems[] = "ups Sp. Atk, but\n" "reduces Sp. Def."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_RASH, .flingPower = 10, }, @@ -1415,8 +1426,9 @@ const struct Item gItems[] = "ups Sp. Atk, but\n" "reduces Speed."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_QUIET, .flingPower = 10, }, @@ -1428,8 +1440,9 @@ const struct Item gItems[] = "ups Sp. Def, but\n" "reduces Attack."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_CALM, .flingPower = 10, }, @@ -1441,8 +1454,9 @@ const struct Item gItems[] = "ups Sp. Def, but\n" "reduces Defense."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_GENTLE, .flingPower = 10, }, @@ -1454,8 +1468,9 @@ const struct Item gItems[] = "ups Sp. Def, but\n" "reduces Sp. Atk."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_CAREFUL, .flingPower = 10, }, @@ -1467,8 +1482,9 @@ const struct Item gItems[] = "ups Sp. Def, but\n" "reduces Speed."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_SASSY, .flingPower = 10, }, @@ -1480,8 +1496,9 @@ const struct Item gItems[] = "ups Speed, but\n" "reduces Attack."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_TIMID, .flingPower = 10, }, @@ -1493,8 +1510,9 @@ const struct Item gItems[] = "ups Speed, but\n" "reduces Defense."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_HASTY, .flingPower = 10, }, @@ -1506,8 +1524,9 @@ const struct Item gItems[] = "ups Speed, but\n" "reduces Sp. Atk."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_JOLLY, .flingPower = 10, }, @@ -1519,8 +1538,9 @@ const struct Item gItems[] = "ups Speed, but\n" "reduces Sp. Def."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_NAIVE, .flingPower = 10, }, @@ -1532,8 +1552,9 @@ const struct Item gItems[] = "makes each stat\n" "grow equally."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Mint, + .secondaryId = NATURE_SERIOUS, .flingPower = 10, }, @@ -1630,8 +1651,8 @@ const struct Item gItems[] = "Level of a single\n" "Pokémon by one."), .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_DynamaxCandy, .flingPower = 30, }, diff --git a/src/daycare.c b/src/daycare.c index 33b3d8ac1b..31d2abe62e 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -229,7 +229,6 @@ static void StorePokemonInDaycare(struct Pokemon *mon, struct DaycareMon *daycar } daycareMon->mon = mon->box; - BoxMonRestorePP(&daycareMon->mon); daycareMon->steps = 0; ZeroMonData(mon); CompactPartySlots(); diff --git a/src/debug.c b/src/debug.c index c1853af4cf..e68d65cbd6 100644 --- a/src/debug.c +++ b/src/debug.c @@ -2938,7 +2938,7 @@ static void ResetMonDataStruct(struct DebugMonData *sDebugMonData) { sDebugMonData->species = 1; sDebugMonData->level = MIN_LEVEL; - sDebugMonData->isShiny = 0; + sDebugMonData->isShiny = FALSE; sDebugMonData->nature = 0; sDebugMonData->abilityNum = 0; sDebugMonData->mon_iv_hp = 0; @@ -3608,7 +3608,7 @@ static void DebugAction_Give_Pokemon_ComplexCreateMon(u8 taskId) //https://githu u8 iv_val; u16 species = sDebugMonData->species; u8 level = sDebugMonData->level; - u8 isShiny = sDebugMonData->isShiny; //Shiny: no 0, yes 1 + bool8 isShiny = sDebugMonData->isShiny; u8 nature = sDebugMonData->nature; u8 abilityNum = sDebugMonData->abilityNum; moves[0] = sDebugMonData->mon_move_0; @@ -3625,26 +3625,10 @@ 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, 32, nature); //Shininess - if (isShiny == 1) - { - u32 personality; - u32 otid = gSaveBlock2Ptr->playerTrainerId[0] - | (gSaveBlock2Ptr->playerTrainerId[1] << 8) - | (gSaveBlock2Ptr->playerTrainerId[2] << 16) - | (gSaveBlock2Ptr->playerTrainerId[3] << 24); - - do - { - personality = Random32(); - personality = ((((Random() % 8) ^ (HIHALF(otid) ^ LOHALF(otid))) ^ LOHALF(personality)) << 16) | LOHALF(personality); - } while (nature != GetNatureFromPersonality(personality)); - - CreateMon(&mon, species, level, 32, 1, personality, OT_ID_PRESET, otid); - } - else - CreateMonWithNature(&mon, species, level, 32, nature); + SetMonData(&mon, MON_DATA_IS_SHINY, &isShiny); //IVs for (i = 0; i < NUM_STATS; i++) diff --git a/src/evolution_scene.c b/src/evolution_scene.c index 1bd0e26561..b254dab42a 100644 --- a/src/evolution_scene.c +++ b/src/evolution_scene.c @@ -209,7 +209,8 @@ void EvolutionScene(struct Pokemon *mon, u16 postEvoSpecies, bool8 canStopEvo, u { u8 name[POKEMON_NAME_BUFFER_SIZE]; u16 currSpecies; - u32 trainerId, personality; + u32 personality; + bool32 isShiny; u8 id; SetHBlankCallback(NULL); @@ -255,13 +256,13 @@ void EvolutionScene(struct Pokemon *mon, u16 postEvoSpecies, bool8 canStopEvo, u // preEvo sprite currSpecies = GetMonData(mon, MON_DATA_SPECIES); - trainerId = GetMonData(mon, MON_DATA_OT_ID); + isShiny = GetMonData(mon, MON_DATA_IS_SHINY); personality = GetMonData(mon, MON_DATA_PERSONALITY); LoadSpecialPokePic(gMonSpritesGfxPtr->sprites.ptr[B_POSITION_OPPONENT_LEFT], currSpecies, personality, TRUE); - LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(currSpecies, trainerId, personality), OBJ_PLTT_ID(1), PLTT_SIZE_4BPP); + LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(currSpecies, isShiny, personality), OBJ_PLTT_ID(1), PLTT_SIZE_4BPP); SetMultiuseSpriteTemplateToPokemon(currSpecies, B_POSITION_OPPONENT_LEFT); gMultiuseSpriteTemplate.affineAnims = gDummySpriteAffineAnimTable; @@ -276,7 +277,7 @@ void EvolutionScene(struct Pokemon *mon, u16 postEvoSpecies, bool8 canStopEvo, u postEvoSpecies, personality, TRUE); - LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(postEvoSpecies, trainerId, personality), OBJ_PLTT_ID(2), PLTT_SIZE_4BPP); + LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(postEvoSpecies, isShiny, personality), OBJ_PLTT_ID(2), PLTT_SIZE_4BPP); SetMultiuseSpriteTemplateToPokemon(postEvoSpecies, B_POSITION_OPPONENT_RIGHT); gMultiuseSpriteTemplate.affineAnims = gDummySpriteAffineAnimTable; @@ -310,11 +311,12 @@ static void CB2_EvolutionSceneLoadGraphics(void) { u8 id; u16 postEvoSpecies; - u32 trainerId, personality; + u32 personality; struct Pokemon *mon = &gPlayerParty[gTasks[sEvoStructPtr->evoTaskId].tPartyId]; + bool8 isShiny; postEvoSpecies = gTasks[sEvoStructPtr->evoTaskId].tPostEvoSpecies; - trainerId = GetMonData(mon, MON_DATA_OT_ID); + isShiny = GetMonData(mon, MON_DATA_IS_SHINY); personality = GetMonData(mon, MON_DATA_PERSONALITY); SetHBlankCallback(NULL); @@ -352,7 +354,7 @@ static void CB2_EvolutionSceneLoadGraphics(void) postEvoSpecies, personality, TRUE); - LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(postEvoSpecies, trainerId, personality), OBJ_PLTT_ID(2), PLTT_SIZE_4BPP); + LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(postEvoSpecies, isShiny, personality), OBJ_PLTT_ID(2), PLTT_SIZE_4BPP); SetMultiuseSpriteTemplateToPokemon(postEvoSpecies, B_POSITION_OPPONENT_RIGHT); gMultiuseSpriteTemplate.affineAnims = gDummySpriteAffineAnimTable; @@ -416,13 +418,13 @@ static void CB2_TradeEvolutionSceneLoadGraphics(void) break; case 4: { - u32 trainerId = GetMonData(mon, MON_DATA_OT_ID); + bool8 isShiny = GetMonData(mon, MON_DATA_IS_SHINY); u32 personality = GetMonData(mon, MON_DATA_PERSONALITY); LoadSpecialPokePic(gMonSpritesGfxPtr->sprites.ptr[B_POSITION_OPPONENT_RIGHT], postEvoSpecies, personality, TRUE); - LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(postEvoSpecies, trainerId, personality), OBJ_PLTT_ID(2), PLTT_SIZE_4BPP); + LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(postEvoSpecies, isShiny, personality), OBJ_PLTT_ID(2), PLTT_SIZE_4BPP); gMain.state++; } break; @@ -464,8 +466,9 @@ void TradeEvolutionScene(struct Pokemon *mon, u16 postEvoSpecies, u8 preEvoSprit { u8 name[POKEMON_NAME_BUFFER_SIZE]; u16 currSpecies; - u32 trainerId, personality; + u32 personality; u8 id; + bool8 isShiny; GetMonData(mon, MON_DATA_NICKNAME, name); StringCopy_Nickname(gStringVar1, name); @@ -476,7 +479,7 @@ void TradeEvolutionScene(struct Pokemon *mon, u16 postEvoSpecies, u8 preEvoSprit // preEvo sprite currSpecies = GetMonData(mon, MON_DATA_SPECIES); personality = GetMonData(mon, MON_DATA_PERSONALITY); - trainerId = GetMonData(mon, MON_DATA_OT_ID); + isShiny = GetMonData(mon, MON_DATA_IS_SHINY); sEvoStructPtr = AllocZeroed(sizeof(struct EvoInfo)); sEvoStructPtr->preEvoSpriteId = preEvoSpriteId; @@ -486,7 +489,7 @@ void TradeEvolutionScene(struct Pokemon *mon, u16 postEvoSpecies, u8 preEvoSprit personality, TRUE); - LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(postEvoSpecies, trainerId, personality), OBJ_PLTT_ID(2), PLTT_SIZE_4BPP); + LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(postEvoSpecies, isShiny, personality), OBJ_PLTT_ID(2), PLTT_SIZE_4BPP); SetMultiuseSpriteTemplateToPokemon(postEvoSpecies, B_POSITION_OPPONENT_LEFT); gMultiuseSpriteTemplate.affineAnims = gDummySpriteAffineAnimTable; @@ -564,8 +567,6 @@ static void CreateShedinja(u16 preEvoSpecies, struct Pokemon *mon) SetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_NICKNAME, GetSpeciesName(evolutions[1].targetSpecies)); SetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_HELD_ITEM, &data); SetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_MARKINGS, &data); - SetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_ENCRYPT_SEPARATOR, &data); - #if P_SHEDINJA_BALL >= GEN_4 SetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_POKEBALL, &ball); RemoveBagItem(ball, 1); @@ -573,7 +574,7 @@ static void CreateShedinja(u16 preEvoSpecies, struct Pokemon *mon) for (i = MON_DATA_COOL_RIBBON; i < MON_DATA_COOL_RIBBON + CONTEST_CATEGORIES_COUNT; i++) SetMonData(&gPlayerParty[gPlayerPartyCount], i, &data); - for (i = MON_DATA_CHAMPION_RIBBON; i <= MON_DATA_UNUSED_RIBBONS; i++) + for (i = MON_DATA_CHAMPION_RIBBON; i <= MON_DATA_WORLD_RIBBON; i++) SetMonData(&gPlayerParty[gPlayerPartyCount], i, &data); SetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_STATUS, &data); diff --git a/src/field_effect.c b/src/field_effect.c index 5d0ba3a50d..294d13b01c 100644 --- a/src/field_effect.c +++ b/src/field_effect.c @@ -183,7 +183,7 @@ static void AnimateIndoorShowMonBg(struct Task *); static bool8 SlideIndoorBannerOnscreen(struct Task *); static bool8 SlideIndoorBannerOffscreen(struct Task *); -static u8 InitFieldMoveMonSprite(u32, u32, u32); +static u8 InitFieldMoveMonSprite(u32, bool8, u32); static void SpriteCB_FieldMoveMonSlideOnscreen(struct Sprite *); static void SpriteCB_FieldMoveMonWaitAfterCry(struct Sprite *); static void SpriteCB_FieldMoveMonSlideOffscreen(struct Sprite *); @@ -919,7 +919,7 @@ u8 AddNewGameBirchObject(s16 x, s16 y, u8 subpriority) u8 CreateMonSprite_PicBox(u16 species, s16 x, s16 y, u8 subpriority) { - s32 spriteId = CreateMonPicSprite(species, 0, 0x8000, TRUE, x, y, 0, species); + s32 spriteId = CreateMonPicSprite(species, FALSE, 0x8000, TRUE, x, y, 0, species); PreservePaletteInWeather(IndexOfSpritePaletteTag(species) + 0x10); if (spriteId == 0xFFFF) return MAX_SPRITES; @@ -927,10 +927,10 @@ u8 CreateMonSprite_PicBox(u16 species, s16 x, s16 y, u8 subpriority) return spriteId; } -u8 CreateMonSprite_FieldMove(u16 species, u32 otId, u32 personality, s16 x, s16 y, u8 subpriority) +u8 CreateMonSprite_FieldMove(u16 species, bool8 isShiny, u32 personality, s16 x, s16 y, u8 subpriority) { - u16 spriteId = CreateMonPicSprite(species, otId, personality, TRUE, x, y, 0, species); - PreservePaletteInWeather(IndexOfSpritePaletteTag(species) + 0x10); + u16 spriteId = CreateMonPicSprite(species, isShiny, personality, TRUE, x, y, 0, species); + PreservePaletteInWeather(gSprites[spriteId].oam.paletteNum + 0x10); if (spriteId == 0xFFFF) return MAX_SPRITES; else @@ -2586,7 +2586,7 @@ bool8 FldEff_FieldMoveShowMonInit(void) bool32 noDucking = gFieldEffectArguments[0] & SHOW_MON_CRY_NO_DUCKING; pokemon = &gPlayerParty[(u8)gFieldEffectArguments[0]]; gFieldEffectArguments[0] = GetMonData(pokemon, MON_DATA_SPECIES); - gFieldEffectArguments[1] = GetMonData(pokemon, MON_DATA_OT_ID); + gFieldEffectArguments[1] = GetMonData(pokemon, MON_DATA_IS_SHINY); gFieldEffectArguments[2] = GetMonData(pokemon, MON_DATA_PERSONALITY); gFieldEffectArguments[0] |= noDucking; FieldEffectStart(FLDEFF_FIELD_MOVE_SHOW_MON); @@ -2926,14 +2926,14 @@ static bool8 SlideIndoorBannerOffscreen(struct Task *task) #undef tBgOffset #undef tMonSpriteId -static u8 InitFieldMoveMonSprite(u32 species, u32 otId, u32 personality) +static u8 InitFieldMoveMonSprite(u32 species, bool8 isShiny, u32 personality) { bool16 noDucking; u8 monSprite; struct Sprite *sprite; noDucking = (species & SHOW_MON_CRY_NO_DUCKING) >> 16; species &= ~SHOW_MON_CRY_NO_DUCKING; - monSprite = CreateMonSprite_FieldMove(species, otId, personality, 320, 80, 0); + monSprite = CreateMonSprite_FieldMove(species, isShiny, personality, 320, 80, 0); sprite = &gSprites[monSprite]; sprite->callback = SpriteCallbackDummy; sprite->oam.priority = 0; diff --git a/src/frontier_util.c b/src/frontier_util.c index 40bc24c741..15f5385e83 100644 --- a/src/frontier_util.c +++ b/src/frontier_util.c @@ -2462,10 +2462,7 @@ void CreateFrontierBrainPokemon(void) do { - do - { - j = Random32(); //should just be one while loop, but that doesn't match - } while (IsShinyOtIdPersonality(FRONTIER_BRAIN_OTID, j)); + j = Random32(); //should just be one while loop, but that doesn't match } while (sFrontierBrainsMons[facility][symbol][i].nature != GetNatureFromPersonality(j)); CreateMon(&gEnemyParty[monPartyId], sFrontierBrainsMons[facility][symbol][i].species, @@ -2484,6 +2481,8 @@ void CreateFrontierBrainPokemon(void) friendship = 0; } SetMonData(&gEnemyParty[monPartyId], MON_DATA_FRIENDSHIP, &friendship); + j = FALSE; + SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_IS_SHINY, &j); CalculateMonStats(&gEnemyParty[monPartyId]); monPartyId++; } diff --git a/src/hall_of_fame.c b/src/hall_of_fame.c index caa497e395..cdb1edb461 100644 --- a/src/hall_of_fame.c +++ b/src/hall_of_fame.c @@ -42,7 +42,8 @@ struct HallofFameMon { u32 tid; u32 personality; - u16 species; + u16 isShiny:1; + u16 species:15; u8 lvl; u8 nickname[POKEMON_NAME_LENGTH]; }; @@ -336,6 +337,7 @@ static const struct HallofFameMon sDummyFameMon = { .tid = 0x3EA03EA, .personality = 0, + .isShiny = FALSE, .species = SPECIES_NONE, .lvl = 0, .nickname = {0} @@ -447,6 +449,7 @@ static void Task_Hof_InitMonData(u8 taskId) { sHofMonPtr->mon[i].species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG); sHofMonPtr->mon[i].tid = GetMonData(&gPlayerParty[i], MON_DATA_OT_ID); + sHofMonPtr->mon[i].isShiny = GetMonData(&gPlayerParty[i], MON_DATA_IS_SHINY); sHofMonPtr->mon[i].personality = GetMonData(&gPlayerParty[i], MON_DATA_PERSONALITY); sHofMonPtr->mon[i].lvl = GetMonData(&gPlayerParty[i], MON_DATA_LEVEL); GetMonData(&gPlayerParty[i], MON_DATA_NICKNAME, nickname); @@ -458,6 +461,7 @@ static void Task_Hof_InitMonData(u8 taskId) { sHofMonPtr->mon[i].species = SPECIES_NONE; sHofMonPtr->mon[i].tid = 0; + sHofMonPtr->mon[i].isShiny = FALSE; sHofMonPtr->mon[i].personality = 0; sHofMonPtr->mon[i].lvl = 0; sHofMonPtr->mon[i].nickname[0] = EOS; @@ -583,7 +587,7 @@ static void Task_Hof_DisplayMon(u8 taskId) if (currMon->species == SPECIES_EGG) destY += 10; - spriteId = CreateMonPicSprite_Affine(currMon->species, currMon->tid, currMon->personality, MON_PIC_AFFINE_FRONT, startX, startY, currMonId, TAG_NONE); + spriteId = CreateMonPicSprite_Affine(currMon->species, currMon->isShiny, currMon->personality, MON_PIC_AFFINE_FRONT, startX, startY, currMonId, TAG_NONE); gSprites[spriteId].tDestinationX = destX; gSprites[spriteId].tDestinationY = destY; gSprites[spriteId].data[0] = 0; @@ -929,7 +933,7 @@ static void Task_HofPC_DrawSpritesPrintText(u8 taskId) if (currMon->species == SPECIES_EGG) posY += 10; - spriteId = CreateMonPicSprite(currMon->species, currMon->tid, currMon->personality, TRUE, posX, posY, i, TAG_NONE); + spriteId = CreateMonPicSprite(currMon->species, currMon->isShiny, currMon->personality, TRUE, posX, posY, i, TAG_NONE); gSprites[spriteId].oam.priority = 1; gTasks[taskId].tMonSpriteId(i) = spriteId; } diff --git a/src/item_use.c b/src/item_use.c index bce5d83ed6..344122e8b3 100644 --- a/src/item_use.c +++ b/src/item_use.c @@ -797,6 +797,12 @@ void ItemUseOutOfBattle_AbilityPatch(u8 taskId) SetUpItemUseCallback(taskId); } +void ItemUseOutOfBattle_Mint(u8 taskId) +{ + gItemUseCB = ItemUseCB_Mint; + SetUpItemUseCallback(taskId); +} + void ItemUseOutOfBattle_ResetEVs(u8 taskId) { gItemUseCB = ItemUseCB_ResetEVs; @@ -833,6 +839,12 @@ void ItemUseOutOfBattle_RareCandy(u8 taskId) SetUpItemUseCallback(taskId); } +void ItemUseOutOfBattle_DynamaxCandy(u8 taskId) +{ + gItemUseCB = ItemUseCB_DynamaxCandy; + SetUpItemUseCallback(taskId); +} + void ItemUseOutOfBattle_TMHM(u8 taskId) { if (gSpecialVar_ItemId >= ITEM_HM01) diff --git a/src/load_save.c b/src/load_save.c index d42b71a4a9..667f9e1c3c 100644 --- a/src/load_save.c +++ b/src/load_save.c @@ -175,7 +175,17 @@ void LoadPlayerParty(void) gPlayerPartyCount = gSaveBlock1Ptr->playerPartyCount; for (i = 0; i < PARTY_SIZE; i++) + { + u32 data; gPlayerParty[i] = gSaveBlock1Ptr->playerParty[i]; + + // TODO: Turn this into a save migration once those are available. + // At which point we can remove hp and status from Pokemon entirely. + data = gPlayerParty[i].maxHP - gPlayerParty[i].hp; + SetBoxMonData(&gPlayerParty[i].box, MON_DATA_HP_LOST, &data); + data = gPlayerParty[i].status; + SetBoxMonData(&gPlayerParty[i].box, MON_DATA_STATUS, &data); + } } void SaveObjectEvents(void) diff --git a/src/main_menu.c b/src/main_menu.c index c7295bd863..c1f092b2ca 100644 --- a/src/main_menu.c +++ b/src/main_menu.c @@ -1876,7 +1876,7 @@ static void SpriteCB_MovePlayerDownWhileShrinking(struct Sprite *sprite) static u8 NewGameBirchSpeech_CreateLotadSprite(u8 x, u8 y) { - return CreateMonPicSprite_Affine(SPECIES_LOTAD, SHINY_ODDS, 0, MON_PIC_AFFINE_FRONT, x, y, 14, TAG_NONE); + return CreateMonPicSprite_Affine(SPECIES_LOTAD, FALSE, 0, MON_PIC_AFFINE_FRONT, x, y, 14, TAG_NONE); } static void AddBirchSpeechObjects(u8 taskId) diff --git a/src/menu_specialized.c b/src/menu_specialized.c index c99c71e4b5..125a935666 100644 --- a/src/menu_specialized.c +++ b/src/menu_specialized.c @@ -1075,11 +1075,11 @@ void GetConditionMenuMonGfx(void *tilesDst, void *palDst, u16 boxId, u16 monId, if (partyId != numMons) { u16 species = GetBoxOrPartyMonData(boxId, monId, MON_DATA_SPECIES_OR_EGG, NULL); - u32 trainerId = GetBoxOrPartyMonData(boxId, monId, MON_DATA_OT_ID, NULL); + bool8 isShiny = GetBoxOrPartyMonData(boxId, monId, MON_DATA_IS_SHINY, NULL); u32 personality = GetBoxOrPartyMonData(boxId, monId, MON_DATA_PERSONALITY, NULL); LoadSpecialPokePic(tilesDst, species, personality, TRUE); - LZ77UnCompWram(GetMonSpritePalFromSpeciesAndPersonality(species, trainerId, personality), palDst); + LZ77UnCompWram(GetMonSpritePalFromSpeciesAndPersonality(species, isShiny, personality), palDst); } } diff --git a/src/party_menu.c b/src/party_menu.c index e973100586..eb276b30c6 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -4851,6 +4851,104 @@ void ItemUseCB_AbilityPatch(u8 taskId, TaskFunc task) #undef tMonId #undef tOldFunc +#define tState data[0] +#define tMonId data[1] +#define tOldNature data[2] +#define tNewNature data[3] +#define tOldFunc 4 + +void Task_Mint(u8 taskId) +{ + static const u8 askText[] = _("It might affect {STR_VAR_1}'s stats.\nAre you sure you want to use it?"); + static const u8 doneText[] = _("{STR_VAR_1}'s stats may have changed due\nto the effects of the {STR_VAR_2}!{PAUSE_UNTIL_PRESS}"); + s16 *data = gTasks[taskId].data; + + switch (tState) + { + case 0: + // Can't use. + if (tOldNature == tNewNature) + { + gPartyMenuUseExitCallback = FALSE; + PlaySE(SE_SELECT); + DisplayPartyMenuMessage(gText_WontHaveEffect, 1); + ScheduleBgCopyTilemapToVram(2); + gTasks[taskId].func = Task_ClosePartyMenuAfterText; + return; + } + gPartyMenuUseExitCallback = TRUE; + GetMonNickname(&gPlayerParty[tMonId], gStringVar1); + CopyItemName(gSpecialVar_ItemId, gStringVar2); + StringExpandPlaceholders(gStringVar4, askText); + PlaySE(SE_SELECT); + DisplayPartyMenuMessage(gStringVar4, 1); + ScheduleBgCopyTilemapToVram(2); + tState++; + break; + case 1: + if (!IsPartyMenuTextPrinterActive()) + { + PartyMenuDisplayYesNoMenu(); + tState++; + } + break; + case 2: + switch (Menu_ProcessInputNoWrapClearOnChoose()) + { + case 0: + tState++; + break; + case 1: + case MENU_B_PRESSED: + gPartyMenuUseExitCallback = FALSE; + PlaySE(SE_SELECT); + ScheduleBgCopyTilemapToVram(2); + // Don't exit party selections screen, return to choosing a mon. + ClearStdWindowAndFrameToTransparent(6, 0); + ClearWindowTilemap(6); + DisplayPartyMenuStdMessage(5); + gTasks[taskId].func = (void *)GetWordTaskArg(taskId, tOldFunc); + return; + } + break; + case 3: + PlaySE(SE_USE_ITEM); + StringExpandPlaceholders(gStringVar4, doneText); + DisplayPartyMenuMessage(gStringVar4, 1); + ScheduleBgCopyTilemapToVram(2); + tState++; + break; + case 4: + if (!IsPartyMenuTextPrinterActive()) + tState++; + break; + case 5: + SetMonData(&gPlayerParty[tMonId], MON_DATA_HIDDEN_NATURE, &tNewNature); + CalculateMonStats(&gPlayerParty[tMonId]); + RemoveBagItem(gSpecialVar_ItemId, 1); + gTasks[taskId].func = Task_ClosePartyMenu; + break; + } +} + +void ItemUseCB_Mint(u8 taskId, TaskFunc task) +{ + s16 *data = gTasks[taskId].data; + + tState = 0; + tMonId = gPartyMenu.slotId; + tOldNature = GetMonData(&gPlayerParty[tMonId], MON_DATA_HIDDEN_NATURE); + tNewNature = ItemId_GetSecondaryId(gSpecialVar_ItemId); + SetWordTaskArg(taskId, tOldFunc, (uintptr_t)(gTasks[taskId].func)); + gTasks[taskId].func = Task_Mint; +} + +#undef tState +#undef tMonId +#undef tOldNature +#undef tNewNature +#undef tOldFunc + static void Task_DisplayHPRestoredMessage(u8 taskId) { GetMonNickname(&gPlayerParty[gPartyMenu.slotId], gStringVar1); @@ -5672,6 +5770,70 @@ static void BufferMonStatsToTaskData(struct Pokemon *mon, s16 *data) data[3] = GetMonData(mon, MON_DATA_SPEED); } +#define tState data[0] +#define tMonId data[1] +#define tDynamaxLevel data[2] +#define tOldFunc 4 + +void Task_DynamaxCandy(u8 taskId) +{ + static const u8 doneText[] = _("{STR_VAR_1}'s Dynamax Level\nincreased by 1!{PAUSE_UNTIL_PRESS}"); + s16 *data = gTasks[taskId].data; + + switch (tState) + { + case 0: + // Can't use. + if (tDynamaxLevel == MAX_DYNAMAX_LEVEL) + { + gPartyMenuUseExitCallback = FALSE; + PlaySE(SE_SELECT); + DisplayPartyMenuMessage(gText_WontHaveEffect, 1); + ScheduleBgCopyTilemapToVram(2); + gTasks[taskId].func = Task_ClosePartyMenuAfterText; + return; + } + gPartyMenuUseExitCallback = TRUE; + GetMonNickname(&gPlayerParty[tMonId], gStringVar1); + CopyItemName(gSpecialVar_ItemId, gStringVar2); + tState++; + break; + case 1: + PlaySE(SE_USE_ITEM); + StringExpandPlaceholders(gStringVar4, doneText); + DisplayPartyMenuMessage(gStringVar4, 1); + ScheduleBgCopyTilemapToVram(2); + tState++; + break; + case 2: + if (!IsPartyMenuTextPrinterActive()) + tState++; + break; + case 3: + tDynamaxLevel++; + SetMonData(&gPlayerParty[tMonId], MON_DATA_DYNAMAX_LEVEL, &tDynamaxLevel); + RemoveBagItem(gSpecialVar_ItemId, 1); + gTasks[taskId].func = Task_ClosePartyMenu; + break; + } +} + +void ItemUseCB_DynamaxCandy(u8 taskId, TaskFunc task) +{ + s16 *data = gTasks[taskId].data; + + tState = 0; + tMonId = gPartyMenu.slotId; + tDynamaxLevel = GetMonData(&gPlayerParty[tMonId], MON_DATA_DYNAMAX_LEVEL); + SetWordTaskArg(taskId, tOldFunc, (uintptr_t)(gTasks[taskId].func)); + gTasks[taskId].func = Task_DynamaxCandy; +} + +#undef tState +#undef tMonId +#undef tDynamaxLevel +#undef tOldFunc + #define tUsedOnSlot data[0] #define tHadEffect data[1] #define tLastSlotUsed data[2] diff --git a/src/pokeblock_feed.c b/src/pokeblock_feed.c index c7ef8c9e90..61e1b7a6b3 100644 --- a/src/pokeblock_feed.c +++ b/src/pokeblock_feed.c @@ -718,7 +718,8 @@ static void HandleInitBackgrounds(void) static bool8 LoadMonAndSceneGfx(struct Pokemon *mon) { u16 species; - u32 personality, trainerId; + u32 personality; + bool32 isShiny; switch (sPokeblockFeed->loadGfxState) { @@ -733,8 +734,8 @@ static bool8 LoadMonAndSceneGfx(struct Pokemon *mon) // Load mon palette species = GetMonData(mon, MON_DATA_SPECIES_OR_EGG); personality = GetMonData(mon, MON_DATA_PERSONALITY); - trainerId = GetMonData(mon, MON_DATA_OT_ID); - LoadCompressedSpritePaletteWithTag(GetMonSpritePalFromSpeciesAndPersonality(species, trainerId, personality), species); + isShiny = GetMonData(mon, MON_DATA_IS_SHINY); + LoadCompressedSpritePaletteWithTag(GetMonSpritePalFromSpeciesAndPersonality(species, isShiny, personality), species); SetMultiuseSpriteTemplateToPokemon(species, B_POSITION_OPPONENT_LEFT); sPokeblockFeed->loadGfxState++; break; diff --git a/src/pokedex.c b/src/pokedex.c index 33b7dd02dd..ee3656da68 100644 --- a/src/pokedex.c +++ b/src/pokedex.c @@ -3971,12 +3971,11 @@ static void HighlightSubmenuScreenSelectBarItem(u8 a, u16 b) #define tSpecies data[1] #define tPalTimer data[2] #define tMonSpriteId data[3] -#define tOtIdLo data[12] -#define tOtIdHi data[13] +#define tIsShiny data[13] #define tPersonalityLo data[14] #define tPersonalityHi data[15] -u8 DisplayCaughtMonDexPage(u16 species, u32 otId, u32 personality) +u8 DisplayCaughtMonDexPage(u16 species, bool32 isShiny, u32 personality) { u8 taskId = 0; if (POKEDEX_PLUS_HGSS) @@ -3986,8 +3985,7 @@ u8 DisplayCaughtMonDexPage(u16 species, u32 otId, u32 personality) gTasks[taskId].tState = 0; gTasks[taskId].tSpecies = species; - gTasks[taskId].tOtIdLo = otId; - gTasks[taskId].tOtIdHi = otId >> 16; + gTasks[taskId].tIsShiny = isShiny; gTasks[taskId].tPersonalityLo = personality; gTasks[taskId].tPersonalityHi = personality >> 16; return taskId; @@ -4039,7 +4037,7 @@ static void Task_DisplayCaughtMonDexPage(u8 taskId) gTasks[taskId].tState++; break; case 4: - spriteId = CreateMonPicSprite(NationalPokedexNumToSpecies(dexNum), 0, ((u16)gTasks[taskId].tPersonalityHi << 16) | (u16)gTasks[taskId].tPersonalityLo, TRUE, MON_PAGE_X, MON_PAGE_Y, 0, TAG_NONE); + spriteId = CreateMonPicSprite(NationalPokedexNumToSpecies(dexNum), FALSE, ((u16)gTasks[taskId].tPersonalityHi << 16) | (u16)gTasks[taskId].tPersonalityLo, TRUE, MON_PAGE_X, MON_PAGE_Y, 0, TAG_NONE); gSprites[spriteId].oam.priority = 0; BeginNormalPaletteFade(PALETTES_ALL, 0, 0x10, 0, RGB_BLACK); SetVBlankCallback(gPokedexVBlankCB); @@ -4089,7 +4087,7 @@ static void Task_ExitCaughtMonPage(u8 taskId) { if (!gPaletteFade.active) { - u32 otId; + bool32 isShiny; u32 personality; u8 paletteNum; const u32 *lzPaletteData; @@ -4104,10 +4102,10 @@ static void Task_ExitCaughtMonPage(u8 taskId) if (buffer) Free(buffer); - otId = ((u16)gTasks[taskId].tOtIdHi << 16) | (u16)gTasks[taskId].tOtIdLo; + isShiny = (bool8)gTasks[taskId].tIsShiny; personality = ((u16)gTasks[taskId].tPersonalityHi << 16) | (u16)gTasks[taskId].tPersonalityLo; paletteNum = gSprites[gTasks[taskId].tMonSpriteId].oam.paletteNum; - lzPaletteData = GetMonSpritePalFromSpeciesAndPersonality(gTasks[taskId].tSpecies, otId, personality); + lzPaletteData = GetMonSpritePalFromSpeciesAndPersonality(gTasks[taskId].tSpecies, isShiny, personality); LoadCompressedPalette(lzPaletteData, OBJ_PLTT_ID(paletteNum), PLTT_SIZE_4BPP); DestroyTask(taskId); } @@ -4654,7 +4652,7 @@ static u32 GetPokedexMonPersonality(u16 species) u16 CreateMonSpriteFromNationalDexNumber(u16 nationalNum, s16 x, s16 y, u16 paletteSlot) { nationalNum = NationalPokedexNumToSpecies(nationalNum); - return CreateMonPicSprite(nationalNum, SHINY_ODDS, GetPokedexMonPersonality(nationalNum), TRUE, x, y, paletteSlot, TAG_NONE); + return CreateMonPicSprite(nationalNum, FALSE, GetPokedexMonPersonality(nationalNum), TRUE, x, y, paletteSlot, TAG_NONE); } static u16 GetPokemonScaleFromNationalDexNumber(u16 nationalNum) diff --git a/src/pokemon.c b/src/pokemon.c index f89a37c61e..89e3f59a45 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -673,6 +673,63 @@ static const struct SpriteTemplate sSpriteTemplate_64x64 = .callback = SpriteCallbackDummy, }; +// NOTE: Reordering this array will break compatibility with existing +// saves. +static const u32 sCompressedStatuses[] = +{ + STATUS1_NONE, + STATUS1_SLEEP_TURN(1), + STATUS1_SLEEP_TURN(2), + STATUS1_SLEEP_TURN(3), + STATUS1_SLEEP_TURN(4), + STATUS1_SLEEP_TURN(5), + STATUS1_POISON, + STATUS1_BURN, + STATUS1_FREEZE, + STATUS1_PARALYSIS, + STATUS1_TOXIC_POISON, + STATUS1_FROSTBITE, +}; + +// Attempt to detect situations where the BoxPokemon struct is unable to +// contain all the values. +// TODO: Is it possible to compute: +// - The maximum experience. +// - The maximum PP. +// - The maximum HP. +// - The maximum form countdown. +STATIC_ASSERT(NUM_SPECIES < (1 << 11), PokemonSubstruct0_species_TooSmall); +STATIC_ASSERT(NUMBER_OF_MON_TYPES + 1 <= (1 << 5), PokemonSubstruct0_teraType_TooSmall); +STATIC_ASSERT(ITEMS_COUNT < (1 << 10), PokemonSubstruct0_heldItem_TooSmall); +STATIC_ASSERT(MAX_LEVEL <= 100, PokemonSubstruct0_experience_PotentiallTooSmall); // Maximum of ~2 million exp. +STATIC_ASSERT(LAST_BALL < (1 << 6), PokemonSubstruct0_pokeball_TooSmall); +STATIC_ASSERT(MOVES_COUNT_ALL < (1 << 11), PokemonSubstruct1_moves_TooSmall); +STATIC_ASSERT(ARRAY_COUNT(sCompressedStatuses) <= (1 << 4), PokemonSubstruct3_compressedStatus_TooSmall); +STATIC_ASSERT(MAX_LEVEL < (1 << 7), PokemonSubstruct3_metLevel_TooSmall); +STATIC_ASSERT(NUM_VERSIONS < (1 << 4), PokemonSubstruct3_metGame_TooSmall); +STATIC_ASSERT(MAX_DYNAMAX_LEVEL < (1 << 4), PokemonSubstruct3_dynamaxLevel_TooSmall); +STATIC_ASSERT(MAX_PER_STAT_IVS < (1 << 5), PokemonSubstruct3_ivs_TooSmall); +STATIC_ASSERT(NUM_NATURES <= (1 << 5), BoxPokemon_hiddenNatureModifier_TooSmall); + +static u32 CompressStatus(u32 status) +{ + s32 i; + for (i = 0; i < ARRAY_COUNT(sCompressedStatuses); i++) + { + if (sCompressedStatuses[i] == status) + return i; + } + return 0; // STATUS1_NONE +} + +static u32 UncompressStatus(u32 compressedStatus) +{ + if (compressedStatus < ARRAY_COUNT(sCompressedStatuses)) + return sCompressedStatuses[compressedStatus]; + else + return STATUS1_NONE; +} + void ZeroBoxMonData(struct BoxPokemon *boxMon) { u8 *raw = (u8 *)boxMon; @@ -733,6 +790,7 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, u8 i; u8 availableIVs[NUM_STATS]; u8 selectedIvs[LEGENDARY_PERFECT_IV_COUNT]; + bool32 isShiny; ZeroBoxMonData(boxMon); @@ -744,17 +802,13 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, // Determine original trainer ID if (otIdType == OT_ID_RANDOM_NO_SHINY) { - u32 shinyValue; - do - { - // Choose random OT IDs until one that results in a non-shiny Pokémon - value = Random32(); - shinyValue = GET_SHINY_VALUE(value, personality); - } while (shinyValue < SHINY_ODDS); + value = Random32(); + isShiny = FALSE; } else if (otIdType == OT_ID_PRESET) { value = fixedOtId; + isShiny = GET_SHINY_VALUE(value, personality) < SHINY_ODDS; } else // Player is the OT { @@ -763,26 +817,15 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, | (gSaveBlock2Ptr->playerTrainerId[2] << 16) | (gSaveBlock2Ptr->playerTrainerId[3] << 24); -#if P_FLAG_FORCE_NO_SHINY != 0 - if (FlagGet(P_FLAG_FORCE_NO_SHINY)) + if (P_FLAG_FORCE_NO_SHINY != 0 && FlagGet(P_FLAG_FORCE_NO_SHINY)) { - while (GET_SHINY_VALUE(value, personality) < SHINY_ODDS) - personality = Random32(); + isShiny = FALSE; } -#endif -#if P_FLAG_FORCE_SHINY != 0 - #if P_FLAG_FORCE_NO_SHINY != 0 - else - #endif - if (FlagGet(P_FLAG_FORCE_SHINY)) + else if (P_FLAG_FORCE_SHINY != 0 && FlagGet(P_FLAG_FORCE_SHINY)) { - while (GET_SHINY_VALUE(value, personality) >= SHINY_ODDS) - personality = Random32(); + isShiny = TRUE; } -#endif -#if P_FLAG_FORCE_SHINY != 0 || P_FLAG_FORCE_NO_SHINY != 0 else -#endif { u32 totalRerolls = 0; if (CheckBagHasItem(ITEM_SHINY_CHARM, 1)) @@ -795,6 +838,8 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, personality = Random32(); totalRerolls--; } + + isShiny = GET_SHINY_VALUE(value, personality) < SHINY_ODDS; } } @@ -804,6 +849,7 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, checksum = CalculateBoxMonChecksum(boxMon); SetBoxMonData(boxMon, MON_DATA_CHECKSUM, &checksum); EncryptBoxMon(boxMon); + SetBoxMonData(boxMon, MON_DATA_IS_SHINY, &isShiny); StringCopy(speciesName, GetSpeciesName(species)); SetBoxMonData(boxMon, MON_DATA_NICKNAME, speciesName); SetBoxMonData(boxMon, MON_DATA_LANGUAGE, &gGameLanguage); @@ -1382,7 +1428,6 @@ static u16 CalculateBoxMonChecksum(struct BoxPokemon *boxMon) { \ u8 baseStat = gSpeciesInfo[species].base; \ s32 n = (((2 * baseStat + iv + ev / 4) * level) / 100) + 5; \ - u8 nature = GetNature(mon); \ n = ModifyStatByNature(nature, n, statIndex); \ if (B_FRIENDSHIP_BOOST == TRUE) \ n = n + ((n * 10 * friendship) / (MAX_FRIENDSHIP * 100));\ @@ -1393,23 +1438,25 @@ void CalculateMonStats(struct Pokemon *mon) { s32 oldMaxHP = GetMonData(mon, MON_DATA_MAX_HP, NULL); s32 currentHP = GetMonData(mon, MON_DATA_HP, NULL); - s32 hpIV = GetMonData(mon, MON_DATA_HP_IV, NULL); + s32 hpIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_HP) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_HP_IV, NULL); s32 hpEV = GetMonData(mon, MON_DATA_HP_EV, NULL); - s32 attackIV = GetMonData(mon, MON_DATA_ATK_IV, NULL); + s32 attackIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_ATK) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_ATK_IV, NULL); s32 attackEV = GetMonData(mon, MON_DATA_ATK_EV, NULL); - s32 defenseIV = GetMonData(mon, MON_DATA_DEF_IV, NULL); + s32 defenseIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_DEF) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_DEF_IV, NULL); s32 defenseEV = GetMonData(mon, MON_DATA_DEF_EV, NULL); - s32 speedIV = GetMonData(mon, MON_DATA_SPEED_IV, NULL); + s32 speedIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_SPEED) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_SPEED_IV, NULL); s32 speedEV = GetMonData(mon, MON_DATA_SPEED_EV, NULL); - s32 spAttackIV = GetMonData(mon, MON_DATA_SPATK_IV, NULL); + s32 spAttackIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_SPATK) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_SPATK_IV, NULL); s32 spAttackEV = GetMonData(mon, MON_DATA_SPATK_EV, NULL); - s32 spDefenseIV = GetMonData(mon, MON_DATA_SPDEF_IV, NULL); + s32 spDefenseIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_SPDEF) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_SPDEF_IV, NULL); s32 spDefenseEV = GetMonData(mon, MON_DATA_SPDEF_EV, NULL); u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL); u8 friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL); s32 level = GetLevelFromMonExp(mon); s32 newMaxHP; + u8 nature = GetMonData(mon, MON_DATA_HIDDEN_NATURE, NULL); + SetMonData(mon, MON_DATA_LEVEL, &level); if (species == SPECIES_SHEDINJA) @@ -1465,12 +1512,15 @@ void BoxMonToMon(const struct BoxPokemon *src, struct Pokemon *dest) { u32 value = 0; dest->box = *src; - SetMonData(dest, MON_DATA_STATUS, &value); - SetMonData(dest, MON_DATA_HP, &value); - SetMonData(dest, MON_DATA_MAX_HP, &value); + dest->status = GetBoxMonData(&dest->box, MON_DATA_STATUS, NULL); + dest->hp = 0; + dest->maxHP = 0; value = MAIL_NONE; SetMonData(dest, MON_DATA_MAIL, &value); + value = GetBoxMonData(&dest->box, MON_DATA_HP_LOST); CalculateMonStats(dest); + value = GetMonData(dest, MON_DATA_MAX_HP) - value; + SetMonData(dest, MON_DATA_HP, &value); } u8 GetLevelFromMonExp(struct Pokemon *mon) @@ -2044,6 +2094,76 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) switch (field) { + case MON_DATA_NICKNAME: + { + if (boxMon->isBadEgg) + { + for (retVal = 0; + retVal < POKEMON_NAME_LENGTH && gText_BadEgg[retVal] != EOS; + data[retVal] = gText_BadEgg[retVal], retVal++) {} + + data[retVal] = EOS; + } + else if (boxMon->isEgg) + { + StringCopy(data, gText_EggNickname); + retVal = StringLength(data); + } + else if (boxMon->language == LANGUAGE_JAPANESE) + { + data[0] = EXT_CTRL_CODE_BEGIN; + data[1] = EXT_CTRL_CODE_JPN; + + for (retVal = 2, i = 0; + i < 5 && boxMon->nickname[i] != EOS; + data[retVal] = boxMon->nickname[i], retVal++, i++) {} + + data[retVal++] = EXT_CTRL_CODE_BEGIN; + data[retVal++] = EXT_CTRL_CODE_ENG; + data[retVal] = EOS; + } + else + { + retVal = 0; + while (retVal < min(sizeof(boxMon->nickname), POKEMON_NAME_LENGTH)) + { + data[retVal] = boxMon->nickname[retVal]; + retVal++; + } + + // Vanilla Pokémon have 0s in nickname11 and nickname12 + // so if both are 0 we assume that this is a vanilla + // Pokémon and replace them with EOS. This means that + // two CHAR_SPACE at the end of a nickname are trimmed. + if (POKEMON_NAME_LENGTH >= 12) + { + if (substruct0->nickname11 == 0 && substruct0->nickname12 == 0) + { + data[retVal++] = EOS; + data[retVal++] = EOS; + } + else + { + data[retVal++] = substruct0->nickname11; + data[retVal++] = substruct0->nickname12; + } + } + else if (POKEMON_NAME_LENGTH >= 11) + { + if (substruct0->nickname11 == 0) + { + data[retVal++] = EOS; + } + else + { + data[retVal++] = substruct0->nickname11; + } + } + + data[retVal] = EOS; + } + break; + } case MON_DATA_SPECIES: retVal = boxMon->isBadEgg ? SPECIES_EGG : substruct0->species; break; @@ -2060,16 +2180,28 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) retVal = substruct0->friendship; break; case MON_DATA_MOVE1: + retVal = substruct1->move1; + break; case MON_DATA_MOVE2: + retVal = substruct1->move2; + break; case MON_DATA_MOVE3: + retVal = substruct1->move3; + break; case MON_DATA_MOVE4: - retVal = substruct1->moves[field - MON_DATA_MOVE1]; + retVal = substruct1->move4; break; case MON_DATA_PP1: + retVal = substruct1->pp1; + break; case MON_DATA_PP2: + retVal = substruct1->pp2; + break; case MON_DATA_PP3: + retVal = substruct1->pp3; + break; case MON_DATA_PP4: - retVal = substruct1->pp[field - MON_DATA_PP1]; + retVal = substruct1->pp4; break; case MON_DATA_HP_EV: retVal = substruct2->hpEV; @@ -2200,9 +2332,6 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) case MON_DATA_WORLD_RIBBON: retVal = substruct3->worldRibbon; break; - case MON_DATA_UNUSED_RIBBONS: - retVal = substruct3->unusedRibbons; - break; case MON_DATA_MODERN_FATEFUL_ENCOUNTER: retVal = substruct3->modernFatefulEncounter; break; @@ -2228,10 +2357,10 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) while (moves[i] != MOVES_COUNT) { u16 move = moves[i]; - if (substruct1->moves[0] == move - || substruct1->moves[1] == move - || substruct1->moves[2] == move - || substruct1->moves[3] == move) + if (substruct1->move1 == move + || substruct1->move2 == move + || substruct1->move3 == move + || substruct1->move4 == move) retVal |= gBitTable[i]; i++; } @@ -2283,6 +2412,46 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) | (substruct3->worldRibbon << 26); } break; + case MON_DATA_HYPER_TRAINED_HP: + retVal = substruct1->hyperTrainedHP; + break; + case MON_DATA_HYPER_TRAINED_ATK: + retVal = substruct1->hyperTrainedAttack; + break; + case MON_DATA_HYPER_TRAINED_DEF: + retVal = substruct1->hyperTrainedDefense; + break; + case MON_DATA_HYPER_TRAINED_SPEED: + retVal = substruct1->hyperTrainedSpeed; + break; + case MON_DATA_HYPER_TRAINED_SPATK: + retVal = substruct1->hyperTrainedSpAttack; + break; + case MON_DATA_HYPER_TRAINED_SPDEF: + retVal = substruct1->hyperTrainedSpDefense; + break; + case MON_DATA_IS_SHADOW: + retVal = substruct3->isShadow; + break; + case MON_DATA_DYNAMAX_LEVEL: + retVal = substruct3->dynamaxLevel; + break; + case MON_DATA_GIGANTAMAX_FACTOR: + retVal = substruct3->gigantamaxFactor; + break; + case MON_DATA_TERA_TYPE: + { + if (substruct0->teraType == 0) + { + const u8 *types = gSpeciesInfo[substruct0->species].types; + retVal = (boxMon->personality & 0x1) == 0 ? types[0] : types[1]; + } + else + { + retVal = substruct0->teraType - 1; + } + break; + } default: break; } @@ -2291,50 +2460,18 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) { switch (field) { + case MON_DATA_STATUS: + retVal = UncompressStatus(boxMon->compressedStatus); + break; + case MON_DATA_HP_LOST: + retVal = boxMon->hpLost; + break; case MON_DATA_PERSONALITY: retVal = boxMon->personality; break; case MON_DATA_OT_ID: retVal = boxMon->otId; break; - case MON_DATA_NICKNAME: - { - if (boxMon->isBadEgg) - { - for (retVal = 0; - retVal < POKEMON_NAME_LENGTH && gText_BadEgg[retVal] != EOS; - data[retVal] = gText_BadEgg[retVal], retVal++) {} - - data[retVal] = EOS; - } - else if (boxMon->isEgg) - { - StringCopy(data, gText_EggNickname); - retVal = StringLength(data); - } - else if (boxMon->language == LANGUAGE_JAPANESE) - { - data[0] = EXT_CTRL_CODE_BEGIN; - data[1] = EXT_CTRL_CODE_JPN; - - for (retVal = 2, i = 0; - i < 5 && boxMon->nickname[i] != EOS; - data[retVal] = boxMon->nickname[i], retVal++, i++) {} - - data[retVal++] = EXT_CTRL_CODE_BEGIN; - data[retVal++] = EXT_CTRL_CODE_ENG; - data[retVal] = EOS; - } - else - { - for (retVal = 0; - retVal < POKEMON_NAME_LENGTH; - data[retVal] = boxMon->nickname[retVal], retVal++){} - - data[retVal] = EOS; - } - break; - } case MON_DATA_LANGUAGE: retVal = boxMon->language; break; @@ -2366,9 +2503,18 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) case MON_DATA_CHECKSUM: retVal = boxMon->checksum; break; - case MON_DATA_ENCRYPT_SEPARATOR: - retVal = boxMon->unknown; + case MON_DATA_IS_SHINY: + { + u32 shinyValue = GET_SHINY_VALUE(boxMon->otId, boxMon->personality); + retVal = (shinyValue < SHINY_ODDS) ^ boxMon->shinyModifier; break; + } + case MON_DATA_HIDDEN_NATURE: + { + u32 nature = GetNatureFromPersonality(boxMon->personality); + retVal = nature ^ boxMon->hiddenNatureModifier; + break; + } default: break; } @@ -2397,13 +2543,27 @@ void SetMonData(struct Pokemon *mon, s32 field, const void *dataArg) { case MON_DATA_STATUS: SET32(mon->status); + SetBoxMonData(&mon->box, MON_DATA_STATUS, dataArg); break; case MON_DATA_LEVEL: SET8(mon->level); break; case MON_DATA_HP: + { + u32 hpLost; SET16(mon->hp); + hpLost = mon->maxHP - mon->hp; + SetBoxMonData(&mon->box, MON_DATA_HP_LOST, &hpLost); break; + } + case MON_DATA_HP_LOST: + { + u32 hpLost; + SET16(hpLost); + mon->hp = mon->maxHP - hpLost; + SetBoxMonData(&mon->box, MON_DATA_HP_LOST, &hpLost); + break; + } case MON_DATA_MAX_HP: SET16(mon->maxHP); break; @@ -2462,6 +2622,17 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg) switch (field) { + case MON_DATA_NICKNAME: + { + s32 i; + for (i = 0; i < min(sizeof(boxMon->nickname), POKEMON_NAME_LENGTH); i++) + boxMon->nickname[i] = data[i]; + if (POKEMON_NAME_LENGTH >= 11) + substruct0->nickname11 = data[10]; + if (POKEMON_NAME_LENGTH >= 12) + substruct0->nickname12 = data[11]; + break; + } case MON_DATA_SPECIES: { SET16(substruct0->species); @@ -2484,16 +2655,28 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg) SET8(substruct0->friendship); break; case MON_DATA_MOVE1: + SET16(substruct1->move1); + break; case MON_DATA_MOVE2: + SET16(substruct1->move2); + break; case MON_DATA_MOVE3: + SET16(substruct1->move3); + break; case MON_DATA_MOVE4: - SET16(substruct1->moves[field - MON_DATA_MOVE1]); + SET16(substruct1->move4); break; case MON_DATA_PP1: + SET8(substruct1->pp1); + break; case MON_DATA_PP2: + SET8(substruct1->pp2); + break; case MON_DATA_PP3: + SET8(substruct1->pp3); + break; case MON_DATA_PP4: - SET8(substruct1->pp[field - MON_DATA_PP1]); + SET8(substruct1->pp4); break; case MON_DATA_HP_EV: SET8(substruct2->hpEV); @@ -2634,9 +2817,6 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg) case MON_DATA_WORLD_RIBBON: SET8(substruct3->worldRibbon); break; - case MON_DATA_UNUSED_RIBBONS: - SET8(substruct3->unusedRibbons); - break; case MON_DATA_MODERN_FATEFUL_ENCOUNTER: SET8(substruct3->modernFatefulEncounter); break; @@ -2651,6 +2831,40 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg) substruct3->spDefenseIV = (ivs >> 25) & MAX_IV_MASK; break; } + case MON_DATA_HYPER_TRAINED_HP: + SET8(substruct1->hyperTrainedHP); + break; + case MON_DATA_HYPER_TRAINED_ATK: + SET8(substruct1->hyperTrainedAttack); + break; + case MON_DATA_HYPER_TRAINED_DEF: + SET8(substruct1->hyperTrainedDefense); + break; + case MON_DATA_HYPER_TRAINED_SPEED: + SET8(substruct1->hyperTrainedSpeed); + break; + case MON_DATA_HYPER_TRAINED_SPATK: + SET8(substruct1->hyperTrainedSpAttack); + break; + case MON_DATA_HYPER_TRAINED_SPDEF: + SET8(substruct1->hyperTrainedSpDefense); + break; + case MON_DATA_IS_SHADOW: + SET8(substruct3->isShadow); + break; + case MON_DATA_DYNAMAX_LEVEL: + SET8(substruct3->dynamaxLevel); + break; + case MON_DATA_GIGANTAMAX_FACTOR: + SET8(substruct3->gigantamaxFactor); + break; + case MON_DATA_TERA_TYPE: + { + u32 teraType; + SET8(teraType); + substruct0->teraType = 1 + teraType; + break; + } default: break; } @@ -2659,19 +2873,22 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg) { switch (field) { + case MON_DATA_STATUS: + { + u32 status; + SET32(status); + boxMon->compressedStatus = CompressStatus(status); + break; + } + case MON_DATA_HP_LOST: + SET16(boxMon->hpLost); + break; case MON_DATA_PERSONALITY: SET32(boxMon->personality); break; case MON_DATA_OT_ID: SET32(boxMon->otId); break; - case MON_DATA_NICKNAME: - { - s32 i; - for (i = 0; i < POKEMON_NAME_LENGTH; i++) - boxMon->nickname[i] = data[i]; - break; - } case MON_DATA_LANGUAGE: SET8(boxMon->language); break; @@ -2697,10 +2914,23 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg) case MON_DATA_CHECKSUM: SET16(boxMon->checksum); break; - case MON_DATA_ENCRYPT_SEPARATOR: - SET16(boxMon->unknown); + case MON_DATA_IS_SHINY: + { + u32 shinyValue = GET_SHINY_VALUE(boxMon->otId, boxMon->personality); + bool32 isShiny; + SET8(isShiny); + boxMon->shinyModifier = (shinyValue < SHINY_ODDS) ^ isShiny; break; } + case MON_DATA_HIDDEN_NATURE: + { + u32 nature = GetNatureFromPersonality(boxMon->personality); + u32 hiddenNature; + SET8(hiddenNature); + boxMon->hiddenNatureModifier = nature ^ hiddenNature; + break; + } + } } if (field > MON_DATA_ENCRYPT_SEPARATOR) @@ -3764,6 +3994,18 @@ u8 GetNatureFromPersonality(u32 personality) return personality % NUM_NATURES; } +static u32 GetGMaxTargetSpecies(u32 species) +{ + const struct FormChange *formChanges = GetSpeciesFormChanges(species); + u32 i; + for (i = 0; formChanges[i].method != FORM_CHANGE_TERMINATOR; i++) + { + if (formChanges[i].method == FORM_CHANGE_BATTLE_GIGANTAMAX) + return formChanges[i].targetSpecies; + } + return SPECIES_NONE; +} + u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 mode, u16 evolutionItem, struct Pokemon *tradePartner) { int i, j; @@ -3776,6 +4018,7 @@ u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 mode, u16 evolutionItem, s u8 beauty = GetMonData(mon, MON_DATA_BEAUTY, 0); u16 upperPersonality = personality >> 16; u32 holdEffect, currentMap, partnerSpecies, partnerHeldItem, partnerHoldEffect; + bool32 consumeItem = FALSE; const struct Evolution *evolutions = GetSpeciesEvolutions(species); if (evolutions == NULL) @@ -3848,17 +4091,15 @@ u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 mode, u16 evolutionItem, s case EVO_ITEM_HOLD_NIGHT: if (GetTimeOfDay() == TIME_NIGHT && heldItem == evolutions[i].param) { - heldItem = ITEM_NONE; - SetMonData(mon, MON_DATA_HELD_ITEM, &heldItem); targetSpecies = evolutions[i].targetSpecies; + consumeItem = TRUE; } break; case EVO_ITEM_HOLD_DAY: if (GetTimeOfDay() != TIME_NIGHT && heldItem == evolutions[i].param) { - heldItem = ITEM_NONE; - SetMonData(mon, MON_DATA_HELD_ITEM, &heldItem); targetSpecies = evolutions[i].targetSpecies; + consumeItem = TRUE; } break; case EVO_LEVEL_DUSK: @@ -4037,9 +4278,8 @@ u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 mode, u16 evolutionItem, s case EVO_ITEM_HOLD: if (heldItem == evolutions[i].param) { - heldItem = 0; - SetMonData(mon, MON_DATA_HELD_ITEM, &heldItem); targetSpecies = evolutions[i].targetSpecies; + consumeItem = TRUE; } break; } @@ -4059,9 +4299,8 @@ u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 mode, u16 evolutionItem, s case EVO_TRADE_ITEM: if (evolutions[i].param == heldItem) { - heldItem = ITEM_NONE; - SetMonData(mon, MON_DATA_HELD_ITEM, &heldItem); targetSpecies = evolutions[i].targetSpecies; + consumeItem = TRUE; } break; case EVO_TRADE_SPECIFIC_MON: @@ -4150,6 +4389,22 @@ u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 mode, u16 evolutionItem, s break; } + // Pikachu, Meowth, and Eevee cannot evolve if they have the + // Gigantamax Factor. We assume that is because their evolutions + // do not have a Gigantamax Form. + if (GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR, NULL) + && GetGMaxTargetSpecies(species) != SPECIES_NONE + && GetGMaxTargetSpecies(targetSpecies) == SPECIES_NONE) + { + return SPECIES_NONE; + } + + if (consumeItem) + { + heldItem = ITEM_NONE; + SetMonData(mon, MON_DATA_HELD_ITEM, &heldItem); + } + return targetSpecies; } @@ -5067,20 +5322,17 @@ static void Task_PlayMapChosenOrBattleBGM(u8 taskId) const u32 *GetMonFrontSpritePal(struct Pokemon *mon) { - u16 species = GetMonData(mon, MON_DATA_SPECIES_OR_EGG, 0); - u32 otId = GetMonData(mon, MON_DATA_OT_ID, 0); - u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, 0); - return GetMonSpritePalFromSpeciesAndPersonality(species, otId, personality); + u16 species = GetMonData(mon, MON_DATA_SPECIES_OR_EGG, NULL); + bool32 isShiny = GetMonData(mon, MON_DATA_IS_SHINY, NULL); + u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, NULL); + return GetMonSpritePalFromSpeciesAndPersonality(species, isShiny, personality); } -const u32 *GetMonSpritePalFromSpeciesAndPersonality(u16 species, u32 otId, u32 personality) +const u32 *GetMonSpritePalFromSpeciesAndPersonality(u16 species, bool32 isShiny, u32 personality) { - u32 shinyValue; - species = SanitizeSpeciesId(species); - shinyValue = GET_SHINY_VALUE(otId, personality); - if (shinyValue < SHINY_ODDS) + if (isShiny) { if (gSpeciesInfo[species].shinyPaletteFemale != NULL && IsPersonalityFemale(species, personality)) return gSpeciesInfo[species].shinyPaletteFemale; @@ -5278,18 +5530,7 @@ void SetWildMonHeldItem(void) bool8 IsMonShiny(struct Pokemon *mon) { - u32 otId = GetMonData(mon, MON_DATA_OT_ID, 0); - u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, 0); - return IsShinyOtIdPersonality(otId, personality); -} - -bool8 IsShinyOtIdPersonality(u32 otId, u32 personality) -{ - bool8 retVal = FALSE; - u32 shinyValue = GET_SHINY_VALUE(otId, personality); - if (shinyValue < SHINY_ODDS) - retVal = TRUE; - return retVal; + return GetMonData(mon, MON_DATA_IS_SHINY, NULL); } const u8 *GetTrainerPartnerName(void) @@ -6072,6 +6313,10 @@ void UpdateMonPersonality(struct BoxPokemon *boxMon, u32 personality) struct PokemonSubstruct3 *old3, *new3; struct BoxPokemon old; + bool32 isShiny = GetBoxMonData(boxMon, MON_DATA_IS_SHINY, NULL); + u32 hiddenNature = GetBoxMonData(boxMon, MON_DATA_HIDDEN_NATURE, NULL); + u32 teraType = GetBoxMonData(boxMon, MON_DATA_TERA_TYPE, NULL); + old = *boxMon; old0 = &(GetSubstruct(&old, old.personality, 0)->type0); old1 = &(GetSubstruct(&old, old.personality, 1)->type1); @@ -6091,6 +6336,36 @@ void UpdateMonPersonality(struct BoxPokemon *boxMon, u32 personality) *new3 = *old3; boxMon->checksum = CalculateBoxMonChecksum(boxMon); EncryptBoxMon(boxMon); + + SetBoxMonData(boxMon, MON_DATA_IS_SHINY, &isShiny); + SetBoxMonData(boxMon, MON_DATA_HIDDEN_NATURE, &hiddenNature); + SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, &teraType); +} + +void HealPokemon(struct Pokemon *mon) +{ + u32 data; + + data = GetMonData(mon, MON_DATA_MAX_HP); + SetMonData(mon, MON_DATA_HP, &data); + + data = STATUS1_NONE; + SetMonData(mon, MON_DATA_STATUS, &data); + + MonRestorePP(mon); +} + +void HealBoxPokemon(struct BoxPokemon *boxMon) +{ + u32 data; + + data = 0; + SetBoxMonData(boxMon, MON_DATA_HP_LOST, &data); + + data = STATUS1_NONE; + SetBoxMonData(boxMon, MON_DATA_STATUS, &data); + + BoxMonRestorePP(boxMon); } u16 GetCryIdBySpecies(u16 species) diff --git a/src/pokemon_jump.c b/src/pokemon_jump.c index 6ea897c0a6..ecdcbf26d2 100755 --- a/src/pokemon_jump.c +++ b/src/pokemon_jump.c @@ -157,7 +157,8 @@ enum { struct PokemonJump_MonInfo { - u16 species; + u16 isShiny:1; + u16 species:15; u32 otId; u32 personality; }; @@ -894,6 +895,7 @@ static void InitJumpMonInfo(struct PokemonJump_MonInfo *monInfo, struct Pokemon { monInfo->species = GetMonData(mon, MON_DATA_SPECIES); monInfo->otId = GetMonData(mon, MON_DATA_OT_ID); + monInfo->isShiny = GetMonData(mon, MON_DATA_IS_SHINY); monInfo->personality = GetMonData(mon, MON_DATA_PERSONALITY); } @@ -2970,7 +2972,7 @@ static void CreateJumpMonSprite(struct PokemonJumpGfx *jumpGfx, struct PokemonJu spriteSheet.size = MON_PIC_SIZE; LoadSpriteSheet(&spriteSheet); - spritePalette.data = GetMonSpritePalFromSpeciesAndPersonality(monInfo->species, monInfo->otId, monInfo->personality); + spritePalette.data = GetMonSpritePalFromSpeciesAndPersonality(monInfo->species, monInfo->isShiny, monInfo->personality); spritePalette.tag = multiplayerId; LoadCompressedSpritePalette(&spritePalette); @@ -4161,7 +4163,8 @@ static void Task_UpdateBonus(u8 taskId) struct MonInfoPacket { u8 id; - u16 species; + u16 isShiny:1; + u16 species:15; u32 personality; u32 otId; }; @@ -4170,6 +4173,7 @@ static void SendPacket_MonInfo(struct PokemonJump_MonInfo *monInfo) { struct MonInfoPacket packet; packet.id = PACKET_MON_INFO, + packet.isShiny = monInfo->isShiny, packet.species = monInfo->species, packet.otId = monInfo->otId, packet.personality = monInfo->personality, @@ -4187,6 +4191,7 @@ static bool32 RecvPacket_MonInfo(int multiplayerId, struct PokemonJump_MonInfo * if (packet.id == PACKET_MON_INFO) { monInfo->species = packet.species; + monInfo->isShiny = packet.isShiny; monInfo->otId = packet.otId; monInfo->personality = packet.personality; return TRUE; diff --git a/src/pokemon_storage_system.c b/src/pokemon_storage_system.c index 817ec51543..95a8387066 100644 --- a/src/pokemon_storage_system.c +++ b/src/pokemon_storage_system.c @@ -6414,15 +6414,13 @@ static void SetMovingMonData(u8 boxId, u8 position) static void SetPlacedMonData(u8 boxId, u8 position) { + if (OW_PC_HEAL <= GEN_7) + HealPokemon(&sStorage->movingMon); + if (boxId == TOTAL_BOXES_COUNT) - { gPlayerParty[position] = sStorage->movingMon; - } else - { - BoxMonRestorePP(&sStorage->movingMon.box); SetBoxMonAt(boxId, position, &sStorage->movingMon.box); - } } static void PurgeMonOrBoxMon(u8 boxId, u8 position) @@ -6964,7 +6962,7 @@ static void SetDisplayMonData(void *pokemon, u8 mode) sStorage->displayMonSpecies = GetBoxMonData(pokemon, MON_DATA_SPECIES_OR_EGG); if (sStorage->displayMonSpecies != SPECIES_NONE) { - u32 otId = GetBoxMonData(boxMon, MON_DATA_OT_ID); + bool8 isShiny = GetBoxMonData(boxMon, MON_DATA_IS_SHINY); sanityIsBadEgg = GetBoxMonData(boxMon, MON_DATA_SANITY_IS_BAD_EGG); if (sanityIsBadEgg) sStorage->displayMonIsEgg = TRUE; @@ -6977,7 +6975,7 @@ static void SetDisplayMonData(void *pokemon, u8 mode) sStorage->displayMonLevel = GetLevelFromBoxMonExp(boxMon); sStorage->displayMonMarkings = GetBoxMonData(boxMon, MON_DATA_MARKINGS); sStorage->displayMonPersonality = GetBoxMonData(boxMon, MON_DATA_PERSONALITY); - sStorage->displayMonPalette = GetMonSpritePalFromSpeciesAndPersonality(sStorage->displayMonSpecies, otId, sStorage->displayMonPersonality); + sStorage->displayMonPalette = GetMonSpritePalFromSpeciesAndPersonality(sStorage->displayMonSpecies, isShiny, sStorage->displayMonPersonality); gender = GetGenderFromSpeciesAndPersonality(sStorage->displayMonSpecies, sStorage->displayMonPersonality); sStorage->displayMonItemId = GetBoxMonData(boxMon, MON_DATA_HELD_ITEM); } @@ -8653,6 +8651,8 @@ static void MultiMove_SetPlacedMonData(void) u8 boxPosition = (IN_BOX_COLUMNS * i) + sMultiMove->minColumn; for (j = sMultiMove->minColumn; j < columnCount; j++) { + if (OW_PC_HEAL <= GEN_7) + HealBoxPokemon(&sMultiMove->boxMons[monArrayId]); if (GetBoxMonData(&sMultiMove->boxMons[monArrayId], MON_DATA_SANITY_HAS_SPECIES)) SetBoxMonAt(boxId, boxPosition, &sMultiMove->boxMons[monArrayId]); boxPosition++; @@ -10147,12 +10147,12 @@ static void UnkUtil_DmaRun(struct UnkUtilData *data) void UpdateSpeciesSpritePSS(struct BoxPokemon *boxMon) { u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES); - u32 otId = GetBoxMonData(boxMon, MON_DATA_OT_ID); + bool8 isShiny = GetBoxMonData(boxMon, MON_DATA_IS_SHINY); u32 pid = GetBoxMonData(boxMon, MON_DATA_PERSONALITY); // Update front sprite sStorage->displayMonSpecies = species; - sStorage->displayMonPalette = GetMonSpritePalFromSpeciesAndPersonality(species, otId, pid); + sStorage->displayMonPalette = GetMonSpritePalFromSpeciesAndPersonality(species, isShiny, pid); if (!sJustOpenedBag) { LoadDisplayMonGfx(species, pid); diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index 2ea61ba730..7ccc605b68 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -141,7 +141,9 @@ static EWRAM_DATA struct PokemonSummaryScreenData { u16 species; // 0x0 u16 species2; // 0x2 - u8 isEgg; // 0x4 + u8 isEgg:1; // 0x4 + u8 isShiny:1; + u8 padding:6; u8 level; // 0x5 u8 ribbonCount; // 0x6 u8 ailment; // 0x7 @@ -168,6 +170,7 @@ static EWRAM_DATA struct PokemonSummaryScreenData u8 sanity; // 0x35 u8 OTName[17]; // 0x36 u32 OTID; // 0x48 + u8 teraType; } summary; u16 bgTilemapBuffers[PSS_PAGE_COUNT][2][0x400]; u8 mode; @@ -1541,6 +1544,8 @@ static bool8 ExtractMonDataToSummaryStruct(struct Pokemon *mon) break; default: sum->ribbonCount = GetMonData(mon, MON_DATA_RIBBON_COUNT); + sum->teraType = GetMonData(mon, MON_DATA_TERA_TYPE); + sum->isShiny = GetMonData(mon, MON_DATA_IS_SHINY); return TRUE; } sMonSummaryScreen->switchCounter++; @@ -3938,6 +3943,10 @@ static void SetMonTypeIcons(void) { SetSpriteInvisibility(SPRITE_ARR_ID_TYPE + 1, TRUE); } + if (P_SHOW_TERA_TYPE >= GEN_9) + { + SetTypeSpritePosAndPal(summary->teraType, 200, 48, SPRITE_ARR_ID_TYPE + 2); + } } } @@ -4042,7 +4051,7 @@ static u8 LoadMonGfxAndSprite(struct Pokemon *mon, s16 *state) (*state)++; return 0xFF; case 1: - LoadCompressedSpritePaletteWithTag(GetMonSpritePalFromSpeciesAndPersonality(summary->species2, summary->OTID, summary->pid), summary->species2); + LoadCompressedSpritePaletteWithTag(GetMonSpritePalFromSpeciesAndPersonality(summary->species2, summary->isShiny, summary->pid), summary->species2); SetMultiuseSpriteTemplateToPokemon(summary->species2, B_POSITION_OPPONENT_LEFT); (*state)++; return 0xFF; diff --git a/src/pokenav_conditions.c b/src/pokenav_conditions.c index 3f662e6f3e..19cfe38b95 100644 --- a/src/pokenav_conditions.c +++ b/src/pokenav_conditions.c @@ -522,7 +522,8 @@ static void GetMonConditionGraphData(s16 listId, u8 loadId) static void ConditionGraphDrawMonPic(s16 listId, u8 loadId) { u16 boxId, monId, species; - u32 personality, tid; + u32 personality; + bool8 isShiny; struct Pokenav_ConditionMenu *menu = GetSubstructPtr(POKENAV_SUBSTRUCT_CONDITION_GRAPH_MENU); struct PokenavMonList *monListPtr = GetSubstructPtr(POKENAV_SUBSTRUCT_MON_LIST); @@ -532,10 +533,10 @@ static void ConditionGraphDrawMonPic(s16 listId, u8 loadId) boxId = monListPtr->monData[listId].boxId; monId = monListPtr->monData[listId].monId; species = GetBoxOrPartyMonData(boxId, monId, MON_DATA_SPECIES_OR_EGG, NULL); - tid = GetBoxOrPartyMonData(boxId, monId, MON_DATA_OT_ID, NULL); + isShiny = GetBoxOrPartyMonData(boxId, monId, MON_DATA_IS_SHINY, NULL); personality = GetBoxOrPartyMonData(boxId, monId, MON_DATA_PERSONALITY, NULL); LoadSpecialPokePic(menu->monPicGfx[loadId], species, personality, TRUE); - LZ77UnCompWram(GetMonSpritePalFromSpeciesAndPersonality(species, tid, personality), menu->monPal[loadId]); + LZ77UnCompWram(GetMonSpritePalFromSpeciesAndPersonality(species, isShiny, personality), menu->monPal[loadId]); } u16 GetMonListCount(void) diff --git a/src/pokenav_ribbons_summary.c b/src/pokenav_ribbons_summary.c index f2e589e56d..1ff55dc187 100644 --- a/src/pokenav_ribbons_summary.c +++ b/src/pokenav_ribbons_summary.c @@ -401,7 +401,7 @@ static void GetMonNicknameLevelGender(u8 *nick, u8 *level, u8 *gender) StringGet_Nickname(nick); } -static void GetMonSpeciesPersonalityOtId(u16 *species, u32 *personality, u32 *otId) +static void GetMonSpeciesPersonalityShiny(u16 *species, u32 *personality, bool8 *isShiny) { struct Pokenav_RibbonsSummaryList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_LIST); struct PokenavMonList *mons = list->monList; @@ -413,7 +413,7 @@ static void GetMonSpeciesPersonalityOtId(u16 *species, u32 *personality, u32 *ot struct Pokemon *mon = &gPlayerParty[monInfo->monId]; *species = GetMonData(mon, MON_DATA_SPECIES); *personality = GetMonData(mon, MON_DATA_PERSONALITY); - *otId = GetMonData(mon, MON_DATA_OT_ID); + *isShiny = GetMonData(mon, MON_DATA_IS_SHINY); } else { @@ -421,7 +421,7 @@ static void GetMonSpeciesPersonalityOtId(u16 *species, u32 *personality, u32 *ot struct BoxPokemon *boxMon = GetBoxedMonPtr(monInfo->boxId, monInfo->monId); *species = GetBoxMonData(boxMon, MON_DATA_SPECIES); *personality = GetBoxMonData(boxMon, MON_DATA_PERSONALITY); - *otId = GetBoxMonData(boxMon, MON_DATA_OT_ID); + *isShiny = GetBoxMonData(boxMon, MON_DATA_IS_SHINY); } } @@ -941,9 +941,10 @@ static void PrintRibbonsMonListIndex(struct Pokenav_RibbonsSummaryMenu *menu) static void ResetSpritesAndDrawMonFrontPic(struct Pokenav_RibbonsSummaryMenu *menu) { u16 species; - u32 personality, otId; + u32 personality; + bool8 isShiny; - GetMonSpeciesPersonalityOtId(&species, &personality, &otId); + GetMonSpeciesPersonalityShiny(&species, &personality, &isShiny); ResetAllPicSprites(); menu->monSpriteId = DrawRibbonsMonFrontPic(MON_SPRITE_X_ON, MON_SPRITE_Y); PokenavFillPalette(15, 0); @@ -960,10 +961,11 @@ static void DestroyRibbonsMonFrontPic(struct Pokenav_RibbonsSummaryMenu *menu) static u16 DrawRibbonsMonFrontPic(s32 x, s32 y) { u16 species, spriteId; - u32 personality, otId; + u32 personality; + bool8 isShiny; - GetMonSpeciesPersonalityOtId(&species, &personality, &otId); - spriteId = CreateMonPicSprite(species, otId, personality, TRUE, MON_SPRITE_X_ON, MON_SPRITE_Y, 15, TAG_NONE); + GetMonSpeciesPersonalityShiny(&species, &personality, &isShiny); + spriteId = CreateMonPicSprite(species, isShiny, personality, TRUE, MON_SPRITE_X_ON, MON_SPRITE_Y, 15, TAG_NONE); gSprites[spriteId].oam.priority = 0; return spriteId; } diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index ca455d544d..04448b6f6c 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -16,6 +16,7 @@ #include "party_menu.h" #include "pokedex.h" #include "pokemon.h" +#include "pokemon_storage_system.h" #include "random.h" #include "script.h" #include "sprite.h" @@ -27,35 +28,30 @@ static void CB2_ReturnFromChooseHalfParty(void); static void CB2_ReturnFromChooseBattleFrontierParty(void); +static void HealPlayerBoxes(void); void HealPlayerParty(void) { - u8 i, j; - u8 ppBonuses; - u8 arg[4]; + u32 i; + for (i = 0; i < gPlayerPartyCount; i++) + HealPokemon(&gPlayerParty[i]); + if (OW_PC_HEAL >= GEN_8) + HealPlayerBoxes(); +} - // restore HP. - for(i = 0; i < gPlayerPartyCount; i++) +static void HealPlayerBoxes(void) +{ + int boxId, boxPosition; + struct BoxPokemon *boxMon; + + for (boxId = 0; boxId < TOTAL_BOXES_COUNT; boxId++) { - u16 maxHP = GetMonData(&gPlayerParty[i], MON_DATA_MAX_HP); - arg[0] = maxHP; - arg[1] = maxHP >> 8; - SetMonData(&gPlayerParty[i], MON_DATA_HP, arg); - ppBonuses = GetMonData(&gPlayerParty[i], MON_DATA_PP_BONUSES); - - // restore PP. - for(j = 0; j < MAX_MON_MOVES; j++) + for (boxPosition = 0; boxPosition < IN_BOX_COUNT; boxPosition++) { - arg[0] = CalculatePPWithBonus(GetMonData(&gPlayerParty[i], MON_DATA_MOVE1 + j), ppBonuses, j); - SetMonData(&gPlayerParty[i], MON_DATA_PP1 + j, arg); + boxMon = &gPokemonStoragePtr->boxes[boxId][boxPosition]; + if (GetBoxMonData(boxMon, MON_DATA_SANITY_HAS_SPECIES)) + HealBoxPokemon(boxMon); } - - // since status is u32, the four 0 assignments here are probably for safety to prevent undefined data from reaching SetMonData. - arg[0] = 0; - arg[1] = 0; - arg[2] = 0; - arg[3] = 0; - SetMonData(&gPlayerParty[i], MON_DATA_STATUS, arg); } } @@ -273,3 +269,72 @@ void ReducePlayerPartyToSelectedMons(void) CalculatePlayerPartyCount(); } + +void CanHyperTrain(struct ScriptContext *ctx) +{ + u32 stat = ScriptReadByte(ctx); + u32 partyIndex = VarGet(ScriptReadHalfword(ctx)); + if (stat < NUM_STATS + && partyIndex < PARTY_SIZE + && !GetMonData(&gPlayerParty[partyIndex], MON_DATA_HYPER_TRAINED_HP + stat) + && GetMonData(&gPlayerParty[partyIndex], MON_DATA_HP_IV + stat) < MAX_PER_STAT_IVS) + { + gSpecialVar_Result = TRUE; + } + else + { + gSpecialVar_Result = FALSE; + } +} + +void HyperTrain(struct ScriptContext *ctx) +{ + u32 stat = ScriptReadByte(ctx); + u32 partyIndex = VarGet(ScriptReadHalfword(ctx)); + if (stat < NUM_STATS && partyIndex < PARTY_SIZE) + { + bool32 data = TRUE; + SetMonData(&gPlayerParty[partyIndex], MON_DATA_HYPER_TRAINED_HP + stat, &data); + CalculateMonStats(&gPlayerParty[partyIndex]); + } +} + +void HasGigantamaxFactor(struct ScriptContext *ctx) +{ + u32 partyIndex = VarGet(ScriptReadHalfword(ctx)); + if (partyIndex < PARTY_SIZE) + gSpecialVar_Result = GetMonData(&gPlayerParty[partyIndex], MON_DATA_GIGANTAMAX_FACTOR); + else + gSpecialVar_Result = FALSE; +} + +static const u16 sGigantaxFactorLockedSpecies[] = +{ + SPECIES_MELMETAL, +}; + +void ToggleGigantamaxFactor(struct ScriptContext *ctx) +{ + u32 i; + u32 partyIndex = VarGet(ScriptReadHalfword(ctx)); + u32 species; + + gSpecialVar_Result = FALSE; + + if (partyIndex < PARTY_SIZE) + { + bool32 gigantamaxFactor; + + species = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPECIES); + for (i = 0; i < ARRAY_COUNT(sGigantaxFactorLockedSpecies); i++) + { + if (species == sGigantaxFactorLockedSpecies[i]) + return; + } + + gigantamaxFactor = GetMonData(&gPlayerParty[partyIndex], MON_DATA_GIGANTAMAX_FACTOR); + gigantamaxFactor = !gigantamaxFactor; + SetMonData(&gPlayerParty[partyIndex], MON_DATA_GIGANTAMAX_FACTOR, &gigantamaxFactor); + gSpecialVar_Result = TRUE; + } +} diff --git a/src/starter_choose.c b/src/starter_choose.c index 3097f87f73..f1821fe04d 100644 --- a/src/starter_choose.c +++ b/src/starter_choose.c @@ -630,7 +630,7 @@ static u8 CreatePokemonFrontSprite(u16 species, u8 x, u8 y) { u8 spriteId; - spriteId = CreateMonPicSprite_Affine(species, SHINY_ODDS, 0, MON_PIC_AFFINE_FRONT, x, y, 14, TAG_NONE); + spriteId = CreateMonPicSprite_Affine(species, FALSE, 0, MON_PIC_AFFINE_FRONT, x, y, 14, TAG_NONE); gSprites[spriteId].oam.priority = 0; return spriteId; } diff --git a/src/trade.c b/src/trade.c index 5e0564a1ff..0d7765857f 100644 --- a/src/trade.c +++ b/src/trade.c @@ -4842,7 +4842,7 @@ static void CheckPartnersMonForRibbons(void) { u8 i; u8 numRibbons = 0; - for (i = 0; i < (MON_DATA_UNUSED_RIBBONS - MON_DATA_CHAMPION_RIBBON); i++) + for (i = 0; i < (MON_DATA_WORLD_RIBBON - MON_DATA_CHAMPION_RIBBON + 1); i++) numRibbons += GetMonData(&gEnemyParty[gSelectedTradeMonPositions[TRADE_PARTNER] % PARTY_SIZE], MON_DATA_CHAMPION_RIBBON + i); if (numRibbons != 0) diff --git a/src/trainer_pokemon_sprites.c b/src/trainer_pokemon_sprites.c index 6ffbc836d7..0d1954ab71 100644 --- a/src/trainer_pokemon_sprites.c +++ b/src/trainer_pokemon_sprites.c @@ -73,19 +73,19 @@ static bool16 DecompressPic(u16 species, u32 personality, bool8 isFrontPic, u8 * return FALSE; } -static void LoadPicPaletteByTagOrSlot(u16 species, u32 otId, u32 personality, u8 paletteSlot, u16 paletteTag, bool8 isTrainer) +static void LoadPicPaletteByTagOrSlot(u16 species, bool8 isShiny, u32 personality, u8 paletteSlot, u16 paletteTag, bool8 isTrainer) { if (!isTrainer) { if (paletteTag == TAG_NONE) { sCreatingSpriteTemplate.paletteTag = TAG_NONE; - LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(species, otId, personality), OBJ_PLTT_ID(paletteSlot), PLTT_SIZE_4BPP); + LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(species, isShiny, personality), OBJ_PLTT_ID(paletteSlot), PLTT_SIZE_4BPP); } else { sCreatingSpriteTemplate.paletteTag = paletteTag; - LoadCompressedSpritePaletteWithTag(GetMonSpritePalFromSpeciesAndPersonality(species, otId, personality), species); + LoadCompressedSpritePaletteWithTag(GetMonSpritePalFromSpeciesAndPersonality(species, isShiny, personality), species); } } else @@ -103,10 +103,10 @@ static void LoadPicPaletteByTagOrSlot(u16 species, u32 otId, u32 personality, u8 } } -static void LoadPicPaletteBySlot(u16 species, u32 otId, u32 personality, u8 paletteSlot, bool8 isTrainer) +static void LoadPicPaletteBySlot(u16 species, bool8 isShiny, u32 personality, u8 paletteSlot, bool8 isTrainer) { if (!isTrainer) - LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(species, otId, personality), PLTT_ID(paletteSlot), PLTT_SIZE_4BPP); + LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(species, isShiny, personality), PLTT_ID(paletteSlot), PLTT_SIZE_4BPP); else LoadCompressedPalette(gTrainerSprites[species].palette.data, PLTT_ID(paletteSlot), PLTT_SIZE_4BPP); } @@ -119,7 +119,7 @@ static void AssignSpriteAnimsTable(bool8 isTrainer) sCreatingSpriteTemplate.anims = gTrainerSprites[0].animation; } -static u16 CreatePicSprite(u16 species, u32 otId, u32 personality, bool8 isFrontPic, s16 x, s16 y, u8 paletteSlot, u16 paletteTag, bool8 isTrainer) +static u16 CreatePicSprite(u16 species, bool8 isShiny, u32 personality, bool8 isFrontPic, s16 x, s16 y, u8 paletteSlot, u16 paletteTag, bool8 isTrainer) { u8 i; u8 *framePics; @@ -161,7 +161,7 @@ static u16 CreatePicSprite(u16 species, u32 otId, u32 personality, bool8 isFront sCreatingSpriteTemplate.images = images; sCreatingSpriteTemplate.affineAnims = gDummySpriteAffineAnimTable; sCreatingSpriteTemplate.callback = DummyPicSpriteCallback; - LoadPicPaletteByTagOrSlot(species, otId, personality, paletteSlot, paletteTag, isTrainer); + LoadPicPaletteByTagOrSlot(species, isShiny, personality, paletteSlot, paletteTag, isTrainer); spriteId = CreateSprite(&sCreatingSpriteTemplate, x, y, 0); if (paletteTag == TAG_NONE) gSprites[spriteId].oam.paletteNum = paletteSlot; @@ -173,7 +173,7 @@ static u16 CreatePicSprite(u16 species, u32 otId, u32 personality, bool8 isFront return spriteId; } -u16 CreateMonPicSprite_Affine(u16 species, u32 otId, u32 personality, u8 flags, s16 x, s16 y, u8 paletteSlot, u16 paletteTag) +u16 CreateMonPicSprite_Affine(u16 species, bool8 isShiny, u32 personality, u8 flags, s16 x, s16 y, u8 paletteSlot, u16 paletteTag) { u8 *framePics; struct SpriteFrameImage *images; @@ -239,7 +239,7 @@ u16 CreateMonPicSprite_Affine(u16 species, u32 otId, u32 personality, u8 flags, sCreatingSpriteTemplate.affineAnims = gDummySpriteAffineAnimTable; } sCreatingSpriteTemplate.callback = DummyPicSpriteCallback; - LoadPicPaletteByTagOrSlot(species, otId, personality, paletteSlot, paletteTag, FALSE); + LoadPicPaletteByTagOrSlot(species, isShiny, personality, paletteSlot, paletteTag, FALSE); spriteId = CreateSprite(&sCreatingSpriteTemplate, x, y, 0); if (paletteTag == TAG_NONE) gSprites[spriteId].oam.paletteNum = paletteSlot; @@ -276,16 +276,16 @@ static u16 FreeAndDestroyPicSpriteInternal(u16 spriteId) return 0; } -static u16 LoadPicSpriteInWindow(u16 species, u32 otId, u32 personality, bool8 isFrontPic, u8 paletteSlot, u8 windowId, bool8 isTrainer) +static u16 LoadPicSpriteInWindow(u16 species, bool8 isShiny, u32 personality, bool8 isFrontPic, u8 paletteSlot, u8 windowId, bool8 isTrainer) { if (DecompressPic(species, personality, isFrontPic, (u8 *)GetWindowAttribute(windowId, WINDOW_TILE_DATA), FALSE)) return 0xFFFF; - LoadPicPaletteBySlot(species, otId, personality, paletteSlot, isTrainer); + LoadPicPaletteBySlot(species, isShiny, personality, paletteSlot, isTrainer); return 0; } -static u16 CreateTrainerCardSprite(u16 species, u32 otId, u32 personality, bool8 isFrontPic, u16 destX, u16 destY, u8 paletteSlot, u8 windowId, bool8 isTrainer) +static u16 CreateTrainerCardSprite(u16 species, bool8 isShiny, u32 personality, bool8 isFrontPic, u16 destX, u16 destY, u8 paletteSlot, u8 windowId, bool8 isTrainer) { u8 *framePics; @@ -293,16 +293,16 @@ static u16 CreateTrainerCardSprite(u16 species, u32 otId, u32 personality, bool8 if (framePics && !DecompressPic(species, personality, isFrontPic, framePics, isTrainer)) { BlitBitmapRectToWindow(windowId, framePics, 0, 0, TRAINER_PIC_WIDTH, TRAINER_PIC_HEIGHT, destX, destY, TRAINER_PIC_WIDTH, TRAINER_PIC_HEIGHT); - LoadPicPaletteBySlot(species, otId, personality, paletteSlot, isTrainer); + LoadPicPaletteBySlot(species, isShiny, personality, paletteSlot, isTrainer); Free(framePics); return 0; } return 0xFFFF; } -u16 CreateMonPicSprite(u16 species, u32 otId, u32 personality, bool8 isFrontPic, s16 x, s16 y, u8 paletteSlot, u16 paletteTag) +u16 CreateMonPicSprite(u16 species, bool8 isShiny, u32 personality, bool8 isFrontPic, s16 x, s16 y, u8 paletteSlot, u16 paletteTag) { - return CreatePicSprite(species, otId, personality, isFrontPic, x, y, paletteSlot, paletteTag, FALSE); + return CreatePicSprite(species, isShiny, personality, isFrontPic, x, y, paletteSlot, paletteTag, FALSE); } u16 FreeAndDestroyMonPicSprite(u16 spriteId) @@ -310,20 +310,20 @@ u16 FreeAndDestroyMonPicSprite(u16 spriteId) return FreeAndDestroyPicSpriteInternal(spriteId); } -static u16 UNUSED LoadMonPicInWindow(u16 species, u32 otId, u32 personality, bool8 isFrontPic, u8 paletteSlot, u8 windowId) +static u16 UNUSED LoadMonPicInWindow(u16 species, bool8 isShiny, u32 personality, bool8 isFrontPic, u8 paletteSlot, u8 windowId) { - return LoadPicSpriteInWindow(species, otId, personality, isFrontPic, paletteSlot, windowId, FALSE); + return LoadPicSpriteInWindow(species, isShiny, personality, isFrontPic, paletteSlot, windowId, FALSE); } // Unused, FRLG only -u16 CreateTrainerCardMonIconSprite(u16 species, u32 otId, u32 personality, bool8 isFrontPic, u16 destX, u16 destY, u8 paletteSlot, u8 windowId) +u16 CreateTrainerCardMonIconSprite(u16 species, bool8 isShiny, u32 personality, bool8 isFrontPic, u16 destX, u16 destY, u8 paletteSlot, u8 windowId) { - return CreateTrainerCardSprite(species, otId, personality, isFrontPic, destX, destY, paletteSlot, windowId, FALSE); + return CreateTrainerCardSprite(species, isShiny, personality, isFrontPic, destX, destY, paletteSlot, windowId, FALSE); } u16 CreateTrainerPicSprite(u16 species, bool8 isFrontPic, s16 x, s16 y, u8 paletteSlot, u16 paletteTag) { - return CreatePicSprite(species, 0, 0, isFrontPic, x, y, paletteSlot, paletteTag, TRUE); + return CreatePicSprite(species, FALSE, 0, isFrontPic, x, y, paletteSlot, paletteTag, TRUE); } u16 FreeAndDestroyTrainerPicSprite(u16 spriteId) @@ -333,12 +333,12 @@ u16 FreeAndDestroyTrainerPicSprite(u16 spriteId) static u16 UNUSED LoadTrainerPicInWindow(u16 species, bool8 isFrontPic, u8 paletteSlot, u8 windowId) { - return LoadPicSpriteInWindow(species, 0, 0, isFrontPic, paletteSlot, windowId, TRUE); + return LoadPicSpriteInWindow(species, FALSE, 0, isFrontPic, paletteSlot, windowId, TRUE); } u16 CreateTrainerCardTrainerPicSprite(u16 species, bool8 isFrontPic, u16 destX, u16 destY, u8 paletteSlot, u8 windowId) { - return CreateTrainerCardSprite(species, 0, 0, isFrontPic, destX, destY, paletteSlot, windowId, TRUE); + return CreateTrainerCardSprite(species, FALSE, 0, isFrontPic, destX, destY, paletteSlot, windowId, TRUE); } u16 PlayerGenderToFrontTrainerPicId_Debug(u8 gender, bool8 getClass) diff --git a/test/battle/trainer_control.c b/test/battle/trainer_control.c index 810a721fa5..e3e071745d 100644 --- a/test/battle/trainer_control.c +++ b/test/battle/trainer_control.c @@ -27,7 +27,8 @@ static const struct TrainerMon sTestParty1[] = .lvl = 67, .moves = {MOVE_AIR_SLASH, MOVE_BARRIER, MOVE_SOLAR_BEAM, MOVE_EXPLOSION}, .nature = TRAINER_PARTY_NATURE(NATURE_HASTY), - .nickname = COMPOUND_STRING("Bubbles") + .nickname = COMPOUND_STRING("Bubbles"), + .dynamaxLevel = 5, }, { .species = SPECIES_WOBBUFFET, @@ -110,6 +111,9 @@ TEST("CreateNPCTrainerPartyForTrainer generates customized Pokémon") EXPECT(GetMonGender(&testParty[0]) == MON_FEMALE); EXPECT(GetNature(&testParty[0]) == NATURE_HASTY); + EXPECT_EQ(GetMonData(&testParty[0], MON_DATA_DYNAMAX_LEVEL), 5); + EXPECT_EQ(GetMonData(&testParty[1], MON_DATA_DYNAMAX_LEVEL), 0); + Free(testParty); } diff --git a/test/dynamax.c b/test/dynamax.c index 69887d6d2f..0d6ee72172 100644 --- a/test/dynamax.c +++ b/test/dynamax.c @@ -465,16 +465,19 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon are immune to Instruct") } } -// TODO: Gigantamax factor SINGLE_BATTLE_TEST("(DYNAMAX) Pokemon with Gigantamax forms change upon Dynamaxing") { + u32 species; + bool32 gigantamaxFactor; + PARAMETRIZE { gigantamaxFactor = FALSE; species = SPECIES_VENUSAUR; } + PARAMETRIZE { gigantamaxFactor = TRUE; species = SPECIES_VENUSAUR_GIGANTAMAX; } GIVEN { - PLAYER(SPECIES_VENUSAUR); + PLAYER(SPECIES_VENUSAUR) { GigantamaxFactor(gigantamaxFactor); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); } } THEN { - EXPECT_EQ(player->species, SPECIES_VENUSAUR_GIGANTAMAX); + EXPECT_EQ(player->species, species); } } @@ -870,7 +873,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) G-Max Stonesurge sets up Stealth Rocks") { GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_STONESURGE].argument == MAX_EFFECT_STEALTH_ROCK); - PLAYER(SPECIES_DREDNAW); + PLAYER(SPECIES_DREDNAW) { GigantamaxFactor(TRUE); } OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -890,7 +893,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) G-Max Steelsurge sets up sharp steel") { GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_STEELSURGE].argument == MAX_EFFECT_STEELSURGE); - PLAYER(SPECIES_COPPERAJAH); + PLAYER(SPECIES_COPPERAJAH) { GigantamaxFactor(TRUE); } OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_HATTERENE); } WHEN { @@ -921,7 +924,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) G-Max Hydrosnipe has fixed power and ignores abili PARAMETRIZE { move = MOVE_HYDRO_CANNON; } GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_HYDROSNIPE].argument == MAX_EFFECT_FIXED_POWER); - PLAYER(SPECIES_INTELEON); + PLAYER(SPECIES_INTELEON) { GigantamaxFactor(TRUE); } OPPONENT(SPECIES_ARCTOVISH) { Ability(ABILITY_WATER_ABSORB); } } WHEN { TURN { MOVE(player, move, dynamax: TRUE); } @@ -937,7 +940,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Volt Crash paralyzes both opponents") { GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_VOLT_CRASH].argument == MAX_EFFECT_PARALYZE_FOES); - PLAYER(SPECIES_PIKACHU); + PLAYER(SPECIES_PIKACHU) { GigantamaxFactor(TRUE); } PLAYER(SPECIES_PICHU); OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WYNAUT); @@ -964,7 +967,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Stun Shock paralyzes or poisons both opponen PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = STATUS1_POISON; } GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_STUN_SHOCK].argument == MAX_EFFECT_POISON_PARALYZE_FOES); - PLAYER(SPECIES_TOXTRICITY); + PLAYER(SPECIES_TOXTRICITY) { GigantamaxFactor(TRUE); } PLAYER(SPECIES_TOXEL); OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WYNAUT); @@ -1001,7 +1004,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Stun Shock chooses statuses before consideri { GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_STUN_SHOCK].argument == MAX_EFFECT_POISON_PARALYZE_FOES); - PLAYER(SPECIES_TOXTRICITY); + PLAYER(SPECIES_TOXTRICITY) { GigantamaxFactor(TRUE); } PLAYER(SPECIES_TOXEL); OPPONENT(SPECIES_GARBODOR); OPPONENT(SPECIES_TRUBBISH); @@ -1034,7 +1037,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Befuddle paralyzes, poisons, or sleeps both PARAMETRIZE { statusAnim = B_ANIM_STATUS_SLP; rng = STATUS1_SLEEP; } GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_BEFUDDLE].argument == MAX_EFFECT_EFFECT_SPORE_FOES); - PLAYER(SPECIES_BUTTERFREE); + PLAYER(SPECIES_BUTTERFREE) { GigantamaxFactor(TRUE); } PLAYER(SPECIES_CATERPIE); OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -1078,7 +1081,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Gold Rush confuses both opponents and genera { GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_GOLD_RUSH].argument == MAX_EFFECT_CONFUSE_FOES_PAY_DAY); - PLAYER(SPECIES_MEOWTH); + PLAYER(SPECIES_MEOWTH) { GigantamaxFactor(TRUE); } PLAYER(SPECIES_PERSIAN); OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -1098,7 +1101,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Smite confuses both opponents") { GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_SMITE].argument == MAX_EFFECT_CONFUSE_FOES); - PLAYER(SPECIES_HATTERENE); + PLAYER(SPECIES_HATTERENE) { GigantamaxFactor(TRUE); } PLAYER(SPECIES_HATENNA); OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -1117,7 +1120,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Cuddle infatuates both opponents, if possibl { GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_CUDDLE].argument == MAX_EFFECT_INFATUATE_FOES); - PLAYER(SPECIES_EEVEE) { Gender(MON_MALE); } + PLAYER(SPECIES_EEVEE) { Gender(MON_MALE); GigantamaxFactor(TRUE); } PLAYER(SPECIES_EEVEE); OPPONENT(SPECIES_WOBBUFFET) { Gender(MON_FEMALE); } OPPONENT(SPECIES_WOBBUFFET) { Gender(MON_MALE); } @@ -1138,7 +1141,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Terror traps both opponents") { GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_TERROR].argument == MAX_EFFECT_MEAN_LOOK); - PLAYER(SPECIES_GENGAR); + PLAYER(SPECIES_GENGAR) { GigantamaxFactor(TRUE); } PLAYER(SPECIES_GASTLY); OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -1157,7 +1160,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Meltdown torments both opponents for 3 turns { GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_MELTDOWN].argument == MAX_EFFECT_TORMENT_FOES); - PLAYER(SPECIES_MELMETAL); + PLAYER(SPECIES_MELMETAL) { GigantamaxFactor(TRUE); } PLAYER(SPECIES_MELTAN); OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SPLASH, MOVE_CELEBRATE); } OPPONENT(SPECIES_WYNAUT) { Moves(MOVE_SPLASH, MOVE_CELEBRATE); } @@ -1194,7 +1197,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Wildfire sets a field effect that damages no s16 damage; GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_WILDFIRE].argument == MAX_EFFECT_WILDFIRE); - PLAYER(SPECIES_CHARIZARD); + PLAYER(SPECIES_CHARIZARD) { GigantamaxFactor(TRUE); } PLAYER(SPECIES_CHARMANDER); OPPONENT(SPECIES_WOBBUFFET) { HP(600); MaxHP(600); } OPPONENT(SPECIES_WYNAUT); @@ -1240,7 +1243,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Replenish recycles allies' berries 50\% of t PASSES_RANDOMLY(1, 2, RNG_G_MAX_REPLENISH); GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_REPLENISH].argument == MAX_EFFECT_RECYCLE_BERRIES); - PLAYER(SPECIES_SNORLAX) { Item(ITEM_APICOT_BERRY); } + PLAYER(SPECIES_SNORLAX) { Item(ITEM_APICOT_BERRY); GigantamaxFactor(TRUE); } PLAYER(SPECIES_MUNCHLAX) { Item(ITEM_APICOT_BERRY); } OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_APICOT_BERRY); } OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_APICOT_BERRY); } @@ -1268,7 +1271,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Snooze makes only the target drowsy") PASSES_RANDOMLY(1, 2, RNG_G_MAX_SNOOZE); GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_SNOOZE].argument == MAX_EFFECT_YAWN_FOE); - PLAYER(SPECIES_GRIMMSNARL); + PLAYER(SPECIES_GRIMMSNARL) { GigantamaxFactor(TRUE); } PLAYER(SPECIES_IMPIDIMP); OPPONENT(SPECIES_BLISSEY); OPPONENT(SPECIES_CHANSEY); @@ -1291,7 +1294,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Finale heals allies by 1/6 of their health") s16 damage1, damage2; GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_FINALE].argument == MAX_EFFECT_HEAL_TEAM); - PLAYER(SPECIES_ALCREMIE) { HP(1); } + PLAYER(SPECIES_ALCREMIE) { HP(1); GigantamaxFactor(TRUE); } PLAYER(SPECIES_MILCERY) { HP(1); } OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -1311,7 +1314,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Sweetness cures allies' status conditions") { GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_SWEETNESS].argument == MAX_EFFECT_AROMATHERAPY); - PLAYER(SPECIES_APPLETUN) { Status1(STATUS1_POISON); } + PLAYER(SPECIES_APPLETUN) { Status1(STATUS1_POISON); GigantamaxFactor(TRUE); } PLAYER(SPECIES_APPLIN) { Status1(STATUS1_POISON); } OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -1331,7 +1334,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Centiferno traps both opponents in Fire Spin { GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_CENTIFERNO].argument == MAX_EFFECT_FIRE_SPIN_FOES); - PLAYER(SPECIES_CENTISKORCH); + PLAYER(SPECIES_CENTISKORCH) { GigantamaxFactor(TRUE); } PLAYER(SPECIES_SIZZLIPEDE); PLAYER(SPECIES_SIZZLIPEDE); OPPONENT(SPECIES_WOBBUFFET); @@ -1360,7 +1363,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Chi Strike boosts allies' crit chance") GIVEN { ASSUME(B_CRIT_CHANCE >= GEN_6); ASSUME(gBattleMoves[MOVE_G_MAX_CHI_STRIKE].argument == MAX_EFFECT_CRIT_PLUS); - PLAYER(SPECIES_MACHAMP); + PLAYER(SPECIES_MACHAMP) { GigantamaxFactor(TRUE); } PLAYER(SPECIES_MACHOP); OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -1389,7 +1392,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Depletion takes away 2 PP from the target's { GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_DEPLETION].argument == MAX_EFFECT_SPITE); - PLAYER(SPECIES_DURALUDON); + PLAYER(SPECIES_DURALUDON) { GigantamaxFactor(TRUE); } PLAYER(SPECIES_WYNAUT); // Dynamax behaves weird with test turn order because stats are recalculated. OPPONENT(SPECIES_SABLEYE) { Ability(ABILITY_PRANKSTER); } @@ -1411,7 +1414,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max One Blow bypasses Max Guard for full damage" PARAMETRIZE { protect = FALSE; } GIVEN { ASSUME(gBattleMoves[MOVE_G_MAX_ONE_BLOW].argument == MAX_EFFECT_BYPASS_PROTECT); - PLAYER(SPECIES_URSHIFU); + PLAYER(SPECIES_URSHIFU) { GigantamaxFactor(TRUE); } PLAYER(SPECIES_KUBFU); OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WYNAUT); diff --git a/test/pokemon.c b/test/pokemon.c new file mode 100644 index 0000000000..05d6ac15e5 --- /dev/null +++ b/test/pokemon.c @@ -0,0 +1,182 @@ +#include "global.h" +#include "battle.h" +#include "event_data.h" +#include "pokemon.h" +#include "test/overworld_script.h" +#include "test/test.h" + +TEST("Nature independent from Hidden Nature") +{ + u32 i, j, nature = 0, hiddenNature = 0; + struct Pokemon mon; + for (i = 0; i < NUM_NATURES; i++) + { + for (j = 0; j < NUM_NATURES; j++) + { + PARAMETRIZE { nature = i; hiddenNature = j; } + } + } + CreateMonWithNature(&mon, SPECIES_WOBBUFFET, 100, 0, nature); + SetMonData(&mon, MON_DATA_HIDDEN_NATURE, &hiddenNature); + EXPECT_EQ(GetNature(&mon), nature); + EXPECT_EQ(GetMonData(&mon, MON_DATA_HIDDEN_NATURE), hiddenNature); +} + +TEST("Terastallization type defaults to primary or secondary type") +{ + u32 i, teraType; + struct Pokemon mon; + for (i = 0; i < 128; i++) PARAMETRIZE {} + CreateMon(&mon, SPECIES_PIDGEY, 100, 0, FALSE, 0, OT_ID_PRESET, 0); + teraType = GetMonData(&mon, MON_DATA_TERA_TYPE); + EXPECT(teraType == gSpeciesInfo[SPECIES_PIDGEY].types[0] + || teraType == gSpeciesInfo[SPECIES_PIDGEY].types[1]); +} + +TEST("Terastallization type can be set to any type") +{ + u32 i, teraType; + struct Pokemon mon; + for (i = 0; i < NUMBER_OF_MON_TYPES; i++) + { + PARAMETRIZE { teraType = i; } + } + CreateMon(&mon, SPECIES_WOBBUFFET, 100, 0, FALSE, 0, OT_ID_PRESET, 0); + SetMonData(&mon, MON_DATA_TERA_TYPE, &teraType); + EXPECT_EQ(teraType, GetMonData(&mon, MON_DATA_TERA_TYPE)); +} + +TEST("Shininess independent from PID and OTID") +{ + u32 pid, otId, data; + bool32 isShiny; + struct Pokemon mon; + PARAMETRIZE { pid = 0; otId = 0; } + CreateMon(&mon, SPECIES_WOBBUFFET, 100, 0, TRUE, pid, OT_ID_PRESET, otId); + isShiny = IsMonShiny(&mon); + data = !isShiny; + SetMonData(&mon, MON_DATA_IS_SHINY, &data); + EXPECT_EQ(pid, GetMonData(&mon, MON_DATA_PERSONALITY)); + EXPECT_EQ(otId, GetMonData(&mon, MON_DATA_OT_ID)); + EXPECT_EQ(!isShiny, GetMonData(&mon, MON_DATA_IS_SHINY)); +} + +TEST("Hyper Training increases stats without affecting IVs") +{ + u32 data, hp, atk, def, speed, spatk, spdef; + struct Pokemon mon; + CreateMon(&mon, SPECIES_WOBBUFFET, 100, 3, TRUE, 0, OT_ID_PRESET, 0); + + hp = GetMonData(&mon, MON_DATA_HP); + atk = GetMonData(&mon, MON_DATA_ATK); + def = GetMonData(&mon, MON_DATA_DEF); + speed = GetMonData(&mon, MON_DATA_SPEED); + spatk = GetMonData(&mon, MON_DATA_SPATK); + spdef = GetMonData(&mon, MON_DATA_SPDEF); + + data = TRUE; + SetMonData(&mon, MON_DATA_HYPER_TRAINED_HP, &data); + SetMonData(&mon, MON_DATA_HYPER_TRAINED_ATK, &data); + SetMonData(&mon, MON_DATA_HYPER_TRAINED_DEF, &data); + SetMonData(&mon, MON_DATA_HYPER_TRAINED_SPEED, &data); + SetMonData(&mon, MON_DATA_HYPER_TRAINED_SPATK, &data); + SetMonData(&mon, MON_DATA_HYPER_TRAINED_SPDEF, &data); + CalculateMonStats(&mon); + + EXPECT_EQ(GetMonData(&mon, MON_DATA_HP_IV), 3); + EXPECT_EQ(GetMonData(&mon, MON_DATA_ATK_IV), 3); + EXPECT_EQ(GetMonData(&mon, MON_DATA_DEF_IV), 3); + EXPECT_EQ(GetMonData(&mon, MON_DATA_SPEED_IV), 3); + EXPECT_EQ(GetMonData(&mon, MON_DATA_SPATK_IV), 3); + EXPECT_EQ(GetMonData(&mon, MON_DATA_SPDEF_IV), 3); + EXPECT_EQ(GetMonData(&mon, MON_DATA_SPEED_IV), 3); + + EXPECT_EQ(hp - 3 + MAX_PER_STAT_IVS, GetMonData(&mon, MON_DATA_HP)); + EXPECT_EQ(atk - 3 + MAX_PER_STAT_IVS, GetMonData(&mon, MON_DATA_ATK)); + EXPECT_EQ(def - 3 + MAX_PER_STAT_IVS, GetMonData(&mon, MON_DATA_DEF)); + EXPECT_EQ(speed - 3 + MAX_PER_STAT_IVS, GetMonData(&mon, MON_DATA_SPEED)); + EXPECT_EQ(spatk - 3 + MAX_PER_STAT_IVS, GetMonData(&mon, MON_DATA_SPATK)); + EXPECT_EQ(spdef - 3 + MAX_PER_STAT_IVS, GetMonData(&mon, MON_DATA_SPDEF)); +} + +TEST("Status1 round-trips through BoxPokemon") +{ + u32 status1; + struct Pokemon mon1, mon2; + PARAMETRIZE { status1 = STATUS1_NONE; } + PARAMETRIZE { status1 = STATUS1_SLEEP_TURN(1); } + PARAMETRIZE { status1 = STATUS1_SLEEP_TURN(2); } + PARAMETRIZE { status1 = STATUS1_SLEEP_TURN(3); } + PARAMETRIZE { status1 = STATUS1_SLEEP_TURN(4); } + PARAMETRIZE { status1 = STATUS1_SLEEP_TURN(5); } + PARAMETRIZE { status1 = STATUS1_POISON; } + PARAMETRIZE { status1 = STATUS1_BURN; } + PARAMETRIZE { status1 = STATUS1_FREEZE; } + 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); + SetMonData(&mon1, MON_DATA_STATUS, &status1); + BoxMonToMon(&mon1.box, &mon2); + EXPECT_EQ(GetMonData(&mon2, MON_DATA_STATUS), status1); +} + +TEST("canhypertrain/hypertrain affect MON_DATA_HYPER_TRAINED_* and recalculate stats") +{ + u32 atk; + CreateMon(&gPlayerParty[0], SPECIES_WOBBUFFET, 100, 0, FALSE, 0, OT_ID_PRESET, 0); + atk = GetMonData(&gPlayerParty[0], MON_DATA_ATK); + + RUN_OVERWORLD_SCRIPT( + canhypertrain STAT_ATK, 0; + ); + EXPECT(VarGet(VAR_RESULT)); + + RUN_OVERWORLD_SCRIPT( + hypertrain STAT_ATK, 0; + canhypertrain STAT_ATK, 0; + ); + EXPECT(GetMonData(&gPlayerParty[0], MON_DATA_HYPER_TRAINED_ATK)); + EXPECT_EQ(atk + 31, GetMonData(&gPlayerParty[0], MON_DATA_ATK)); + EXPECT(!VarGet(VAR_RESULT)); +} + +TEST("hasgigantamaxfactor/togglegigantamaxfactor affect MON_DATA_GIGANTAMAX_FACTOR") +{ + CreateMon(&gPlayerParty[0], SPECIES_WOBBUFFET, 100, 0, FALSE, 0, OT_ID_PRESET, 0); + + RUN_OVERWORLD_SCRIPT( + hasgigantamaxfactor 0; + ); + EXPECT(!VarGet(VAR_RESULT)); + + RUN_OVERWORLD_SCRIPT( + togglegigantamaxfactor 0; + hasgigantamaxfactor 0; + ); + EXPECT(VarGet(VAR_RESULT)); + EXPECT(GetMonData(&gPlayerParty[0], MON_DATA_GIGANTAMAX_FACTOR)); + + RUN_OVERWORLD_SCRIPT( + togglegigantamaxfactor 0; + hasgigantamaxfactor 0; + ); + EXPECT(!VarGet(VAR_RESULT)); + EXPECT(!GetMonData(&gPlayerParty[0], MON_DATA_GIGANTAMAX_FACTOR)); +} + +TEST("togglegigantamaxfactor fails for Melmetal") +{ + CreateMon(&gPlayerParty[0], SPECIES_MELMETAL, 100, 0, FALSE, 0, OT_ID_PRESET, 0); + + RUN_OVERWORLD_SCRIPT( + hasgigantamaxfactor 0; + ); + EXPECT(!VarGet(VAR_RESULT)); + + RUN_OVERWORLD_SCRIPT( + togglegigantamaxfactor 0; + ); + EXPECT(!VarGet(VAR_RESULT)); + EXPECT(!GetMonData(&gPlayerParty[0], MON_DATA_GIGANTAMAX_FACTOR)); +} diff --git a/test/species.c b/test/species.c index 9ea20638c8..d412dd005a 100644 --- a/test/species.c +++ b/test/species.c @@ -34,6 +34,8 @@ TEST("Form change tables contain only forms in the form species ID table") for (i = 0; formChangeTable[i].method != FORM_CHANGE_TERMINATOR; i++) { + if (formChangeTable[i].targetSpecies == SPECIES_NONE) + continue; for (j = 0; formSpeciesIdTable[j] != FORM_SPECIES_END; j++) { if (formChangeTable[i].targetSpecies == formSpeciesIdTable[j]) diff --git a/test/test_runner.c b/test/test_runner.c index 93d859654a..577ea1dbc1 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -398,11 +398,21 @@ static void FunctionTest_TearDown(void *data) FREE_AND_SET_NULL(gFunctionTestRunnerState); } +static bool32 FunctionTest_CheckProgress(void *data) +{ + bool32 madeProgress; + (void)data; + madeProgress = gFunctionTestRunnerState->checkProgressParameter < gFunctionTestRunnerState->runParameter; + gFunctionTestRunnerState->checkProgressParameter = gFunctionTestRunnerState->runParameter; + return madeProgress; +} + const struct TestRunner gFunctionTestRunner = { .setUp = FunctionTest_SetUp, .run = FunctionTest_Run, .tearDown = FunctionTest_TearDown, + .checkProgress = FunctionTest_CheckProgress, }; static void Assumptions_Run(void *data) diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 1528a3ccf9..caccdb2042 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1587,6 +1587,7 @@ static u32 GenerateNature(u32 nature, u32 offset) void ClosePokemon(u32 sourceLine) { s32 i; + u32 data; INVALID_IF(DATA.hasExplicitSpeeds && !(DATA.explicitSpeeds[DATA.currentSide] & (1 << DATA.currentPartyIndex)), "Speed required"); for (i = 0; i < STATE->battlersCount; i++) { @@ -1596,6 +1597,8 @@ void ClosePokemon(u32 sourceLine) INVALID_IF(GetMonData(DATA.currentMon, MON_DATA_HP) == 0, "Battlers cannot be fainted"); } } + data = FALSE; + SetMonData(DATA.currentMon, MON_DATA_IS_SHINY, &data); UpdateMonPersonality(&DATA.currentMon->box, GenerateNature(DATA.nature, DATA.gender % NUM_NATURES) | DATA.gender); DATA.currentMon = NULL; } @@ -1766,10 +1769,34 @@ void Status1_(u32 sourceLine, u32 status1) void OTName_(u32 sourceLine, const u8 *otName) { - INVALID_IF(!DATA.currentMon, "Traded outside of PLAYER/OPPONENT"); + INVALID_IF(!DATA.currentMon, "OTName outside of PLAYER/OPPONENT"); SetMonData(DATA.currentMon, MON_DATA_OT_NAME, &otName); } +void DynamaxLevel_(u32 sourceLine, u32 dynamaxLevel) +{ + INVALID_IF(!DATA.currentMon, "DynamaxLevel outside of PLAYER/OPPONENT"); + SetMonData(DATA.currentMon, MON_DATA_DYNAMAX_LEVEL, &dynamaxLevel); +} + +void GigantamaxFactor_(u32 sourceLine, bool32 gigantamaxFactor) +{ + INVALID_IF(!DATA.currentMon, "GigantamaxFactor outside of PLAYER/OPPONENT"); + SetMonData(DATA.currentMon, MON_DATA_GIGANTAMAX_FACTOR, &gigantamaxFactor); +} + +void TeraType_(u32 sourceLine, u32 teraType) +{ + INVALID_IF(!DATA.currentMon, "TeraType outside of PLAYER/OPPONENT"); + SetMonData(DATA.currentMon, MON_DATA_TERA_TYPE, &teraType); +} + +void Shadow_(u32 sourceLine, bool32 isShadow) +{ + INVALID_IF(!DATA.currentMon, "Shadow outside of PLAYER/OPPONENT"); + SetMonData(DATA.currentMon, MON_DATA_IS_SHADOW, &isShadow); +} + static const char *const sBattlerIdentifiersSingles[] = { "player",