diff --git a/include/config/battle.h b/include/config/battle.h index fd390debf0ec..7e0f05a81947 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -46,6 +46,7 @@ #define B_PRANKSTER_DARK_TYPES GEN_LATEST // In Gen7+, Prankster-elevated status moves do not affect Dark type Pokémon. #define B_SHEER_COLD_IMMUNITY GEN_LATEST // In Gen7+, Ice-types are immune to Sheer Cold #define B_ROOST_PURE_FLYING GEN_LATEST // In Gen5+, Roost makes pure Flying-types into Normal-type. +#define B_STATUS_TYPE_IMMUNITY GEN_LATEST // In Gen1, Pokémon were immune to paralysis/freeze/burn side effects of attacking moves, if they shared a type with the move. // Turn settings #define B_BINDING_TURNS GEN_LATEST // In Gen5+, binding moves last for 4-5 turns instead of 2-5 turns. (With Grip Claw, 7 and 5 turns respectively.) diff --git a/include/item_use.h b/include/item_use.h index e9a2bad34906..fe801083adab 100644 --- a/include/item_use.h +++ b/include/item_use.h @@ -54,5 +54,6 @@ enum { }; bool32 CanThrowBall(void); +bool32 CannotUseItemsInBattle(u16 itemId, struct Pokemon *mon); #endif // GUARD_ITEM_USE_H diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 34a3da0857d2..0272aa4108db 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2933,12 +2933,27 @@ void SetMoveEffect(bool32 primary, u32 certain) RESET_RETURN } + if (B_STATUS_TYPE_IMMUNITY == GEN_1) + { + u8 moveType = 0; + GET_MOVE_TYPE(gCurrentMove, moveType); + if (primary == FALSE && certain != MOVE_EFFECT_CERTAIN && IS_BATTLER_OF_TYPE(gEffectBattler, moveType)) + break; + } + if (!CanBeBurned(gEffectBattler)) break; statusChanged = TRUE; break; case STATUS1_FREEZE: + if (B_STATUS_TYPE_IMMUNITY == GEN_1) + { + u8 moveType = 0; + GET_MOVE_TYPE(gCurrentMove, moveType); + if (primary == FALSE && certain != MOVE_EFFECT_CERTAIN && IS_BATTLER_OF_TYPE(gEffectBattler, moveType)) + break; + } if (!CanBeFrozen(gEffectBattler)) break; @@ -2972,6 +2987,13 @@ void SetMoveEffect(bool32 primary, u32 certain) else break; } + if (B_STATUS_TYPE_IMMUNITY == GEN_1) + { + u8 moveType = 0; + GET_MOVE_TYPE(gCurrentMove, moveType); + if (primary == FALSE && certain != MOVE_EFFECT_CERTAIN && IS_BATTLER_OF_TYPE(gEffectBattler, moveType)) + break; + } if (!CanParalyzeType(gBattleScripting.battler, gEffectBattler) && (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT) && (primary == TRUE || certain == MOVE_EFFECT_CERTAIN)) @@ -3036,6 +3058,13 @@ void SetMoveEffect(bool32 primary, u32 certain) } break; case STATUS1_FROSTBITE: + if (B_STATUS_TYPE_IMMUNITY == GEN_1) + { + u8 moveType = 0; + GET_MOVE_TYPE(gCurrentMove, moveType); + if (primary == FALSE && certain != MOVE_EFFECT_CERTAIN && IS_BATTLER_OF_TYPE(gEffectBattler, moveType)) + break; + } if (!CanGetFrostbite(gEffectBattler)) break; diff --git a/src/battle_tower.c b/src/battle_tower.c index 015f6d207f34..6dba9a63eda4 100644 --- a/src/battle_tower.c +++ b/src/battle_tower.c @@ -2989,6 +2989,8 @@ static void FillPartnerParty(u16 trainerId) otID = STEVEN_OTID; else otID = ((firstIdPart % 72) * 1000) + ((secondIdPart % 23) * 10) + (thirdIdPart % 37) % 65536; + + do { personality = Random32(); } while (IsShinyOtIdPersonality(otID, personality)); diff --git a/src/item_use.c b/src/item_use.c index 53da18ee9855..be253a10a855 100644 --- a/src/item_use.c +++ b/src/item_use.c @@ -78,7 +78,6 @@ static void SetDistanceOfClosestHiddenItem(u8, s16, s16); static void CB2_OpenPokeblockFromBag(void); static void ItemUseOnFieldCB_Honey(u8 taskId); static bool32 IsValidLocationForVsSeeker(void); -static bool32 CannotUseBagBattleItem(u16 itemId); // EWRAM variables EWRAM_DATA static void(*sItemUseOnFieldCB)(u8 taskId) = NULL; @@ -1142,11 +1141,13 @@ void ItemUseInBattle_PartyMenuChooseMove(u8 taskId) } // Returns whether an item can be used in battle and sets the fail text. -static bool32 CannotUseBagBattleItem(u16 itemId) +bool32 CannotUseItemsInBattle(u16 itemId, struct Pokemon *mon) { - u8 cannotUse = FALSE; u16 battleUsage = ItemId_GetBattleUsage(itemId); + bool8 cannotUse = FALSE; const u8* failStr = NULL; + u32 i; + u16 hp = GetMonData(mon, MON_DATA_HP); // Embargo Check if ((gPartyMenu.slotId == 0 && gStatuses3[B_POSITION_PLAYER_LEFT] & STATUS3_EMBARGO) @@ -1154,77 +1155,105 @@ static bool32 CannotUseBagBattleItem(u16 itemId) { return TRUE; } - // X-Items - if (battleUsage == EFFECT_ITEM_INCREASE_STAT - && gBattleMons[gBattlerInMenuId].statStages[gItemEffectTable[itemId][1]] == MAX_STAT_STAGE) - { - cannotUse++; - } - // Dire Hit - if (battleUsage == EFFECT_ITEM_SET_FOCUS_ENERGY - && (gBattleMons[gBattlerInMenuId].status2 & STATUS2_FOCUS_ENERGY)) - { - cannotUse++; - } - // Guard Spec - if (battleUsage == EFFECT_ITEM_SET_MIST - && gSideStatuses[GetBattlerSide(gBattlerInMenuId)] & SIDE_STATUS_MIST) - { - cannotUse++; - } - // Escape Items - if (battleUsage == EFFECT_ITEM_ESCAPE - && gBattleTypeFlags & BATTLE_TYPE_TRAINER) - { - cannotUse++; - } - // Poke Balls - if (battleUsage == EFFECT_ITEM_THROW_BALL) + + // battleUsage checks + switch (battleUsage) { + case EFFECT_ITEM_INCREASE_STAT: + if (gBattleMons[gBattlerInMenuId].statStages[gItemEffectTable[itemId][1]] == MAX_STAT_STAGE) + cannotUse = TRUE; + break; + case EFFECT_ITEM_SET_FOCUS_ENERGY: + if (gBattleMons[gBattlerInMenuId].status2 & STATUS2_FOCUS_ENERGY) + cannotUse = TRUE; + break; + case EFFECT_ITEM_SET_MIST: + if (gSideStatuses[GetBattlerSide(gBattlerInMenuId)] & SIDE_STATUS_MIST) + cannotUse = TRUE; + break; + case EFFECT_ITEM_ESCAPE: + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) + cannotUse = TRUE; + break; + case EFFECT_ITEM_THROW_BALL: switch (GetBallThrowableState()) { - case BALL_THROW_UNABLE_TWO_MONS: - failStr = sText_CantThrowPokeBall_TwoMons; - cannotUse++; - break; - case BALL_THROW_UNABLE_NO_ROOM: - failStr = gText_BoxFull; - cannotUse++; - break; - case BALL_THROW_UNABLE_SEMI_INVULNERABLE: - failStr = sText_CantThrowPokeBall_SemiInvulnerable; - cannotUse++; - break; - case BALL_THROW_UNABLE_DISABLED_FLAG: - failStr = sText_CantThrowPokeBall_Disabled; - cannotUse++; - break; + case BALL_THROW_UNABLE_TWO_MONS: + failStr = sText_CantThrowPokeBall_TwoMons; + cannotUse = TRUE; + break; + case BALL_THROW_UNABLE_NO_ROOM: + failStr = gText_BoxFull; + cannotUse = TRUE; + break; + case BALL_THROW_UNABLE_SEMI_INVULNERABLE: + failStr = sText_CantThrowPokeBall_SemiInvulnerable; + cannotUse = TRUE; + break; + case BALL_THROW_UNABLE_DISABLED_FLAG: + failStr = sText_CantThrowPokeBall_Disabled; + cannotUse = TRUE; + break; } - } - // Max Mushrooms - if (battleUsage == EFFECT_ITEM_INCREASE_ALL_STATS) - { - u32 i; - for (i = 1; i < NUM_STATS; i++) + break; + case EFFECT_ITEM_INCREASE_ALL_STATS: + for (i = STAT_ATK; i < NUM_STATS; i++) { if (CompareStat(gBattlerInMenuId, i, MAX_STAT_STAGE, CMP_EQUAL)) { - cannotUse++; + cannotUse = TRUE; break; } } + break; + case EFFECT_ITEM_RESTORE_HP: + if (hp == 0 || hp == GetMonData(mon, MON_DATA_MAX_HP)) + cannotUse = TRUE; + break; + case EFFECT_ITEM_CURE_STATUS: + if (!((GetMonData(mon, MON_DATA_STATUS) & GetItemStatus1Mask(itemId)) + || (gPartyMenu.slotId == 0 && gBattleMons[gBattlerInMenuId].status2 & GetItemStatus2Mask(itemId)))) + cannotUse = TRUE; + break; + case EFFECT_ITEM_HEAL_AND_CURE_STATUS: + if ((hp == 0 || hp == GetMonData(mon, MON_DATA_MAX_HP)) + && !((GetMonData(mon, MON_DATA_STATUS) & GetItemStatus1Mask(itemId)) + || (gPartyMenu.slotId == 0 && gBattleMons[gBattlerInMenuId].status2 & GetItemStatus2Mask(itemId)))) + cannotUse = TRUE; + break; + case EFFECT_ITEM_REVIVE: + if (hp != 0) + cannotUse = TRUE; + break; + case EFFECT_ITEM_RESTORE_PP: + if (GetItemEffect(itemId)[6] == ITEM4_HEAL_PP) + { + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (GetMonData(mon, MON_DATA_PP1 + i) < CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + i), GetMonData(mon, MON_DATA_PP_BONUSES), i)); + break; + } + if (i == MAX_MON_MOVES) + cannotUse = TRUE; + } + else if (GetMonData(mon, MON_DATA_PP1 + gPartyMenu.data1) == CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + gPartyMenu.data1), GetMonData(mon, MON_DATA_PP_BONUSES), gPartyMenu.data1)) + { + cannotUse = TRUE; + } + break; } if (failStr != NULL) StringExpandPlaceholders(gStringVar4, failStr); else StringExpandPlaceholders(gStringVar4, gText_WontHaveEffect); + return cannotUse; } void ItemUseInBattle_BagMenu(u8 taskId) { - if (CannotUseBagBattleItem(gSpecialVar_ItemId)) + if (CannotUseItemsInBattle(gSpecialVar_ItemId, NULL)) { if (!InBattlePyramid()) DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, CloseItemMessage); diff --git a/src/party_menu.c b/src/party_menu.c index f333db02fb91..e97310058652 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -504,7 +504,6 @@ static bool8 SetUpFieldMove_Dive(void); void TryItemHoldFormChange(struct Pokemon *mon); static void ShowMoveSelectWindow(u8 slot); static void Task_HandleWhichMoveInput(u8 taskId); -static bool32 CannotUsePartyBattleItem(u16 itemId, struct Pokemon* mon); // static const data #include "data/party_menu.h" @@ -4557,70 +4556,11 @@ static bool8 IsItemFlute(u16 item) return FALSE; } -static bool32 CannotUsePartyBattleItem(u16 itemId, struct Pokemon* mon) -{ - u8 i; - u8 cannotUse = FALSE; - u16 battleUsage = ItemId_GetBattleUsage(itemId); - u16 hp = GetMonData(mon, MON_DATA_HP); - - // Embargo Check - if ((gPartyMenu.slotId == 0 && gStatuses3[B_POSITION_PLAYER_LEFT] & STATUS3_EMBARGO) - || (gPartyMenu.slotId == 1 && gStatuses3[B_POSITION_PLAYER_RIGHT] & STATUS3_EMBARGO)) - { - return FALSE; - } - // Items that restore HP (Potions, Sitrus Berry, etc.) - if (battleUsage == EFFECT_ITEM_RESTORE_HP && (hp == 0 || hp == GetMonData(mon, MON_DATA_MAX_HP))) - { - cannotUse++; - } - // Items that cure status (Burn Heal, Awakening, etc.) - if (battleUsage == EFFECT_ITEM_CURE_STATUS - && !((GetMonData(mon, MON_DATA_STATUS) & GetItemStatus1Mask(itemId)) - || (gPartyMenu.slotId == 0 && gBattleMons[gBattlerInMenuId].status2 & GetItemStatus2Mask(itemId)))) - { - cannotUse++; - } - // Items that restore HP and cure status (Full Restore) - if (battleUsage == EFFECT_ITEM_HEAL_AND_CURE_STATUS - && (hp == 0 || hp == GetMonData(mon, MON_DATA_MAX_HP)) - && !((GetMonData(mon, MON_DATA_STATUS) & GetItemStatus1Mask(itemId)) - || (gPartyMenu.slotId == 0 && gBattleMons[gBattlerInMenuId].status2 & GetItemStatus2Mask(itemId)))) - { - cannotUse++; - } - // Items that revive a party member - if (battleUsage == EFFECT_ITEM_REVIVE && hp != 0) - { - cannotUse++; - } - // Items that restore PP (Elixir, Ether, Leppa Berry) - if (battleUsage == EFFECT_ITEM_RESTORE_PP) - { - if (GetItemEffect(itemId)[6] == ITEM4_HEAL_PP) - { - for (i = 0; i < MAX_MON_MOVES; i++) - { - if (GetMonData(mon, MON_DATA_PP1 + i) < CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + i), GetMonData(mon, MON_DATA_PP_BONUSES), i)); - break; - } - if (i == MAX_MON_MOVES) - cannotUse++; - } - else if (GetMonData(mon, MON_DATA_PP1 + gPartyMenu.data1) == CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + gPartyMenu.data1), GetMonData(mon, MON_DATA_PP_BONUSES), gPartyMenu.data1)) - { - cannotUse++; - } - } - return cannotUse; -} - // Battle scripts called in HandleAction_UseItem void ItemUseCB_BattleScript(u8 taskId, TaskFunc task) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; - if (CannotUsePartyBattleItem(gSpecialVar_ItemId, mon)) + if (CannotUseItemsInBattle(gSpecialVar_ItemId, mon)) { gPartyMenuUseExitCallback = FALSE; PlaySE(SE_SELECT); @@ -5129,7 +5069,7 @@ static void TryUseItemOnMove(u8 taskId) // In battle, set appropriate variables to be used in battle script. if (gMain.inBattle) { - if (CannotUsePartyBattleItem(gSpecialVar_ItemId, mon)) + if (CannotUseItemsInBattle(gSpecialVar_ItemId, mon)) { gPartyMenuUseExitCallback = FALSE; PlaySE(SE_SELECT); diff --git a/test/battle/move_effect/burn_hit.c b/test/battle/move_effect/burn_hit.c index 627a051a53c5..5c5dfa3d6035 100644 --- a/test/battle/move_effect/burn_hit.c +++ b/test/battle/move_effect/burn_hit.c @@ -38,3 +38,32 @@ SINGLE_BATTLE_TEST("Ember cannot burn a Fire-type Pokémon") } } } + +#if B_STATUS_TYPE_IMMUNITY > GEN_1 +SINGLE_BATTLE_TEST("Scald should burn a Water-type Pokémon") +#else +SINGLE_BATTLE_TEST("Scald shouldn't burn a Water-type Pokémon") +#endif +{ + GIVEN { + ASSUME(gSpeciesInfo[SPECIES_SQUIRTLE].types[0] == TYPE_WATER); + ASSUME(gBattleMoves[MOVE_SCALD].effect == EFFECT_BURN_HIT); + ASSUME(gBattleMoves[MOVE_SCALD].type == TYPE_WATER); + PLAYER(SPECIES_SQUIRTLE); + OPPONENT(SPECIES_SQUIRTLE); + } WHEN { + TURN { MOVE(player, MOVE_SCALD); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCALD, player); + HP_BAR(opponent); + #if B_STATUS_TYPE_IMMUNITY > GEN_1 + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent); + STATUS_ICON(opponent, burn: TRUE); + #else + NONE_OF { + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent); + STATUS_ICON(opponent, burn: TRUE); + } + #endif + } +} diff --git a/test/battle/move_effect/freeze_hit.c b/test/battle/move_effect/freeze_hit.c index 4c9ff557e414..991acc5da6d0 100644 --- a/test/battle/move_effect/freeze_hit.c +++ b/test/battle/move_effect/freeze_hit.c @@ -66,3 +66,32 @@ SINGLE_BATTLE_TEST("Blizzard bypasses accuracy checks in Hail and Snow") NOT MESSAGE("Wobbuffet's attack missed!"); } } + +#if B_STATUS_TYPE_IMMUNITY > GEN_1 +SINGLE_BATTLE_TEST("Freezing Glare should freeze Psychic-types") +#else +SINGLE_BATTLE_TEST("Freezing Glare shouldn't freeze Psychic-types") +#endif +{ + GIVEN { + ASSUME(gSpeciesInfo[SPECIES_ARTICUNO_GALARIAN].types[0] == TYPE_PSYCHIC); + ASSUME(gBattleMoves[MOVE_FREEZING_GLARE].effect == EFFECT_FREEZE_HIT); + ASSUME(gBattleMoves[MOVE_FREEZING_GLARE].type == TYPE_PSYCHIC); + PLAYER(SPECIES_ARTICUNO_GALARIAN); + OPPONENT(SPECIES_ARTICUNO_GALARIAN); + } WHEN { + TURN { MOVE(player, MOVE_FREEZING_GLARE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FREEZING_GLARE, player); + HP_BAR(opponent); + #if B_STATUS_TYPE_IMMUNITY > GEN_1 + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent); + STATUS_ICON(opponent, freeze: TRUE); + #else + NONE_OF { + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent); + STATUS_ICON(opponent, freeze: TRUE); + } + #endif + } +} diff --git a/test/battle/move_effect/paralyze_hit.c b/test/battle/move_effect/paralyze_hit.c index c2fa440be144..24ae4bff9834 100644 --- a/test/battle/move_effect/paralyze_hit.c +++ b/test/battle/move_effect/paralyze_hit.c @@ -39,3 +39,32 @@ SINGLE_BATTLE_TEST("Thunder Shock cannot paralyze an Electric-type") } } } + +#if B_STATUS_TYPE_IMMUNITY > GEN_1 +SINGLE_BATTLE_TEST("Body Slam should paralyze Normal-types") +#else +SINGLE_BATTLE_TEST("Body Slam shouldn't paralyze Normal-types") +#endif +{ + GIVEN { + ASSUME(gSpeciesInfo[SPECIES_TAUROS].types[0] == TYPE_NORMAL); + ASSUME(gBattleMoves[MOVE_BODY_SLAM].effect == EFFECT_PARALYZE_HIT); + ASSUME(gBattleMoves[MOVE_BODY_SLAM].type == TYPE_NORMAL); + PLAYER(SPECIES_TAUROS); + OPPONENT(SPECIES_TAUROS); + } WHEN { + TURN { MOVE(player, MOVE_BODY_SLAM); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BODY_SLAM, player); + HP_BAR(opponent); + #if B_STATUS_TYPE_IMMUNITY > GEN_1 + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent); + STATUS_ICON(opponent, paralysis: TRUE); + #else + NONE_OF { + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent); + STATUS_ICON(opponent, paralysis: TRUE); + } + #endif + } +}