diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index e47839adfe..c9662b783b 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7058,11 +7058,15 @@ BattleScript_CudChewActivates:: BattleScript_TargetFormChangeNoPopup: flushtextbox - handleformchange BS_TARGET, 0 - handleformchange BS_TARGET, 1 + handleformchange BS_SCRIPTING, 0 + handleformchange BS_SCRIPTING, 1 playanimation BS_TARGET, B_ANIM_FORM_CHANGE waitanimation - handleformchange BS_TARGET, 2 + handleformchange BS_SCRIPTING, 2 +.if B_DISGUISE_HP_LOSS >= GEN_8 + healthbarupdate BS_SCRIPTING + datahpupdate BS_SCRIPTING +.endif return BattleScript_TargetFormChange:: diff --git a/include/battle_script_commands.h b/include/battle_script_commands.h index d77ddfdfbc..540390bb53 100644 --- a/include/battle_script_commands.h +++ b/include/battle_script_commands.h @@ -37,7 +37,7 @@ void BufferMoveToLearnIntoBattleTextBuff2(void); void HandleBattleWindow(u8 xStart, u8 yStart, u8 xEnd, u8 yEnd, u8 flags); bool8 UproarWakeUpCheck(u8 battlerId); bool32 DoesSubstituteBlockMove(u32 battlerAtk, u32 battlerDef, u32 move); -bool32 DoesDisguiseBlockMove(u32 battlerAtk, u32 battlerDef, u32 move); +bool32 DoesDisguiseBlockMove(u32 battler, u32 move); bool32 CanPoisonType(u8 battlerAttacker, u8 battlerTarget); bool32 CanParalyzeType(u8 battlerAttacker, u8 battlerTarget); bool32 CanUseLastResort(u8 battlerId); diff --git a/include/config/battle.h b/include/config/battle.h index ffdfc40385..04d290f664 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -143,6 +143,7 @@ #define B_PROTEAN_LIBERO GEN_LATEST // In Gen9+, Protean and Libero change the user's type only once per Battle. #define B_INTREPID_SWORD GEN_LATEST // In Gen9+, Intrepid Sword raises Attack by one stage only once per Battle. #define B_DAUNTLESS_SHIELD GEN_LATEST // In Gen9+, Dauntless Shield raises Defense by one stage only once per Battle. +#define B_DISGUISE_HP_LOSS GEN_LATEST // In Gen8+, when a Disguised Mimikyu's Disguise is busted, upon changing to its Busted Form it loses HP equal to 1/8 of its maximum HP. // Item settings #define B_HP_BERRIES GEN_LATEST // In Gen4+, berries which restore HP activate immediately after HP drops to half. In Gen3, the effect occurs at the end of the turn. diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index bbee19244e..2bc25cd786 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2006,7 +2006,7 @@ static void Cmd_adjustdamage(void) if (DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove)) goto END; - if (DoesDisguiseBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove)) + if (DoesDisguiseBlockMove(gBattlerTarget, gCurrentMove)) { gBattleStruct->enduredDamage |= gBitTable[gBattlerTarget]; goto END; @@ -2258,7 +2258,7 @@ static void Cmd_healthbarupdate(void) { PrepareStringBattle(STRINGID_SUBSTITUTEDAMAGED, battler); } - else if (!DoesDisguiseBlockMove(gBattlerAttacker, battler, gCurrentMove)) + else if (!DoesDisguiseBlockMove(battler, gCurrentMove)) { s16 healthValue = min(gBattleMoveDamage, 10000); // Max damage (10000) not present in R/S, ensures that huge damage values don't change sign @@ -2311,16 +2311,19 @@ static void Cmd_datahpupdate(void) return; } } - else if (DoesDisguiseBlockMove(gBattlerAttacker, battler, gCurrentMove)) + else if (DoesDisguiseBlockMove(battler, gCurrentMove)) { // TODO: Convert this to a proper FORM_CHANGE type. u32 side = GetBattlerSide(battler); + gBattleScripting.battler = battler; if (gBattleStruct->changedSpecies[side][gBattlerPartyIndexes[battler]] == SPECIES_NONE) gBattleStruct->changedSpecies[side][gBattlerPartyIndexes[battler]] = gBattleMons[battler].species; if (gBattleMons[battler].species == SPECIES_MIMIKYU_TOTEM_DISGUISED) gBattleMons[battler].species = SPECIES_MIMIKYU_TOTEM_BUSTED; else gBattleMons[battler].species = SPECIES_MIMIKYU_BUSTED; + if (B_DISGUISE_HP_LOSS >= GEN_8) + gBattleMoveDamage = GetNonDynamaxMaxHP(battler) / 8; BattleScriptPush(cmd->nextInstr); gBattlescriptCurrInstr = BattleScript_TargetFormChange; return; @@ -14736,13 +14739,13 @@ bool32 DoesSubstituteBlockMove(u32 battlerAtk, u32 battlerDef, u32 move) return TRUE; } -bool32 DoesDisguiseBlockMove(u32 battlerAtk, u32 battlerDef, u32 move) +bool32 DoesDisguiseBlockMove(u32 battler, u32 move) { - if (!(gBattleMons[battlerDef].species == SPECIES_MIMIKYU_DISGUISED || gBattleMons[battlerDef].species == SPECIES_MIMIKYU_TOTEM_DISGUISED) - || gBattleMons[battlerDef].status2 & STATUS2_TRANSFORMED - || IS_MOVE_STATUS(move) + if (!(gBattleMons[battler].species == SPECIES_MIMIKYU_DISGUISED || gBattleMons[battler].species == SPECIES_MIMIKYU_TOTEM_DISGUISED) + || gBattleMons[battler].status2 & STATUS2_TRANSFORMED + || (!gProtectStructs[battler].confusionSelfDmg && (IS_MOVE_STATUS(move) || gHitMarker & HITMARKER_PASSIVE_DAMAGE)) || gHitMarker & HITMARKER_IGNORE_DISGUISE - || GetBattlerAbility(battlerDef) != ABILITY_DISGUISE) + || GetBattlerAbility(battler) != ABILITY_DISGUISE) return FALSE; else return TRUE; diff --git a/src/data/moves_info.h b/src/data/moves_info.h index b4639fd290..f23f664ea5 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -20231,6 +20231,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, + .ignoresTargetAbility = TRUE, }, [MOVE_MENACING_MOONRAZE_MAELSTROM] = { @@ -20244,6 +20245,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_SPECIAL, + .ignoresTargetAbility = TRUE, }, [MOVE_LIGHT_THAT_BURNS_THE_SKY] = { @@ -20257,6 +20259,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_SPECIAL, + .ignoresTargetAbility = TRUE, }, [MOVE_SOUL_STEALING_7_STAR_STRIKE] = { diff --git a/test/battle/ability/disguise.c b/test/battle/ability/disguise.c new file mode 100644 index 0000000000..7d3e36bf78 --- /dev/null +++ b/test/battle/ability/disguise.c @@ -0,0 +1,123 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_AERIAL_ACE].category == DAMAGE_CATEGORY_PHYSICAL); +} + +SINGLE_BATTLE_TEST("Disguised Mimikyu will lose 1/8 of its max HP upon changing to its busted form") +{ + s16 disguiseDamage; + + GIVEN { + PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_AERIAL_ACE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_AERIAL_ACE, opponent); + ABILITY_POPUP(player, ABILITY_DISGUISE); + HP_BAR(player, captureDamage: &disguiseDamage); + } THEN { + EXPECT_EQ(player->species, SPECIES_MIMIKYU_BUSTED); + EXPECT_EQ(disguiseDamage, player->maxHP / 8); + } +} + +SINGLE_BATTLE_TEST("Disguised Mimikyu takes no damage from a confusion hit and changes to its busted form") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_CONFUSE_RAY].effect == EFFECT_CONFUSE); + PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_CONFUSE_RAY); } + TURN { } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, opponent); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, player); + MESSAGE("Mimikyu became confused!"); + MESSAGE("Mimikyu is confused!"); + MESSAGE("It hurt itself in its confusion!"); + NOT HP_BAR(player); + ABILITY_POPUP(player, ABILITY_DISGUISE); + } THEN { + EXPECT_EQ(player->species, SPECIES_MIMIKYU_BUSTED); + } +} + +SINGLE_BATTLE_TEST("Disguised Mimikyu's Air Balloon will pop upon changing to its busted form") +{ + GIVEN { + ASSUME(gItemsInfo[ITEM_AIR_BALLOON].holdEffect == HOLD_EFFECT_AIR_BALLOON); + PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); Item(ITEM_AIR_BALLOON); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_AERIAL_ACE); } + } SCENE { + MESSAGE("Mimikyu floats in the air with its Air Balloon!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_AERIAL_ACE, opponent); + NOT HP_BAR(player); + ABILITY_POPUP(player, ABILITY_DISGUISE); + MESSAGE("Mimikyu's Air Balloon popped!"); + } THEN { + EXPECT_EQ(player->species, SPECIES_MIMIKYU_BUSTED); + } +} + +SINGLE_BATTLE_TEST("Disguised Mimikyu takes damage from secondary damage without breaking the disguise") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_STEALTH_ROCK].effect == EFFECT_STEALTH_ROCK); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_STEALTH_ROCK); } + TURN { SWITCH(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponent); + HP_BAR(player); + MESSAGE("Pointed stones dug into Mimikyu!"); + } THEN { + EXPECT_EQ(player->species, SPECIES_MIMIKYU_DISGUISED); + } +} + +SINGLE_BATTLE_TEST("Disguised Mimikyu takes damage from Rocky Helmet without breaking the disguise") +{ + GIVEN { + ASSUME(gItemsInfo[ITEM_ROCKY_HELMET].holdEffect == HOLD_EFFECT_ROCKY_HELMET); + PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_ROCKY_HELMET); } + } WHEN { + TURN { MOVE(player, MOVE_AERIAL_ACE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_AERIAL_ACE, player); + HP_BAR(opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + HP_BAR(player); + MESSAGE("Mimikyu was hurt by Foe Wobbuffet's Rocky Helmet!"); + } THEN { + EXPECT_EQ(player->species, SPECIES_MIMIKYU_DISGUISED); + } +} + +SINGLE_BATTLE_TEST("Disguised Mimikyu takes damage from Rough Skin without breaking the disguise") +{ + GIVEN { + PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); } + OPPONENT(SPECIES_CARVANHA) { Ability(ABILITY_ROUGH_SKIN); } + } WHEN { + TURN { MOVE(player, MOVE_AERIAL_ACE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_AERIAL_ACE, player); + HP_BAR(opponent); + ABILITY_POPUP(opponent, ABILITY_ROUGH_SKIN); + HP_BAR(player); + MESSAGE("Mimikyu was hurt by Foe Carvanha's Rough Skin!"); + } THEN { + EXPECT_EQ(player->species, SPECIES_MIMIKYU_DISGUISED); + } +}