From 0be643a517a4eb3e20805e3dfea53ce39b7209a6 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Sat, 1 Jun 2024 01:38:22 -0400 Subject: [PATCH] Set TYPE_NONE as type 0 + other type data tweaks (#4462) * Set TYPE_MYSTERY as type 0 * Remove redundant TYPE_NONE * Moved Gen 1-3 type damage categories to gTypesInfo * Set TYPE_NONE as 0 instead * Grouped type info to a single file * Fixed sTypeEffectivenessTable static name * Adjusted MON_DATA_TERA_TYPE to account for shift in type IDs * oops, missed the extern * Moved Tera Type RGB values to gTypesInfo * Fixed Tera Type test * Added option test feature to set IVs * Hidden Power type test * Tera Type safeguard in givemon * Removed isHiddenPowerType for a future PR that refactors Hidden Power * Review changes --- graphics/types/none.png | Bin 0 -> 215 bytes graphics_file_rules.mk | 2 +- include/battle_main.h | 1 + include/config/battle.h | 2 +- include/constants/pokemon.h | 44 +-- include/data.h | 3 + include/test/battle.h | 12 + src/battle_main.c | 337 +------------------- src/battle_terastal.c | 28 +- src/battle_util.c | 46 +-- src/data/types_info.h | 423 +++++++++++++++++++++++++ src/menu.c | 1 + src/pokedex.c | 2 +- src/pokedex_plus_hgss.c | 2 +- src/pokemon.c | 6 +- src/pokemon_summary_screen.c | 55 ++-- src/script_pokemon_util.c | 2 +- test/battle/gimmick/terastal.c | 1 + test/battle/move_effect/hidden_power.c | 106 +++++++ test/battle/move_effect/tera_blast.c | 51 +++ test/pokemon.c | 21 +- test/test_runner_battle.c | 42 +++ 22 files changed, 729 insertions(+), 458 deletions(-) create mode 100644 graphics/types/none.png create mode 100644 src/data/types_info.h create mode 100644 test/battle/move_effect/hidden_power.c diff --git a/graphics/types/none.png b/graphics/types/none.png new file mode 100644 index 0000000000000000000000000000000000000000..a2ea01d556a3d6b1a638c078c3dc702e1013b9bf GIT binary patch literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Et!VDx^^%AmxltF+`h%1mj`ah}qY*19QW3lCb zFUu)=m;7I|SK;0>!|EvlSxcrApWUL6l>}7QTwVPC|9^8=`D;K&o zHRe)O$(h6=HMycCA;*6STa|sTdynamicMoveType = ((NUMBER_OF_MON_TYPES - 4) * typeBits) / 63 + 1; + // Subtract 6 instead of 1 below because 5 types are excluded (TYPE_NONE, TYPE_NORMAL, TYPE_MYSTERY, TYPE_FAIRY and TYPE_STELLAR) + // The final + 2 skips past TYPE_NONE and Normal. + gBattleStruct->dynamicMoveType = ((NUMBER_OF_MON_TYPES - 6) * typeBits) / 63 + 2; if (gBattleStruct->dynamicMoveType >= TYPE_MYSTERY) gBattleStruct->dynamicMoveType++; gBattleStruct->dynamicMoveType |= F_DYNAMIC_TYPE_IGNORE_PHYSICALITY | F_DYNAMIC_TYPE_SET; diff --git a/src/battle_terastal.c b/src/battle_terastal.c index 796cc4ba08..afe7f3a5ce 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -169,33 +169,9 @@ uq4_12_t GetTeraMultiplier(u32 battler, u32 type) } } -// Most values pulled from the Tera type icon palette. -const u16 sTeraTypeRGBValues[NUMBER_OF_MON_TYPES] = { - [TYPE_NORMAL] = RGB_WHITE, // custom - [TYPE_FIGHTING] = RGB(26, 8, 14), - [TYPE_FLYING] = RGB(31, 26, 7), - [TYPE_POISON] = RGB(26, 10, 25), // custom - [TYPE_GROUND] = RGB(25, 23, 18), - [TYPE_ROCK] = RGB(18, 16, 8), // custom - [TYPE_BUG] = RGB(18, 24, 6), - [TYPE_GHOST] = RGB(12, 10, 16), - [TYPE_STEEL] = RGB(19, 19, 20), - [TYPE_MYSTERY] = RGB_WHITE, - [TYPE_FIRE] = RGB(31, 20, 11), - [TYPE_WATER] = RGB(10, 18, 27), - [TYPE_GRASS] = RGB(12, 24, 11), - [TYPE_ELECTRIC] = RGB(30, 26, 7), - [TYPE_PSYCHIC] = RGB(31, 14, 15), - [TYPE_ICE] = RGB(14, 26, 25), - [TYPE_DRAGON] = RGB(10, 18, 27), - [TYPE_DARK] = RGB(6, 5, 8), - [TYPE_FAIRY] = RGB(31, 15, 21), - [TYPE_STELLAR] = RGB(10, 18, 27), -}; - u16 GetTeraTypeRGB(u32 type) { - return sTeraTypeRGBValues[type]; + return gTypesInfo[type].teraTypeRGBValue; } // TERASTAL TRIGGER: @@ -636,6 +612,7 @@ static const struct SpriteTemplate sSpriteTemplate_StellarIndicator = static const struct SpriteSheet sTeraIndicatorSpriteSheets[NUMBER_OF_MON_TYPES + 1] = { + {sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE}, // TYPE_NONE {sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE}, {sFightingIndicatorGfx, sizeof(sFightingIndicatorGfx), TAG_FIGHTING_INDICATOR_TILE}, {sFlyingIndicatorGfx, sizeof(sFlyingIndicatorGfx), TAG_FLYING_INDICATOR_TILE}, @@ -661,6 +638,7 @@ static const struct SpriteSheet sTeraIndicatorSpriteSheets[NUMBER_OF_MON_TYPES + static const struct SpriteTemplate * const sTeraIndicatorSpriteTemplates[NUMBER_OF_MON_TYPES] = { + [TYPE_NONE] = &sSpriteTemplate_NormalIndicator, // just in case [TYPE_NORMAL] = &sSpriteTemplate_NormalIndicator, [TYPE_FIGHTING] = &sSpriteTemplate_FightingIndicator, [TYPE_FLYING] = &sSpriteTemplate_FlyingIndicator, diff --git a/src/battle_util.c b/src/battle_util.c index 477c0aa7dd..4bbb6c3c2e 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -903,44 +903,6 @@ static const uq4_12_t sPercentToModifier[] = UQ_4_12(1.00), // 100 }; -#define X UQ_4_12 -#define ______ X(1.0) // Regular effectiveness. - -// Type matchup updates. Attacker Defender -#define STL_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_6 ? X(1.0) : X(0.5)) // Ghost/Dark -> Steel -#define PSN_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(0.5) : X(2.0)) // Bug -> Poison -#define BUG_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(1.0) : X(2.0)) // Poison -> Bug -#define PSY_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(2.0) : X(0.0)) // Ghost -> Psychic -#define FIR_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(0.5) : X(1.0)) // Ice -> Fire - -static const uq4_12_t sTypeEffectivenessTable[NUMBER_OF_MON_TYPES][NUMBER_OF_MON_TYPES] = -{// Defender --> - // Attacker Normal Fighting Flying Poison Ground Rock Bug Ghost Steel Mystery Fire Water Grass Electric Psychic Ice Dragon Dark Fairy Stellar - [TYPE_NORMAL] = {______, ______, ______, ______, ______, X(0.5), ______, X(0.0), X(0.5), ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, - [TYPE_FIGHTING] = {X(2.0), ______, X(0.5), X(0.5), ______, X(2.0), X(0.5), X(0.0), X(2.0), ______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), X(0.5), ______}, - [TYPE_FLYING] = {______, X(2.0), ______, ______, ______, X(0.5), X(2.0), ______, X(0.5), ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, ______}, - [TYPE_POISON] = {______, ______, ______, X(0.5), X(0.5), X(0.5), BUG_RS, X(0.5), X(0.0), ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, X(2.0), ______}, - [TYPE_GROUND] = {______, ______, X(0.0), X(2.0), ______, X(2.0), X(0.5), ______, X(2.0), ______, X(2.0), ______, X(0.5), X(2.0), ______, ______, ______, ______, ______, ______}, - [TYPE_ROCK] = {______, X(0.5), X(2.0), ______, X(0.5), ______, X(2.0), ______, X(0.5), ______, X(2.0), ______, ______, ______, ______, X(2.0), ______, ______, ______, ______}, - [TYPE_BUG] = {______, X(0.5), X(0.5), PSN_RS, ______, ______, ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, X(2.0), ______, ______, X(2.0), X(0.5), ______}, - [TYPE_GHOST] = {X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), STL_RS, ______, ______, ______, ______, ______, PSY_RS, ______, ______, X(0.5), ______, ______}, - [TYPE_STEEL] = {______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, ______, X(2.0), ______}, - [TYPE_MYSTERY] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, - [TYPE_FIRE] = {______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), ______, X(0.5), X(0.5), X(2.0), ______, ______, X(2.0), X(0.5), ______, ______, ______}, - [TYPE_WATER] = {______, ______, ______, ______, X(2.0), X(2.0), ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, ______, X(0.5), ______, ______, ______}, - [TYPE_GRASS] = {______, ______, X(0.5), X(0.5), X(2.0), X(2.0), X(0.5), ______, X(0.5), ______, X(0.5), X(2.0), X(0.5), ______, ______, ______, X(0.5), ______, ______, ______}, - [TYPE_ELECTRIC] = {______, ______, X(2.0), ______, X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, X(0.5), ______, ______, ______}, - [TYPE_PSYCHIC] = {______, X(2.0), ______, X(2.0), ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, X(0.5), ______, ______, X(0.0), ______, ______}, - [TYPE_ICE] = {______, ______, X(2.0), ______, X(2.0), ______, ______, ______, X(0.5), ______, FIR_RS, X(0.5), X(2.0), ______, ______, X(0.5), X(2.0), ______, ______, ______}, - [TYPE_DRAGON] = {______, ______, ______, ______, ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, ______, ______, X(2.0), ______, X(0.0), ______}, - [TYPE_DARK] = {______, X(0.5), ______, ______, ______, ______, ______, X(2.0), STL_RS, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5), ______}, - [TYPE_FAIRY] = {______, X(2.0), ______, X(0.5), ______, ______, ______, ______, X(0.5), ______, X(0.5), ______, ______, ______, ______, ______, X(2.0), X(2.0), ______, ______}, - [TYPE_STELLAR] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, -}; - -#undef ______ -#undef X - // code u8 GetBattlerForBattleScript(u8 caseId) { @@ -10367,8 +10329,8 @@ static uq4_12_t GetInverseTypeMultiplier(uq4_12_t multiplier) uq4_12_t GetTypeModifier(u32 atkType, u32 defType) { if (B_FLAG_INVERSE_BATTLE != 0 && FlagGet(B_FLAG_INVERSE_BATTLE)) - return GetInverseTypeMultiplier(sTypeEffectivenessTable[atkType][defType]); - return sTypeEffectivenessTable[atkType][defType]; + return GetInverseTypeMultiplier(gTypeEffectivenessTable[atkType][defType]); + return gTypeEffectivenessTable[atkType][defType]; } s32 GetStealthHazardDamageByTypesAndHP(u8 hazardType, u8 type1, u8 type2, u32 maxHp) @@ -10912,10 +10874,8 @@ u8 GetBattleMoveCategory(u32 moveId) if (IS_MOVE_STATUS(moveId)) return DAMAGE_CATEGORY_STATUS; - else if (gMovesInfo[moveId].type < TYPE_MYSTERY) - return DAMAGE_CATEGORY_PHYSICAL; else - return DAMAGE_CATEGORY_SPECIAL; + return gTypesInfo[gMovesInfo[moveId].type].damageCategory; } static bool32 TryRemoveScreens(u32 battler) diff --git a/src/data/types_info.h b/src/data/types_info.h new file mode 100644 index 0000000000..f9bd233ca6 --- /dev/null +++ b/src/data/types_info.h @@ -0,0 +1,423 @@ +#include "constants/battle.h" +#include "constants/pokemon.h" + +#define X UQ_4_12 +#define ______ X(1.0) // Regular effectiveness. + +// Type matchup updates. Attacker Defender +#define STL_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_6 ? X(1.0) : X(0.5)) // Ghost/Dark -> Steel +#define PSN_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(0.5) : X(2.0)) // Bug -> Poison +#define BUG_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(1.0) : X(2.0)) // Poison -> Bug +#define PSY_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(2.0) : X(0.0)) // Ghost -> Psychic +#define FIR_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(0.5) : X(1.0)) // Ice -> Fire + +const uq4_12_t gTypeEffectivenessTable[NUMBER_OF_MON_TYPES][NUMBER_OF_MON_TYPES] = +{// Defender --> + // Attacker None Normal Fighting Flying Poison Ground Rock Bug Ghost Steel Mystery Fire Water Grass Electric Psychic Ice Dragon Dark Fairy Stellar + [TYPE_NONE] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, + [TYPE_NORMAL] = {______, ______, ______, ______, ______, ______, X(0.5), ______, X(0.0), X(0.5), ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, + [TYPE_FIGHTING] = {______, X(2.0), ______, X(0.5), X(0.5), ______, X(2.0), X(0.5), X(0.0), X(2.0), ______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), X(0.5), ______}, + [TYPE_FLYING] = {______, ______, X(2.0), ______, ______, ______, X(0.5), X(2.0), ______, X(0.5), ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, ______}, + [TYPE_POISON] = {______, ______, ______, ______, X(0.5), X(0.5), X(0.5), BUG_RS, X(0.5), X(0.0), ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, X(2.0), ______}, + [TYPE_GROUND] = {______, ______, ______, X(0.0), X(2.0), ______, X(2.0), X(0.5), ______, X(2.0), ______, X(2.0), ______, X(0.5), X(2.0), ______, ______, ______, ______, ______, ______}, + [TYPE_ROCK] = {______, ______, X(0.5), X(2.0), ______, X(0.5), ______, X(2.0), ______, X(0.5), ______, X(2.0), ______, ______, ______, ______, X(2.0), ______, ______, ______, ______}, + [TYPE_BUG] = {______, ______, X(0.5), X(0.5), PSN_RS, ______, ______, ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, X(2.0), ______, ______, X(2.0), X(0.5), ______}, + [TYPE_GHOST] = {______, X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), STL_RS, ______, ______, ______, ______, ______, PSY_RS, ______, ______, X(0.5), ______, ______}, + [TYPE_STEEL] = {______, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, ______, X(2.0), ______}, + [TYPE_MYSTERY] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, + [TYPE_FIRE] = {______, ______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), ______, X(0.5), X(0.5), X(2.0), ______, ______, X(2.0), X(0.5), ______, ______, ______}, + [TYPE_WATER] = {______, ______, ______, ______, ______, X(2.0), X(2.0), ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, ______, X(0.5), ______, ______, ______}, + [TYPE_GRASS] = {______, ______, ______, X(0.5), X(0.5), X(2.0), X(2.0), X(0.5), ______, X(0.5), ______, X(0.5), X(2.0), X(0.5), ______, ______, ______, X(0.5), ______, ______, ______}, + [TYPE_ELECTRIC] = {______, ______, ______, X(2.0), ______, X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, X(0.5), ______, ______, ______}, + [TYPE_PSYCHIC] = {______, ______, X(2.0), ______, X(2.0), ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, X(0.5), ______, ______, X(0.0), ______, ______}, + [TYPE_ICE] = {______, ______, ______, X(2.0), ______, X(2.0), ______, ______, ______, X(0.5), ______, FIR_RS, X(0.5), X(2.0), ______, ______, X(0.5), X(2.0), ______, ______, ______}, + [TYPE_DRAGON] = {______, ______, ______, ______, ______, ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, ______, ______, X(2.0), ______, X(0.0), ______}, + [TYPE_DARK] = {______, ______, X(0.5), ______, ______, ______, ______, ______, X(2.0), STL_RS, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5), ______}, + [TYPE_FAIRY] = {______, ______, X(2.0), ______, X(0.5), ______, ______, ______, ______, X(0.5), ______, X(0.5), ______, ______, ______, ______, ______, X(2.0), X(2.0), ______, ______}, + [TYPE_STELLAR] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, +}; + +#undef ______ +#undef X + +#if B_EXPANDED_TYPE_NAMES == TRUE +#define HANDLE_EXPANDED_TYPE_NAME(_name, ...) _(DEFAULT(_name, __VA_ARGS__)) +#else +#define HANDLE_EXPANDED_TYPE_NAME(_name, ...) _(_name) +#endif + +// .generic is large enough that the text for TYPE_ELECTRIC will exceed TEXT_BUFF_ARRAY_COUNT. +// In this array there's commented-out data such as references to type-resist berries that would otherwise would go unused. +// However, we figured this information would be useful for users that want to add their own types as a reminder of +// what data would they need to add in order to have their new types be fully fledged like official types. +const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = +{ + [TYPE_NONE] = + { + .name = _("None"), + .generic = _("a move"), + .palette = 15, // Uses TYPE_MYSTERY's icon + .teraTypeRGBValue = RGB_WHITE, + .damageCategory = DAMAGE_CATEGORY_PHYSICAL, + .paletteTMHM = gItemIconPalette_NormalTMHM, + }, + [TYPE_NORMAL] = + { + .name = _("Normal"), + .generic = _("a NORMAL move"), + .palette = 13, + .zMove = MOVE_BREAKNECK_BLITZ, + .maxMove = MOVE_MAX_STRIKE, + .teraTypeRGBValue = RGB_WHITE, // custom + .damageCategory = DAMAGE_CATEGORY_PHYSICAL, + .paletteTMHM = gItemIconPalette_NormalTMHM, + //.enhanceItem = ITEM_SILK_SCARF, + //.berry = ITEM_CHILAN_BERRY, + //.gem = ITEM_NORMAL_GEM, + //.zCrystal = ITEM_NORMALIUM_Z, + //.teraShard = ITEM_NORMAL_TERA_SHARD, + //.arceusForm = SPECIES_ARCEUS_NORMAL, + }, + [TYPE_FIGHTING] = + { + .name = HANDLE_EXPANDED_TYPE_NAME("Fight", "Fighting"), + .generic = _("a FIGHTING move"), + .palette = 13, + .zMove = MOVE_ALL_OUT_PUMMELING, + .maxMove = MOVE_MAX_KNUCKLE, + .teraTypeRGBValue = RGB(26, 8, 14), + .damageCategory = DAMAGE_CATEGORY_PHYSICAL, + .paletteTMHM = gItemIconPalette_FightingTMHM, + //.enhanceItem = ITEM_BLACK_BELT, + //.berry = ITEM_CHOPLE_BERRY, + //.gem = ITEM_FIGHTING_GEM, + //.zCrystal = ITEM_FIGHTINIUM_Z, + //.plate = ITEM_FIST_PLATE, + //.memory = ITEM_FIGHTING_MEMORY, + //.teraShard = ITEM_FIGHTING_TERA_SHARD, + //.arceusForm = SPECIES_ARCEUS_FIGHTING, + }, + [TYPE_FLYING] = + { + .name = _("Flying"), + .generic = _("a FLYING move"), + .palette = 14, + .zMove = MOVE_SUPERSONIC_SKYSTRIKE, + .maxMove = MOVE_MAX_AIRSTREAM, + .teraTypeRGBValue = RGB(31, 26, 7), + .damageCategory = DAMAGE_CATEGORY_PHYSICAL, + .paletteTMHM = gItemIconPalette_FlyingTMHM, + //.enhanceItem = ITEM_SHARP_BEAK, + //.berry = ITEM_COBA_BERRY, + //.gem = ITEM_FLYING_GEM, + //.zCrystal = ITEM_FLYINIUM_Z, + //.plate = ITEM_SKY_PLATE, + //.memory = ITEM_FLYING_MEMORY, + //.teraShard = ITEM_FLYING_TERA_SHARD, + //.arceusForm = SPECIES_ARCEUS_FLYING, + }, + [TYPE_POISON] = + { + .name = _("Poison"), + .generic = _("a POISON move"), + .palette = 14, + .zMove = MOVE_ACID_DOWNPOUR, + .maxMove = MOVE_MAX_OOZE, + .teraTypeRGBValue = RGB(26, 10, 25), // custom + .damageCategory = DAMAGE_CATEGORY_PHYSICAL, + .paletteTMHM = gItemIconPalette_PoisonTMHM, + //.enhanceItem = ITEM_POISON_BARB, + //.berry = ITEM_KEBIA_BERRY, + //.gem = ITEM_POISON_GEM, + //.zCrystal = ITEM_POISONIUM_Z, + //.plate = ITEM_TOXIC_PLATE, + //.memory = ITEM_POISON_MEMORY, + //.teraShard = ITEM_POISON_TERA_SHARD, + //.arceusForm = SPECIES_ARCEUS_POISON, + }, + [TYPE_GROUND] = + { + .name = _("Ground"), + .generic = _("a GROUND move"), + .palette = 13, + .zMove = MOVE_TECTONIC_RAGE, + .maxMove = MOVE_MAX_QUAKE, + .teraTypeRGBValue = RGB(25, 23, 18), + .damageCategory = DAMAGE_CATEGORY_PHYSICAL, + .paletteTMHM = gItemIconPalette_GroundTMHM, + //.enhanceItem = ITEM_SOFT_SAND, + //.berry = ITEM_SHUCA_BERRY, + //.gem = ITEM_GROUND_GEM, + //.zCrystal = ITEM_GROUNDIUM_Z, + //.plate = ITEM_EARTH_PLATE, + //.memory = ITEM_GROUND_MEMORY, + //.teraShard = ITEM_GROUND_TERA_SHARD, + //.arceusForm = SPECIES_ARCEUS_GROUND, + }, + [TYPE_ROCK] = + { + .name = _("Rock"), + .generic = _("a ROCK move"), + .palette = 13, + .zMove = MOVE_CONTINENTAL_CRUSH, + .maxMove = MOVE_MAX_ROCKFALL, + .teraTypeRGBValue = RGB(18, 16, 8), // custom + .damageCategory = DAMAGE_CATEGORY_PHYSICAL, + .paletteTMHM = gItemIconPalette_RockTMHM, + //.enhanceItem = ITEM_HARD_STONE, + //.berry = ITEM_CHARTI_BERRY, + //.gem = ITEM_ROCK_GEM, + //.zCrystal = ITEM_ROCKIUM_Z, + //.plate = ITEM_STONE_PLATE, + //.memory = ITEM_ROCK_MEMORY, + //.teraShard = ITEM_ROCK_TERA_SHARD, + //.arceusForm = SPECIES_ARCEUS_ROCK, + }, + [TYPE_BUG] = + { + .name = _("Bug"), + .generic = _("a BUG move"), + .palette = 15, + .zMove = MOVE_SAVAGE_SPIN_OUT, + .maxMove = MOVE_MAX_FLUTTERBY, + .teraTypeRGBValue = RGB(18, 24, 6), + .damageCategory = DAMAGE_CATEGORY_PHYSICAL, + .paletteTMHM = gItemIconPalette_BugTMHM, + //.enhanceItem = ITEM_SILVER_POWDER, + //.berry = ITEM_TANGA_BERRY, + //.gem = ITEM_BUG_GEM, + //.zCrystal = ITEM_BUGINIUM_Z, + //.plate = ITEM_INSECT_PLATE, + //.memory = ITEM_BUG_MEMORY, + //.teraShard = ITEM_BUG_TERA_SHARD, + //.arceusForm = SPECIES_ARCEUS_BUG, + }, + [TYPE_GHOST] = + { + .name = _("Ghost"), + .generic = _("a GHOST move"), + .palette = 14, + .zMove = MOVE_NEVER_ENDING_NIGHTMARE, + .maxMove = MOVE_MAX_PHANTASM, + .teraTypeRGBValue = RGB(12, 10, 16), + .damageCategory = DAMAGE_CATEGORY_PHYSICAL, + .paletteTMHM = gItemIconPalette_GhostTMHM, + //.enhanceItem = ITEM_SPELL_TAG, + //.berry = ITEM_KASIB_BERRY, + //.gem = ITEM_GHOST_GEM, + //.zCrystal = ITEM_GHOSTIUM_Z, + //.plate = ITEM_SPOOKY_PLATE, + //.memory = ITEM_GHOST_MEMORY, + //.teraShard = ITEM_GHOST_TERA_SHARD, + //.arceusForm = SPECIES_ARCEUS_GHOST, + }, + [TYPE_STEEL] = + { + .name = _("Steel"), + .generic = _("a STEEL move"), + .palette = 13, + .zMove = MOVE_CORKSCREW_CRASH, + .maxMove = MOVE_MAX_STEELSPIKE, + .teraTypeRGBValue = RGB(19, 19, 20), + .damageCategory = DAMAGE_CATEGORY_PHYSICAL, + .paletteTMHM = gItemIconPalette_SteelTMHM, + //.enhanceItem = ITEM_METAL_COAT, + //.berry = ITEM_BABIRI_BERRY, + //.gem = ITEM_STEEL_GEM, + //.zCrystal = ITEM_STEELIUM_Z, + //.plate = ITEM_IRON_PLATE, + //.memory = ITEM_STEEL_MEMORY, + //.teraShard = ITEM_STEEL_TERA_SHARD, + //.arceusForm = SPECIES_ARCEUS_STEEL, + }, + [TYPE_MYSTERY] = + { + .name = _("???"), + .generic = _("a ??? move"), + .palette = 15, + .teraTypeRGBValue = RGB_WHITE, + .damageCategory = DAMAGE_CATEGORY_SPECIAL, + }, + [TYPE_FIRE] = + { + .name = _("Fire"), + .generic = _("a FIRE move"), + .palette = 13, + .zMove = MOVE_INFERNO_OVERDRIVE, + .maxMove = MOVE_MAX_FLARE, + .teraTypeRGBValue = RGB(31, 20, 11), + .damageCategory = DAMAGE_CATEGORY_SPECIAL, + .paletteTMHM = gItemIconPalette_FireTMHM, + //.enhanceItem = ITEM_CHARCOAL, + //.berry = ITEM_OCCA_BERRY, + //.gem = ITEM_FIRE_GEM, + //.zCrystal = ITEM_FIRIUM_Z, + //.plate = ITEM_FLAME_PLATE, + //.memory = ITEM_FIRE_MEMORY, + //.teraShard = ITEM_FIRE_TERA_SHARD, + //.arceusForm = SPECIES_ARCEUS_FIRE, + }, + [TYPE_WATER] = + { + .name = _("Water"), + .generic = _("a WATER move"), + .palette = 14, + .zMove = MOVE_HYDRO_VORTEX, + .maxMove = MOVE_MAX_GEYSER, + .teraTypeRGBValue = RGB(10, 18, 27), + .damageCategory = DAMAGE_CATEGORY_SPECIAL, + .paletteTMHM = gItemIconPalette_WaterTMHM, + //.enhanceItem = ITEM_MYSTIC_WATER, + //.berry = ITEM_PASSHO_BERRY, + //.gem = ITEM_WATER_GEM, + //.zCrystal = ITEM_WATERIUM_Z, + //.plate = ITEM_SPLASH_PLATE, + //.memory = ITEM_WATER_MEMORY, + //.teraShard = ITEM_WATER_TERA_SHARD, + //.arceusForm = SPECIES_ARCEUS_WATER, + }, + [TYPE_GRASS] = + { + .name = _("Grass"), + .generic = _("a GRASS move"), + .palette = 15, + .zMove = MOVE_BLOOM_DOOM, + .maxMove = MOVE_MAX_OVERGROWTH, + .teraTypeRGBValue = RGB(12, 24, 11), + .damageCategory = DAMAGE_CATEGORY_SPECIAL, + .paletteTMHM = gItemIconPalette_GrassTMHM, + //.enhanceItem = ITEM_MIRACLE_SEED, + //.berry = ITEM_RINDO_BERRY, + //.gem = ITEM_GRASS_GEM, + //.zCrystal = ITEM_GRASSIUM_Z, + //.plate = ITEM_MEADOW_PLATE, + //.memory = ITEM_GRASS_MEMORY, + //.teraShard = ITEM_GRASS_TERA_SHARD, + //.arceusForm = SPECIES_ARCEUS_GRASS, + }, + [TYPE_ELECTRIC] = + { + .name = HANDLE_EXPANDED_TYPE_NAME("Electr", "Electric"), + .generic = _("an ELECTRIC move"), + .palette = 13, + .zMove = MOVE_GIGAVOLT_HAVOC, + .maxMove = MOVE_MAX_LIGHTNING, + .teraTypeRGBValue = RGB(30, 26, 7), + .damageCategory = DAMAGE_CATEGORY_SPECIAL, + .paletteTMHM = gItemIconPalette_ElectricTMHM, + //.enhanceItem = ITEM_MAGNET, + //.berry = ITEM_WACAN_BERRY, + //.gem = ITEM_ELECTRIC_GEM, + //.zCrystal = ITEM_ELECTRIUM_Z, + //.plate = ITEM_ZAP_PLATE, + //.memory = ITEM_ELECTRIC_MEMORY, + //.teraShard = ITEM_ELECTRIC_TERA_SHARD, + //.arceusForm = SPECIES_ARCEUS_ELECTRIC, + }, + [TYPE_PSYCHIC] = + { + .name = HANDLE_EXPANDED_TYPE_NAME("Psychc", "Psychic"), + .generic = _("a PSYCHIC move"), + .palette = 14, + .zMove = MOVE_SHATTERED_PSYCHE, + .maxMove = MOVE_MAX_MINDSTORM, + .teraTypeRGBValue = RGB(31, 14, 15), + .damageCategory = DAMAGE_CATEGORY_SPECIAL, + .paletteTMHM = gItemIconPalette_PsychicTMHM, + //.enhanceItem = ITEM_TWISTED_SPOON, + //.berry = ITEM_PAYAPA_BERRY, + //.gem = ITEM_PSYCHIC_GEM, + //.zCrystal = ITEM_PSYCHIUM_Z, + //.plate = ITEM_MIND_PLATE, + //.memory = ITEM_PSYCHIC_MEMORY, + //.teraShard = ITEM_PSYCHIC_TERA_SHARD, + //.arceusForm = SPECIES_ARCEUS_PSYCHIC, + }, + [TYPE_ICE] = + { + .name = _("Ice"), + .generic = _("an ICE move"), + .palette = 14, + .zMove = MOVE_SUBZERO_SLAMMER, + .maxMove = MOVE_MAX_HAILSTORM, + .teraTypeRGBValue = RGB(14, 26, 25), + .damageCategory = DAMAGE_CATEGORY_SPECIAL, + .paletteTMHM = gItemIconPalette_IceTMHM, + //.enhanceItem = ITEM_NEVER_MELT_ICE, + //.berry = ITEM_YACHE_BERRY, + //.gem = ITEM_ICE_GEM, + //.zCrystal = ITEM_ICIUM_Z, + //.plate = ITEM_ICICLE_PLATE, + //.memory = ITEM_ICE_MEMORY, + //.teraShard = ITEM_ICE_TERA_SHARD, + //.arceusForm = SPECIES_ARCEUS_ICE, + }, + [TYPE_DRAGON] = + { + .name = _("Dragon"), + .generic = _("a DRAGON move"), + .palette = 15, + .zMove = MOVE_DEVASTATING_DRAKE, + .maxMove = MOVE_MAX_WYRMWIND, + .teraTypeRGBValue = RGB(10, 18, 27), + .damageCategory = DAMAGE_CATEGORY_SPECIAL, + .paletteTMHM = gItemIconPalette_DragonTMHM, + //.enhanceItem = ITEM_DRAGON_FANG, + //.berry = ITEM_HABAN_BERRY, + //.gem = ITEM_DRAGON_GEM, + //.zCrystal = ITEM_DRAGONIUM_Z, + //.plate = ITEM_DRACO_PLATE, + //.memory = ITEM_DRAGON_MEMORY, + //.teraShard = ITEM_DRAGON_TERA_SHARD, + //.arceusForm = SPECIES_ARCEUS_DRAGON, + }, + [TYPE_DARK] = + { + .name = _("Dark"), + .generic = _("a DARK move"), + .palette = 13, + .zMove = MOVE_BLACK_HOLE_ECLIPSE, + .maxMove = MOVE_MAX_DARKNESS, + .teraTypeRGBValue = RGB(6, 5, 8), + .damageCategory = DAMAGE_CATEGORY_SPECIAL, + .paletteTMHM = gItemIconPalette_DarkTMHM, + //.enhanceItem = ITEM_BLACK_GLASSES, + //.berry = ITEM_COLBUR_BERRY, + //.gem = ITEM_DARK_GEM, + //.zCrystal = ITEM_DARKINIUM_Z, + //.plate = ITEM_DREAD_PLATE, + //.memory = ITEM_DARK_MEMORY, + //.teraShard = ITEM_DARK_TERA_SHARD, + //.arceusForm = SPECIES_ARCEUS_DARK, + }, + [TYPE_FAIRY] = + { + .name = _("Fairy"), + .generic = _("a FAIRY move"), + .palette = 14, + .zMove = MOVE_TWINKLE_TACKLE, + .maxMove = MOVE_MAX_STARFALL, + .teraTypeRGBValue = RGB(31, 15, 21), + .damageCategory = DAMAGE_CATEGORY_SPECIAL, + .paletteTMHM = gItemIconPalette_FairyTMHM, + //.enhanceItem = ITEM_FAIRY_FEATHER, + //.berry = ITEM_ROSELI_BERRY, + //.gem = ITEM_FAIRY_GEM, + //.zCrystal = ITEM_FAIRIUM_Z, + //.plate = ITEM_PIXIE_PLATE, + //.memory = ITEM_FAIRY_MEMORY, + //.teraShard = ITEM_FAIRY_TERA_SHARD, + //.arceusForm = SPECIES_ARCEUS_FAIRY, + }, + [TYPE_STELLAR] = + { + .name = HANDLE_EXPANDED_TYPE_NAME("Stellr", "Stellar"), + .generic = _("a STELLAR move"), + .palette = 15, + .zMove = MOVE_BREAKNECK_BLITZ, + .maxMove = MOVE_MAX_STRIKE, + .teraTypeRGBValue = RGB(10, 18, 27), + .paletteTMHM = gItemIconPalette_NormalTMHM, // failsafe + // .teraShard = ITEM_STELLAR_TERA_SHARD, + }, +}; diff --git a/src/menu.c b/src/menu.c index a6bd9ae2bc..1952ec5ed4 100644 --- a/src/menu.c +++ b/src/menu.c @@ -112,6 +112,7 @@ static const u8 sTextColors[] = { TEXT_DYNAMIC_COLOR_6, TEXT_COLOR_WHITE, TEXT_C static const struct MenuInfoIcon sMenuInfoIcons[] = { // { width, height, offset } { 12, 12, 0x00 }, // Unused + [TYPE_NONE + 1] = { 32, 12, 0xA4 }, // Copy of TYPE_MYSTERY's [TYPE_NORMAL + 1] = { 32, 12, 0x20 }, [TYPE_FIGHTING + 1] = { 32, 12, 0x64 }, [TYPE_FLYING + 1] = { 32, 12, 0x60 }, diff --git a/src/pokedex.c b/src/pokedex.c index 6153e72213..8b70529a93 100644 --- a/src/pokedex.c +++ b/src/pokedex.c @@ -1389,7 +1389,7 @@ static const struct SearchOptionText sDexSearchColorOptions[] = static const struct SearchOptionText sDexSearchTypeOptions[NUMBER_OF_MON_TYPES] = // + 2 for "None" and terminator, - 2 for Mystery and Stellar { - {gText_DexEmptyString, gText_DexSearchTypeNone}, + {gText_DexEmptyString, gTypesInfo[TYPE_NONE].name}, {gText_DexEmptyString, gTypesInfo[TYPE_NORMAL].name}, {gText_DexEmptyString, gTypesInfo[TYPE_FIGHTING].name}, {gText_DexEmptyString, gTypesInfo[TYPE_FLYING].name}, diff --git a/src/pokedex_plus_hgss.c b/src/pokedex_plus_hgss.c index e667e44bd6..22cbd5ba14 100644 --- a/src/pokedex_plus_hgss.c +++ b/src/pokedex_plus_hgss.c @@ -1901,7 +1901,7 @@ static const struct SearchOptionText sDexSearchColorOptions[] = static const struct SearchOptionText sDexSearchTypeOptions[NUMBER_OF_MON_TYPES] = // + 2 for "None" and terminator, - 2 for Mystery and Stellar { - {gText_DexEmptyString, gText_DexSearchTypeNone}, + {gText_DexEmptyString, gTypesInfo[TYPE_NONE].name}, {gText_DexEmptyString, gTypesInfo[TYPE_NORMAL].name}, {gText_DexEmptyString, gTypesInfo[TYPE_FIGHTING].name}, {gText_DexEmptyString, gTypesInfo[TYPE_FLYING].name}, diff --git a/src/pokemon.c b/src/pokemon.c index 7952452cb3..d52b6627eb 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -2771,14 +2771,14 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) break; case MON_DATA_TERA_TYPE: { - if (substruct0->teraType == 0) + if (substruct0->teraType == TYPE_NONE) { const u8 *types = gSpeciesInfo[substruct0->species].types; retVal = (boxMon->personality & 0x1) == 0 ? types[0] : types[1]; } else { - retVal = substruct0->teraType - 1; + retVal = substruct0->teraType; } break; } @@ -3207,7 +3207,7 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg) { u32 teraType; SET8(teraType); - substruct0->teraType = 1 + teraType; + substruct0->teraType = teraType; break; } case MON_DATA_EVOLUTION_TRACKER: diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index d4f080096b..70b82a3edc 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -812,6 +812,10 @@ static const struct OamData sOamData_MoveTypes = .paletteNum = 0, .affineParam = 0, }; +static const union AnimCmd sSpriteAnim_TypeNone[] = { + ANIMCMD_FRAME(TYPE_NONE * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; static const union AnimCmd sSpriteAnim_TypeNormal[] = { ANIMCMD_FRAME(TYPE_NORMAL * 8, 0, FALSE, FALSE), ANIMCMD_END @@ -913,31 +917,32 @@ static const union AnimCmd sSpriteAnim_CategoryTough[] = { ANIMCMD_END }; static const union AnimCmd *const sSpriteAnimTable_MoveTypes[NUMBER_OF_MON_TYPES + CONTEST_CATEGORIES_COUNT] = { - sSpriteAnim_TypeNormal, - sSpriteAnim_TypeFighting, - sSpriteAnim_TypeFlying, - sSpriteAnim_TypePoison, - sSpriteAnim_TypeGround, - sSpriteAnim_TypeRock, - sSpriteAnim_TypeBug, - sSpriteAnim_TypeGhost, - sSpriteAnim_TypeSteel, - sSpriteAnim_TypeMystery, - sSpriteAnim_TypeFire, - sSpriteAnim_TypeWater, - sSpriteAnim_TypeGrass, - sSpriteAnim_TypeElectric, - sSpriteAnim_TypePsychic, - sSpriteAnim_TypeIce, - sSpriteAnim_TypeDragon, - sSpriteAnim_TypeDark, - sSpriteAnim_TypeFairy, - sSpriteAnim_TypeStellar, - sSpriteAnim_CategoryCool, - sSpriteAnim_CategoryBeauty, - sSpriteAnim_CategoryCute, - sSpriteAnim_CategorySmart, - sSpriteAnim_CategoryTough, + [TYPE_NONE] = sSpriteAnim_TypeNone, + [TYPE_NORMAL] = sSpriteAnim_TypeNormal, + [TYPE_FIGHTING] = sSpriteAnim_TypeFighting, + [TYPE_FLYING] = sSpriteAnim_TypeFlying, + [TYPE_POISON] = sSpriteAnim_TypePoison, + [TYPE_GROUND] = sSpriteAnim_TypeGround, + [TYPE_ROCK] = sSpriteAnim_TypeRock, + [TYPE_BUG] = sSpriteAnim_TypeBug, + [TYPE_GHOST] = sSpriteAnim_TypeGhost, + [TYPE_STEEL] = sSpriteAnim_TypeSteel, + [TYPE_MYSTERY] = sSpriteAnim_TypeMystery, + [TYPE_FIRE] = sSpriteAnim_TypeFire, + [TYPE_WATER] = sSpriteAnim_TypeWater, + [TYPE_GRASS] = sSpriteAnim_TypeGrass, + [TYPE_ELECTRIC] = sSpriteAnim_TypeElectric, + [TYPE_PSYCHIC] = sSpriteAnim_TypePsychic, + [TYPE_ICE] = sSpriteAnim_TypeIce, + [TYPE_DRAGON] = sSpriteAnim_TypeDragon, + [TYPE_DARK] = sSpriteAnim_TypeDark, + [TYPE_FAIRY] = sSpriteAnim_TypeFairy, + [TYPE_STELLAR] = sSpriteAnim_TypeStellar, + [NUMBER_OF_MON_TYPES + CONTEST_CATEGORY_COOL] = sSpriteAnim_CategoryCool, + [NUMBER_OF_MON_TYPES + CONTEST_CATEGORY_BEAUTY] = sSpriteAnim_CategoryBeauty, + [NUMBER_OF_MON_TYPES + CONTEST_CATEGORY_CUTE] = sSpriteAnim_CategoryCute, + [NUMBER_OF_MON_TYPES + CONTEST_CATEGORY_SMART] = sSpriteAnim_CategorySmart, + [NUMBER_OF_MON_TYPES + CONTEST_CATEGORY_TOUGH] = sSpriteAnim_CategoryTough, }; const struct CompressedSpriteSheet gSpriteSheet_MoveTypes = diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index f7dacbbfb7..0946ec1d15 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -352,7 +352,7 @@ u32 ScriptGiveMonParameterized(u16 species, u8 level, u16 item, u8 ball, u8 natu // tera type if (teraType >= NUMBER_OF_MON_TYPES) - teraType = gSpeciesInfo[species].types[0]; + teraType = TYPE_NONE; SetMonData(&mon, MON_DATA_TERA_TYPE, &teraType); // EV and IV diff --git a/test/battle/gimmick/terastal.c b/test/battle/gimmick/terastal.c index 18eab992af..c40b6823e7 100644 --- a/test/battle/gimmick/terastal.c +++ b/test/battle/gimmick/terastal.c @@ -765,6 +765,7 @@ SINGLE_BATTLE_TEST("(TERA) Stellar type's one-time boost factors in dynamically- SINGLE_BATTLE_TEST("(TERA) All type indicators function correctly") { u32 type; + PARAMETRIZE { type = TYPE_NONE; } PARAMETRIZE { type = TYPE_NORMAL; } PARAMETRIZE { type = TYPE_FIGHTING; } PARAMETRIZE { type = TYPE_FLYING; } diff --git a/test/battle/move_effect/hidden_power.c b/test/battle/move_effect/hidden_power.c new file mode 100644 index 0000000000..8ec63c21a7 --- /dev/null +++ b/test/battle/move_effect/hidden_power.c @@ -0,0 +1,106 @@ +#include "global.h" +#include "test/battle.h" + +// IV combinations sourced from https://www.smogon.com/forums/threads/hidden-power-iv-combinations.78083/ +SINGLE_BATTLE_TEST("Hidden Power's type is determined by IVs") +{ + u32 type, j, foeType, foeSpecies; + u32 hp, atk, def, spAtk, spDef, speed; + bool32 hidden; + + PARAMETRIZE { type = TYPE_NONE; hidden = FALSE; } + PARAMETRIZE { type = TYPE_NORMAL; hidden = FALSE; } + PARAMETRIZE { type = TYPE_FIGHTING; hidden = TRUE; foeType = TYPE_PSYCHIC; foeSpecies = SPECIES_WOBBUFFET; hp = 30; atk = 2; def = 31; spAtk = 30; spDef = 30; speed = 30; } + PARAMETRIZE { type = TYPE_FIGHTING; hidden = TRUE; foeType = TYPE_PSYCHIC; foeSpecies = SPECIES_WOBBUFFET; hp = 31; atk = 15; def = 30; spAtk = 30; spDef = 30; speed = 30; } + PARAMETRIZE { type = TYPE_FIGHTING; hidden = TRUE; foeType = TYPE_PSYCHIC; foeSpecies = SPECIES_WOBBUFFET; hp = 30; atk = 22; def = 31; spAtk = 30; spDef = 30; speed = 30; } + PARAMETRIZE { type = TYPE_FIGHTING; hidden = TRUE; foeType = TYPE_PSYCHIC; foeSpecies = SPECIES_WOBBUFFET; hp = 31; atk = 31; def = 30; spAtk = 30; spDef = 30; speed = 30; } + PARAMETRIZE { type = TYPE_FLYING; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_REGISTEEL; hp = 31; atk = 2; def = 31; spAtk = 30; spDef = 30; speed = 30; } + PARAMETRIZE { type = TYPE_FLYING; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_REGISTEEL; hp = 31; atk = 15; def = 31; spAtk = 30; spDef = 30; speed = 30; } + PARAMETRIZE { type = TYPE_FLYING; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_REGISTEEL; hp = 31; atk = 22; def = 31; spAtk = 30; spDef = 30; speed = 30; } + PARAMETRIZE { type = TYPE_FLYING; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_REGISTEEL; hp = 31; atk = 31; def = 31; spAtk = 30; spDef = 30; speed = 30; } + PARAMETRIZE { type = TYPE_POISON; hidden = TRUE; foeType = TYPE_POISON; foeSpecies = SPECIES_ARBOK; hp = 30; atk = 2; def = 31; spAtk = 30; spDef = 30; speed = 31; } + PARAMETRIZE { type = TYPE_POISON; hidden = TRUE; foeType = TYPE_POISON; foeSpecies = SPECIES_ARBOK; hp = 31; atk = 15; def = 30; spAtk = 30; spDef = 30; speed = 31; } + PARAMETRIZE { type = TYPE_POISON; hidden = TRUE; foeType = TYPE_POISON; foeSpecies = SPECIES_ARBOK; hp = 30; atk = 22; def = 31; spAtk = 30; spDef = 30; speed = 31; } + PARAMETRIZE { type = TYPE_POISON; hidden = TRUE; foeType = TYPE_POISON; foeSpecies = SPECIES_ARBOK; hp = 31; atk = 31; def = 30; spAtk = 30; spDef = 30; speed = 31; } + PARAMETRIZE { type = TYPE_GROUND; hidden = TRUE; foeType = TYPE_GRASS; foeSpecies = SPECIES_TANGELA; hp = 31; atk = 2; def = 31; spAtk = 30; spDef = 30; speed = 31; } + PARAMETRIZE { type = TYPE_GROUND; hidden = TRUE; foeType = TYPE_GRASS; foeSpecies = SPECIES_TANGELA; hp = 31; atk = 15; def = 31; spAtk = 30; spDef = 30; speed = 31; } + PARAMETRIZE { type = TYPE_GROUND; hidden = TRUE; foeType = TYPE_GRASS; foeSpecies = SPECIES_TANGELA; hp = 31; atk = 22; def = 31; spAtk = 30; spDef = 30; speed = 31; } + PARAMETRIZE { type = TYPE_GROUND; hidden = TRUE; foeType = TYPE_GRASS; foeSpecies = SPECIES_TANGELA; hp = 31; atk = 31; def = 31; spAtk = 30; spDef = 30; speed = 31; } + PARAMETRIZE { type = TYPE_ROCK; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 2; def = 30; spAtk = 31; spDef = 30; speed = 30; } + PARAMETRIZE { type = TYPE_ROCK; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 15; def = 30; spAtk = 31; spDef = 30; speed = 30; } + PARAMETRIZE { type = TYPE_ROCK; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 22; def = 30; spAtk = 31; spDef = 30; speed = 30; } + PARAMETRIZE { type = TYPE_ROCK; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 31; def = 30; spAtk = 31; spDef = 30; speed = 30; } + PARAMETRIZE { type = TYPE_BUG; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 2; def = 31; spAtk = 31; spDef = 30; speed = 30; } + PARAMETRIZE { type = TYPE_BUG; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 15; def = 31; spAtk = 31; spDef = 30; speed = 30; } + PARAMETRIZE { type = TYPE_BUG; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 22; def = 31; spAtk = 31; spDef = 30; speed = 30; } + PARAMETRIZE { type = TYPE_BUG; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 31; def = 31; spAtk = 31; spDef = 30; speed = 30; } + PARAMETRIZE { type = TYPE_GHOST; hidden = TRUE; foeType = TYPE_DARK; foeSpecies = SPECIES_UMBREON; hp = 31; atk = 2; def = 31; spAtk = 31; spDef = 30; speed = 31; } + PARAMETRIZE { type = TYPE_GHOST; hidden = TRUE; foeType = TYPE_DARK; foeSpecies = SPECIES_UMBREON; hp = 31; atk = 15; def = 30; spAtk = 31; spDef = 30; speed = 31; } + PARAMETRIZE { type = TYPE_GHOST; hidden = TRUE; foeType = TYPE_DARK; foeSpecies = SPECIES_UMBREON; hp = 31; atk = 22; def = 31; spAtk = 31; spDef = 30; speed = 31; } + PARAMETRIZE { type = TYPE_GHOST; hidden = TRUE; foeType = TYPE_DARK; foeSpecies = SPECIES_UMBREON; hp = 31; atk = 31; def = 30; spAtk = 31; spDef = 30; speed = 31; } + PARAMETRIZE { type = TYPE_STEEL; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 2; def = 30; spAtk = 30; spDef = 31; speed = 30; } + PARAMETRIZE { type = TYPE_STEEL; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 15; def = 31; spAtk = 31; spDef = 30; speed = 31; } + PARAMETRIZE { type = TYPE_STEEL; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 22; def = 30; spAtk = 30; spDef = 31; speed = 30; } + PARAMETRIZE { type = TYPE_STEEL; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 31; def = 31; spAtk = 31; spDef = 30; speed = 31; } + PARAMETRIZE { type = TYPE_FIRE; hidden = TRUE; foeType = TYPE_WATER; foeSpecies = SPECIES_BLASTOISE; hp = 31; atk = 2; def = 31; spAtk = 30; spDef = 31; speed = 30; } + PARAMETRIZE { type = TYPE_FIRE; hidden = TRUE; foeType = TYPE_WATER; foeSpecies = SPECIES_BLASTOISE; hp = 31; atk = 15; def = 30; spAtk = 30; spDef = 31; speed = 30; } + PARAMETRIZE { type = TYPE_FIRE; hidden = TRUE; foeType = TYPE_WATER; foeSpecies = SPECIES_BLASTOISE; hp = 31; atk = 22; def = 31; spAtk = 30; spDef = 31; speed = 30; } + PARAMETRIZE { type = TYPE_FIRE; hidden = TRUE; foeType = TYPE_WATER; foeSpecies = SPECIES_BLASTOISE; hp = 31; atk = 31; def = 30; spAtk = 30; spDef = 31; speed = 30; } + PARAMETRIZE { type = TYPE_WATER; hidden = TRUE; foeType = TYPE_WATER; foeSpecies = SPECIES_BLASTOISE; hp = 31; atk = 2; def = 30; spAtk = 30; spDef = 31; speed = 31; } + PARAMETRIZE { type = TYPE_WATER; hidden = TRUE; foeType = TYPE_WATER; foeSpecies = SPECIES_BLASTOISE; hp = 31; atk = 15; def = 31; spAtk = 30; spDef = 31; speed = 30; } + PARAMETRIZE { type = TYPE_WATER; hidden = TRUE; foeType = TYPE_WATER; foeSpecies = SPECIES_BLASTOISE; hp = 31; atk = 22; def = 30; spAtk = 30; spDef = 31; speed = 31; } + PARAMETRIZE { type = TYPE_WATER; hidden = TRUE; foeType = TYPE_WATER; foeSpecies = SPECIES_BLASTOISE; hp = 31; atk = 31; def = 31; spAtk = 30; spDef = 31; speed = 30; } + PARAMETRIZE { type = TYPE_GRASS; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 2; def = 31; spAtk = 30; spDef = 31; speed = 31; } + PARAMETRIZE { type = TYPE_GRASS; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 15; def = 31; spAtk = 30; spDef = 31; speed = 31; } + PARAMETRIZE { type = TYPE_GRASS; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 22; def = 31; spAtk = 30; spDef = 31; speed = 31; } + PARAMETRIZE { type = TYPE_GRASS; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 31; def = 31; spAtk = 30; spDef = 31; speed = 31; } + PARAMETRIZE { type = TYPE_ELECTRIC; hidden = TRUE; foeType = TYPE_GRASS; foeSpecies = SPECIES_TANGELA; hp = 31; atk = 2; def = 30; spAtk = 31; spDef = 31; speed = 30; } + PARAMETRIZE { type = TYPE_ELECTRIC; hidden = TRUE; foeType = TYPE_GRASS; foeSpecies = SPECIES_TANGELA; hp = 30; atk = 15; def = 30; spAtk = 31; spDef = 31; speed = 30; } + PARAMETRIZE { type = TYPE_ELECTRIC; hidden = TRUE; foeType = TYPE_GRASS; foeSpecies = SPECIES_TANGELA; hp = 31; atk = 22; def = 30; spAtk = 31; spDef = 31; speed = 30; } + PARAMETRIZE { type = TYPE_ELECTRIC; hidden = TRUE; foeType = TYPE_GRASS; foeSpecies = SPECIES_TANGELA; hp = 30; atk = 31; def = 30; spAtk = 31; spDef = 31; speed = 30; } + PARAMETRIZE { type = TYPE_PSYCHIC; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 2; def = 31; spAtk = 31; spDef = 31; speed = 30; } + PARAMETRIZE { type = TYPE_PSYCHIC; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 15; def = 31; spAtk = 31; spDef = 31; speed = 30; } + PARAMETRIZE { type = TYPE_PSYCHIC; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 22; def = 31; spAtk = 31; spDef = 31; speed = 30; } + PARAMETRIZE { type = TYPE_PSYCHIC; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 31; def = 31; spAtk = 31; spDef = 31; speed = 30; } + PARAMETRIZE { type = TYPE_ICE; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 2; def = 30; spAtk = 31; spDef = 31; speed = 31; } + PARAMETRIZE { type = TYPE_ICE; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 15; def = 30; spAtk = 31; spDef = 31; speed = 31; } + PARAMETRIZE { type = TYPE_ICE; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 22; def = 30; spAtk = 31; spDef = 31; speed = 31; } + PARAMETRIZE { type = TYPE_ICE; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 31; def = 30; spAtk = 31; spDef = 31; speed = 31; } + PARAMETRIZE { type = TYPE_MYSTERY; hidden = FALSE; } + PARAMETRIZE { type = TYPE_DRAGON; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 2; def = 31; spAtk = 31; spDef = 31; speed = 31; } + PARAMETRIZE { type = TYPE_DRAGON; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 15; def = 31; spAtk = 31; spDef = 31; speed = 31; } + PARAMETRIZE { type = TYPE_DRAGON; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 22; def = 31; spAtk = 31; spDef = 31; speed = 31; } + PARAMETRIZE { type = TYPE_DRAGON; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 31; def = 31; spAtk = 31; spDef = 31; speed = 31; } + PARAMETRIZE { type = TYPE_DARK; hidden = TRUE; foeType = TYPE_DARK; foeSpecies = SPECIES_UMBREON; hp = 31; atk = 3; def = 31; spAtk = 31; spDef = 31; speed = 31; } + PARAMETRIZE { type = TYPE_DARK; hidden = TRUE; foeType = TYPE_DARK; foeSpecies = SPECIES_UMBREON; hp = 31; atk = 15; def = 31; spAtk = 31; spDef = 31; speed = 31; } + PARAMETRIZE { type = TYPE_DARK; hidden = TRUE; foeType = TYPE_DARK; foeSpecies = SPECIES_UMBREON; hp = 31; atk = 23; def = 31; spAtk = 31; spDef = 31; speed = 31; } + PARAMETRIZE { type = TYPE_DARK; hidden = TRUE; foeType = TYPE_DARK; foeSpecies = SPECIES_UMBREON; hp = 31; atk = 31; def = 31; spAtk = 31; spDef = 31; speed = 31; } + + // Any type after Dark shouldn't be part of Hidden Power officially. + for (j = TYPE_DARK + 1; j < NUMBER_OF_MON_TYPES; j++) { + PARAMETRIZE { type = j; hidden = FALSE; } + } + + GIVEN { + if (hidden) { + ASSUME(gTypeEffectivenessTable[type][foeType] == UQ_4_12(0.5)); // Foe's Type resists + ASSUME(gSpeciesInfo[foeSpecies].types[0] == gSpeciesInfo[foeSpecies].types[1]); // Foe's pure type + ASSUME(gSpeciesInfo[foeSpecies].types[0] == foeType); // Foe is the resisted type + PLAYER(SPECIES_DUNSPARCE) { HPIV(hp); AttackIV(atk); DefenseIV(def); SpAttackIV(spAtk); SpDefenseIV(spDef); SpeedIV(speed); } + } else { + PLAYER(SPECIES_DUNSPARCE); + } + OPPONENT(foeSpecies); + } WHEN { + TURN { MOVE(player, MOVE_HIDDEN_POWER); } + } SCENE { + // Only test valid Hidden Power types + if (hidden) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HIDDEN_POWER, player); + HP_BAR(opponent); + MESSAGE("It's not very effective…"); + } + } +} + +TO_DO_BATTLE_TEST("Hidden Power's power is determined by IVs before Gen6"); diff --git a/test/battle/move_effect/tera_blast.c b/test/battle/move_effect/tera_blast.c index 4592cf32a5..ca1413d2e8 100644 --- a/test/battle/move_effect/tera_blast.c +++ b/test/battle/move_effect/tera_blast.c @@ -21,6 +21,57 @@ SINGLE_BATTLE_TEST("Tera Blast changes from Normal-type to the user's Tera Type" } } +SINGLE_BATTLE_TEST("Tera Blast has correct effectiveness for every Tera Type") +{ + u32 species; + u32 type; + + PARAMETRIZE { species = SPECIES_CHIKORITA; type = TYPE_FLYING; } + PARAMETRIZE { species = SPECIES_CHIKORITA; type = TYPE_POISON; } + PARAMETRIZE { species = SPECIES_CHIKORITA; type = TYPE_FIRE; } + PARAMETRIZE { species = SPECIES_CHIKORITA; type = TYPE_BUG; } + PARAMETRIZE { species = SPECIES_CHIKORITA; type = TYPE_ICE; } + PARAMETRIZE { species = SPECIES_CYNDAQUIL; type = TYPE_GROUND; } + PARAMETRIZE { species = SPECIES_CYNDAQUIL; type = TYPE_ROCK; } + PARAMETRIZE { species = SPECIES_CYNDAQUIL; type = TYPE_WATER; } + PARAMETRIZE { species = SPECIES_GASTLY; type = TYPE_NORMAL; } + PARAMETRIZE { species = SPECIES_GASTLY; type = TYPE_GHOST; } + PARAMETRIZE { species = SPECIES_GASTLY; type = TYPE_PSYCHIC; } + PARAMETRIZE { species = SPECIES_TOTODILE; type = TYPE_GRASS; } + PARAMETRIZE { species = SPECIES_TOTODILE; type = TYPE_ELECTRIC; } + PARAMETRIZE { species = SPECIES_DRATINI; type = TYPE_DRAGON; } + PARAMETRIZE { species = SPECIES_DRATINI; type = TYPE_FAIRY; } + PARAMETRIZE { species = SPECIES_SNEASEL; type = TYPE_FIGHTING; } + PARAMETRIZE { species = SPECIES_SNEASEL; type = TYPE_STEEL; } + PARAMETRIZE { species = SPECIES_ABRA; type = TYPE_DARK; } + + GIVEN { + ASSUME(gSpeciesInfo[SPECIES_CHIKORITA].types[0] == TYPE_GRASS); + ASSUME(gSpeciesInfo[SPECIES_CHIKORITA].types[1] == TYPE_GRASS); + ASSUME(gSpeciesInfo[SPECIES_CYNDAQUIL].types[0] == TYPE_FIRE); + ASSUME(gSpeciesInfo[SPECIES_CYNDAQUIL].types[1] == TYPE_FIRE); + ASSUME(gSpeciesInfo[SPECIES_GASTLY].types[0] == TYPE_GHOST); + ASSUME(gSpeciesInfo[SPECIES_GASTLY].types[1] == TYPE_POISON); + ASSUME(gSpeciesInfo[SPECIES_TOTODILE].types[0] == TYPE_WATER); + ASSUME(gSpeciesInfo[SPECIES_TOTODILE].types[1] == TYPE_WATER); + ASSUME(gSpeciesInfo[SPECIES_DRATINI].types[0] == TYPE_DRAGON); + ASSUME(gSpeciesInfo[SPECIES_DRATINI].types[1] == TYPE_DRAGON); + ASSUME(gSpeciesInfo[SPECIES_SNEASEL].types[0] == TYPE_DARK); + ASSUME(gSpeciesInfo[SPECIES_SNEASEL].types[1] == TYPE_ICE); + ASSUME(gSpeciesInfo[SPECIES_ABRA].types[0] == TYPE_PSYCHIC); + ASSUME(gSpeciesInfo[SPECIES_ABRA].types[1] == TYPE_PSYCHIC); + PLAYER(SPECIES_WOBBUFFET) { TeraType(type); } + OPPONENT(species); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); } + } SCENE { + if (species == SPECIES_GASTLY && type == TYPE_NORMAL) + MESSAGE("It doesn't affect Foe Gastly…"); + else + MESSAGE("It's super effective!"); + } +} + SINGLE_BATTLE_TEST("Tera Blast becomes a physical move if the user is Terastallized and has a higher Attack stat", s16 damage) { bool32 tera; diff --git a/test/pokemon.c b/test/pokemon.c index 2e35896027..fa96b47f26 100644 --- a/test/pokemon.c +++ b/test/pokemon.c @@ -33,11 +33,11 @@ TEST("Terastallization type defaults to primary or secondary type") || teraType == gSpeciesInfo[SPECIES_PIDGEY].types[1]); } -TEST("Terastallization type can be set to any type") +TEST("Terastallization type can be set to any type except TYPE_NONE") { u32 i, teraType; struct Pokemon mon; - for (i = 0; i < NUMBER_OF_MON_TYPES; i++) + for (i = 1; i < NUMBER_OF_MON_TYPES; i++) { PARAMETRIZE { teraType = i; } } @@ -46,6 +46,23 @@ TEST("Terastallization type can be set to any type") EXPECT_EQ(teraType, GetMonData(&mon, MON_DATA_TERA_TYPE)); } +TEST("Terastallization type is reset to the default types when setting Tera Type back to TYPE_NONE") +{ + u32 i, teraType, typeNone; + struct Pokemon mon; + for (i = 1; i < NUMBER_OF_MON_TYPES; i++) + { + PARAMETRIZE { teraType = i; typeNone = TYPE_NONE; } + } + CreateMon(&mon, SPECIES_PIDGEY, 100, 0, FALSE, 0, OT_ID_PRESET, 0); + SetMonData(&mon, MON_DATA_TERA_TYPE, &teraType); + EXPECT_EQ(teraType, GetMonData(&mon, MON_DATA_TERA_TYPE)); + SetMonData(&mon, MON_DATA_TERA_TYPE, &typeNone); + typeNone = GetMonData(&mon, MON_DATA_TERA_TYPE); + EXPECT(typeNone == gSpeciesInfo[SPECIES_PIDGEY].types[0] + || typeNone == gSpeciesInfo[SPECIES_PIDGEY].types[1]); +} + TEST("Shininess independent from PID and OTID") { u32 pid, otId, data; diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 9a71c9a9a8..b2cc4725ce 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1717,6 +1717,48 @@ void Speed_(u32 sourceLine, u32 speed) DATA.explicitSpeeds[DATA.currentSide] |= 1 << DATA.currentPartyIndex; } +void HPIV_(u32 sourceLine, u32 hpIV) +{ + INVALID_IF(!DATA.currentMon, "HP IV outside of PLAYER/OPPONENT"); + INVALID_IF(hpIV > MAX_PER_STAT_IVS, "Illegal HP IV: %d", hpIV); + SetMonData(DATA.currentMon, MON_DATA_HP_IV, &hpIV); +} + +void AttackIV_(u32 sourceLine, u32 attackIV) +{ + INVALID_IF(!DATA.currentMon, "Attack IV outside of PLAYER/OPPONENT"); + INVALID_IF(attackIV > MAX_PER_STAT_IVS, "Illegal attack IV: %d", attackIV); + SetMonData(DATA.currentMon, MON_DATA_ATK_IV, &attackIV); +} + +void DefenseIV_(u32 sourceLine, u32 defenseIV) +{ + INVALID_IF(!DATA.currentMon, "Defense IV outside of PLAYER/OPPONENT"); + INVALID_IF(defenseIV > MAX_PER_STAT_IVS, "Illegal defense IV: %d", defenseIV); + SetMonData(DATA.currentMon, MON_DATA_DEF_IV, &defenseIV); +} + +void SpAttackIV_(u32 sourceLine, u32 spAttackIV) +{ + INVALID_IF(!DATA.currentMon, "SpAttack IV outside of PLAYER/OPPONENT"); + INVALID_IF(spAttackIV > MAX_PER_STAT_IVS, "Illegal special attack IV: %d", spAttackIV); + SetMonData(DATA.currentMon, MON_DATA_SPATK_IV, &spAttackIV); +} + +void SpDefenseIV_(u32 sourceLine, u32 spDefenseIV) +{ + INVALID_IF(!DATA.currentMon, "SpDefense IV outside of PLAYER/OPPONENT"); + INVALID_IF(spDefenseIV > MAX_PER_STAT_IVS, "Illegal special defense IV: %d", spDefenseIV); + SetMonData(DATA.currentMon, MON_DATA_SPDEF_IV, &spDefenseIV); +} + +void SpeedIV_(u32 sourceLine, u32 speedIV) +{ + INVALID_IF(!DATA.currentMon, "Speed IV outside of PLAYER/OPPONENT"); + INVALID_IF(speedIV > MAX_PER_STAT_IVS, "Illegal speed IV: %d", speedIV); + SetMonData(DATA.currentMon, MON_DATA_SPEED_IV, &speedIV); +} + void Item_(u32 sourceLine, u32 item) { INVALID_IF(!DATA.currentMon, "Item outside of PLAYER/OPPONENT");