diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 53d824e7e1db..4c60758184ae 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -430,6 +430,32 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectRevivalBlessing @ EFFECT_REVIVAL_BLESSING .4byte BattleScript_EffectFrostbiteHit @ EFFECT_FROSTBITE_HIT .4byte BattleScript_EffectSnow @ EFFECT_SNOWSCAPE + .4byte BattleScript_EffectTripleArrows @ EFFECT_TRIPLE_ARROWS + .4byte BattleScript_EffectInfernalParade @ EFFECT_INFERNAL_PARADE + .4byte BattleScript_EffectTakeHeart @ EFFECT_TAKE_HEART + .4byte BattleScript_EffectAxeKick @ EFFECT_AXE_KICK + +BattleScript_EffectAxeKick:: + setmoveeffect MOVE_EFFECT_CONFUSION + goto BattleScript_EffectRecoilIfMiss + +BattleScript_EffectTakeHeart:: + attackcanceler + attackstring + ppreduce + cureifburnedparalysedorpoisoned BattleScript_CalmMindTryToRaiseStats + attackanimation + waitanimation + updatestatusicon BS_ATTACKER + printstring STRINGID_PKMNSTATUSNORMAL + waitmessage B_WAIT_TIME_LONG + jumpifstat BS_ATTACKER, CMP_LESS_THAN, STAT_SPATK, MAX_STAT_STAGE, BattleScript_CalmMindStatRaise + jumpifstat BS_ATTACKER, CMP_LESS_THAN, STAT_SPDEF, MAX_STAT_STAGE, BattleScript_CalmMindStatRaise + goto BattleScript_CantRaiseMultipleStats + +BattleScript_EffectTripleArrows:: + setmoveeffect MOVE_EFFECT_TRIPLE_ARROWS + goto BattleScript_EffectHit BattleScript_EffectRevivalBlessing:: attackcanceler @@ -1482,6 +1508,11 @@ BattleScript_DoubleShockRemoveType:: waitmessage B_WAIT_TIME_LONG return +BattleScript_DefDown:: + modifybattlerstatstage BS_TARGET, STAT_DEF, DECREASE, 1, BattleScript_DefDown_Ret, ANIM_ON +BattleScript_DefDown_Ret: + return + BattleScript_EffectPurify: attackcanceler attackstring @@ -3476,6 +3507,7 @@ BattleScript_AbsorbHealBlock:: tryfaintmon BS_TARGET goto BattleScript_MoveEnd +BattleScript_EffectInfernalParade:: BattleScript_EffectBurnHit:: setmoveeffect MOVE_EFFECT_BURN goto BattleScript_EffectHit @@ -6344,11 +6376,13 @@ BattleScript_EffectCalmMind:: attackcanceler attackstring ppreduce +BattleScript_CalmMindTryToRaiseStats:: jumpifstat BS_ATTACKER, CMP_LESS_THAN, STAT_SPATK, MAX_STAT_STAGE, BattleScript_CalmMindDoMoveAnim jumpifstat BS_ATTACKER, CMP_EQUAL, STAT_SPDEF, MAX_STAT_STAGE, BattleScript_CantRaiseMultipleStats BattleScript_CalmMindDoMoveAnim:: attackanimation waitanimation +BattleScript_CalmMindStatRaise:: setbyte sSTAT_ANIM_PLAYED, FALSE playstatchangeanimation BS_ATTACKER, BIT_SPATK | BIT_SPDEF, 0 setstatchanger STAT_SPATK, 1, FALSE diff --git a/include/battle_scripts.h b/include/battle_scripts.h index b2c7cac845f6..3bb4ed4124dc 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -470,6 +470,7 @@ extern const u8 BattleScript_StealthRockActivates[]; extern const u8 BattleScript_SpikesActivates[]; extern const u8 BattleScript_BerserkGeneRet[]; extern const u8 BattleScript_TargetFormChangeWithStringNoPopup[]; +extern const u8 BattleScript_DefDown[]; // zmoves extern const u8 BattleScript_ZMoveActivateDamaging[]; diff --git a/include/battle_util.h b/include/battle_util.h index 96ffc2fa16c0..79bdfaf9a874 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -236,5 +236,6 @@ bool8 ChangeTypeBasedOnTerrain(u8 battlerId); void RemoveConfusionStatus(u8 battlerId); u8 GetBattlerGender(u8 battlerId); bool8 AreBattlersOfOppositeGender(u8 battler1, u8 battler2); +u32 CalcSecondaryEffectChance(u8 battlerId, u8 secondaryEffectChance); #endif // GUARD_BATTLE_UTIL_H diff --git a/include/constants/battle.h b/include/constants/battle.h index 814d1bfa57fb..cbc362861774 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -384,8 +384,9 @@ #define MOVE_EFFECT_DIRE_CLAW 75 #define MOVE_EFFECT_STEALTH_ROCK 76 #define MOVE_EFFECT_SPIKES 77 +#define MOVE_EFFECT_TRIPLE_ARROWS 78 -#define NUM_MOVE_EFFECTS 78 +#define NUM_MOVE_EFFECTS 79 #define MOVE_EFFECT_AFFECTS_USER 0x4000 #define MOVE_EFFECT_CERTAIN 0x8000 diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 4d8330498d64..4f106302a6ee 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -407,7 +407,11 @@ #define EFFECT_REVIVAL_BLESSING 401 #define EFFECT_FROSTBITE_HIT 402 #define EFFECT_SNOWSCAPE 403 +#define EFFECT_TRIPLE_ARROWS 404 +#define EFFECT_INFERNAL_PARADE 405 +#define EFFECT_TAKE_HEART 406 +#define EFFECT_AXE_KICK 407 -#define NUM_BATTLE_MOVE_EFFECTS 404 +#define NUM_BATTLE_MOVE_EFFECTS 408 #endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H diff --git a/include/random.h b/include/random.h index e7c2a3e284fb..5b88db1f58b2 100644 --- a/include/random.h +++ b/include/random.h @@ -67,6 +67,8 @@ enum RandomTag RNG_STATIC, RNG_STENCH, RNG_TRI_ATTACK, + RNG_TRIPLE_ARROWS_DEFENSE_DOWN, + RNG_TRIPLE_ARROWS_FLINCH, }; #define RandomWeighted(tag, ...) \ diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 97ae1f35ccc5..211fa122796c 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -2638,6 +2638,22 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER) score -= 10; break; + case EFFECT_JUNGLE_HEALING: + if (AtMaxHp(battlerAtk) + && AtMaxHp(BATTLE_PARTNER(battlerAtk)) + && !(gBattleMons[battlerAtk].status1 & STATUS1_ANY) + && !(gBattleMons[BATTLE_PARTNER(battlerAtk)].status1 & STATUS1_ANY)) + score -= 10; + break; + case EFFECT_TAKE_HEART: + if ((!(gBattleMons[battlerAtk].status1 & STATUS1_ANY) + || PartnerMoveIs(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove, MOVE_JUNGLE_HEALING) + || PartnerMoveIs(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove, MOVE_HEAL_BELL) + || PartnerMoveIs(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove, MOVE_AROMATHERAPY)) + && !BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPATK) + && !BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPDEF)) + score -= 10; + break; case EFFECT_PLACEHOLDER: return 0; // cannot even select } // move effect checks @@ -3772,7 +3788,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_WISH: case EFFECT_HEAL_BELL: if (ShouldUseWishAromatherapy(battlerAtk, battlerDef, move)) - score += 7; + score += 3; break; case EFFECT_THIEF: { @@ -4424,6 +4440,12 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (gBattleMons[battlerAtk].status1 & STATUS1_ANY) score += 2; break; + case EFFECT_TAKE_HEART: + if (gBattleMons[battlerAtk].status1 & STATUS1_ANY + || BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPATK) + || BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPDEF)) + score += 2; + break; case EFFECT_PSYCHO_SHIFT: if (gBattleMons[battlerAtk].status1 & STATUS1_PSN_ANY) IncreasePoisonScore(battlerAtk, battlerDef, move, &score); @@ -4871,6 +4893,13 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) //break; //case EFFECT_SKY_DROP //break; + case EFFECT_JUNGLE_HEALING: + if (ShouldRecover(battlerAtk, battlerDef, move, 25) + || ShouldRecover(BATTLE_PARTNER(battlerAtk), battlerDef, move, 25) + || gBattleMons[battlerAtk].status1 & STATUS1_ANY + || gBattleMons[BATTLE_PARTNER(battlerAtk)].status1 & STATUS1_ANY) + score += 3; + break; } // move effect checks return score; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 7730604c8798..61782ae62ab9 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -2051,6 +2051,7 @@ bool32 IsHealingMoveEffect(u16 effect) case EFFECT_HEALING_WISH: case EFFECT_HEAL_PULSE: case EFFECT_REST: + case EFFECT_JUNGLE_HEALING: return TRUE; default: return FALSE; diff --git a/src/battle_main.c b/src/battle_main.c index 270447a0e104..644d9ac16c25 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4749,6 +4749,7 @@ s8 GetMovePriority(u32 battlerId, u16 move) case EFFECT_SOFTBOILED: case EFFECT_ABSORB: case EFFECT_ROOST: + case EFFECT_JUNGLE_HEALING: priority += 3; break; } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 7f4b4b1212d6..c4ea866df9ad 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3862,6 +3862,25 @@ void SetMoveEffect(bool32 primary, u32 certain) gBattlescriptCurrInstr = BattleScript_SpikesActivates; } break; + case MOVE_EFFECT_TRIPLE_ARROWS: + { + u8 randomLowerDefenseChance = RandomPercentage(RNG_TRIPLE_ARROWS_DEFENSE_DOWN, CalcSecondaryEffectChance(gBattlerAttacker, 50)); + u8 randomFlinchChance = RandomPercentage(RNG_TRIPLE_ARROWS_FLINCH, CalcSecondaryEffectChance(gBattlerAttacker, 30)); + + if (randomFlinchChance && battlerAbility != ABILITY_INNER_FOCUS && GetBattlerTurnOrderNum(gEffectBattler) > gCurrentTurnActionNumber) + gBattleMons[gEffectBattler].status2 |= sStatusFlagsForMoveEffects[MOVE_EFFECT_FLINCH]; + + if (randomLowerDefenseChance) + { + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_DefDown; + } + else + { + gBattlescriptCurrInstr++; + } + } + break; } } } @@ -3873,12 +3892,7 @@ static void Cmd_seteffectwithchance(void) { CMD_ARGS(); - u32 percentChance; - - if (GetBattlerAbility(gBattlerAttacker) == ABILITY_SERENE_GRACE) - percentChance = gBattleMoves[gCurrentMove].secondaryEffectChance * 2; - else - percentChance = gBattleMoves[gCurrentMove].secondaryEffectChance; + u32 percentChance = CalcSecondaryEffectChance(gBattlerAttacker, gBattleMoves[gCurrentMove].secondaryEffectChance); if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleScripting.moveEffect) diff --git a/src/battle_util.c b/src/battle_util.c index 93d02336302d..ee4e50cf3cf2 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8537,6 +8537,7 @@ static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef) basePower = 120 * gBattleMons[battlerDef].hp / gBattleMons[battlerDef].maxHP; break; case EFFECT_HEX: + case EFFECT_INFERNAL_PARADE: if (gBattleMons[battlerDef].status1 & STATUS1_ANY || GetBattlerAbility(battlerDef) == ABILITY_COMATOSE) basePower *= 2; break; @@ -10928,3 +10929,11 @@ bool8 AreBattlersOfOppositeGender(u8 battler1, u8 battler2) return (gender1 != MON_GENDERLESS && gender2 != MON_GENDERLESS && gender1 != gender2); } + +u32 CalcSecondaryEffectChance(u8 battlerId, u8 secondaryEffectChance) +{ + if (GetBattlerAbility(battlerId) == ABILITY_SERENE_GRACE) + secondaryEffectChance *= 2; + + return secondaryEffectChance; +} diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index ebd64ae10b86..65ace8f3b84c 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -12753,29 +12753,30 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = { #if B_UPDATED_MOVE_DATA >= GEN_9 .power = 90, + .pp = 10, #else .power = 50, + .pp = 15, #endif - .effect = EFFECT_PLACEHOLDER, // EFFECT_TRIPLE_ARROWS, + .effect = EFFECT_TRIPLE_ARROWS, .type = TYPE_FIGHTING, .accuracy = 100, - .pp = 15, - .secondaryEffectChance = 100, + .secondaryEffectChance = 100, // 50% Defense down, 30% Flinch. Can be modified in 'SetMoveEffect' .target = MOVE_TARGET_SELECTED, .priority = 0, - .flags = FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST, + .flags = FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_SHEER_FORCE_BOOST | FLAG_HIGH_CRIT, .split = SPLIT_PHYSICAL, .zMoveEffect = Z_EFFECT_NONE, }, [MOVE_INFERNAL_PARADE] = { - .effect = EFFECT_PLACEHOLDER, // EFFECT_INFERNAL_PARADE, + .effect = EFFECT_INFERNAL_PARADE, .power = 60, .type = TYPE_GHOST, .accuracy = 100, .pp = 15, - .secondaryEffectChance = 0, + .secondaryEffectChance = 30, .target = MOVE_TARGET_SELECTED, .priority = 0, .flags = FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST, @@ -12861,7 +12862,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = [MOVE_LUNAR_BLESSING] = { - .effect = EFFECT_PLACEHOLDER, // EFFECT_LUNAR_BLESSING, + .effect = EFFECT_JUNGLE_HEALING, .power = 0, .type = TYPE_PSYCHIC, .accuracy = 0, @@ -12876,7 +12877,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = [MOVE_TAKE_HEART] = { - .effect = EFFECT_PLACEHOLDER, // EFFECT_TAKE_HEART, + .effect = EFFECT_TAKE_HEART, .power = 0, .type = TYPE_PSYCHIC, .accuracy = 0, @@ -12921,12 +12922,12 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = [MOVE_AXE_KICK] = { - .effect = EFFECT_PLACEHOLDER, // EFFECT_RECOIL_IF_MISS + 30% chance to confuse + .effect = EFFECT_AXE_KICK, .power = 120, .type = TYPE_FIGHTING, .accuracy = 90, .pp = 10, - .secondaryEffectChance = 0, + .secondaryEffectChance = 30, .target = MOVE_TARGET_SELECTED, .priority = 0, .flags = FLAG_MAKES_CONTACT | FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST, diff --git a/test/move_effect_axe_kick.c b/test/move_effect_axe_kick.c new file mode 100644 index 000000000000..cf04e601e2dc --- /dev/null +++ b/test/move_effect_axe_kick.c @@ -0,0 +1,54 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_AXE_KICK].effect == EFFECT_AXE_KICK); +} + +SINGLE_BATTLE_TEST("Axe Kick confuses the target") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_AXE_KICK); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_AXE_KICK, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, opponent); + MESSAGE("Foe Wobbuffet became confused!"); + } +} + +SINGLE_BATTLE_TEST("Axe Kick deals damage half the hp to user if def battler protected") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_PROTECT); MOVE(player, MOVE_AXE_KICK); } + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent); + MESSAGE("Foe Wobbuffet protected itself!"); + MESSAGE("Foe Wobbuffet protected itself!"); + MESSAGE("Wobbuffet kept going and crashed!"); + HP_BAR(player, hp: maxHP / 2); + } +} + +SINGLE_BATTLE_TEST("Axe Kick deals damage half the hp to user if it fails") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_AXE_KICK, hit: FALSE); } + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + MESSAGE("Wobbuffet used Axe Kick!"); + MESSAGE("Wobbuffet's attack missed!"); + MESSAGE("Wobbuffet kept going and crashed!"); + HP_BAR(player, hp: maxHP / 2); + } +} diff --git a/test/move_effect_infernal_parade.c b/test/move_effect_infernal_parade.c new file mode 100644 index 000000000000..5b951eca1cf9 --- /dev/null +++ b/test/move_effect_infernal_parade.c @@ -0,0 +1,48 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_INFERNAL_PARADE].effect == EFFECT_INFERNAL_PARADE); +} + +SINGLE_BATTLE_TEST("Infernal Parade inflicts poison") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_INFERNAL_PARADE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_INFERNAL_PARADE, player); + HP_BAR(opponent); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent); + STATUS_ICON(opponent, burn: TRUE); + } +} + +SINGLE_BATTLE_TEST("Infernal Parade's power doubles if the target has a status condition", s16 damage) +{ + u32 status1; + PARAMETRIZE { status1 = STATUS1_NONE; } + PARAMETRIZE { status1 = STATUS1_SLEEP; } + PARAMETRIZE { status1 = STATUS1_POISON; } + PARAMETRIZE { status1 = STATUS1_BURN; } + PARAMETRIZE { status1 = STATUS1_FREEZE; } + PARAMETRIZE { status1 = STATUS1_PARALYSIS; } + PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Status1(status1); }; + } WHEN { + TURN { MOVE(player, MOVE_INFERNAL_PARADE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_INFERNAL_PARADE, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } THEN { + if (i > 0) + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[i].damage); + if (i > 1) + EXPECT_EQ(results[i-1].damage, results[i].damage); + } +} diff --git a/test/move_effect_take_heart.c b/test/move_effect_take_heart.c new file mode 100644 index 000000000000..82745957ffe7 --- /dev/null +++ b/test/move_effect_take_heart.c @@ -0,0 +1,47 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_TAKE_HEART].effect == EFFECT_TAKE_HEART); +} + +SINGLE_BATTLE_TEST("Take Heart increases Sp. Atk and Sp. Def by one stage") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TAKE_HEART); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + } +} + +SINGLE_BATTLE_TEST("Take Heart cures the user of all status conditions") +{ + u32 status1; + PARAMETRIZE { status1 = STATUS1_SLEEP; } + PARAMETRIZE { status1 = STATUS1_POISON; } + PARAMETRIZE { status1 = STATUS1_FREEZE; } + PARAMETRIZE { status1 = STATUS1_BURN; } + PARAMETRIZE { status1 = STATUS1_PARALYSIS; } + PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Status1(status1); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TAKE_HEART); } + } SCENE { + if (status1 == STATUS1_SLEEP) { + MESSAGE("Wobbuffet is fast asleep."); + } else if (status1 == STATUS1_FREEZE) { + PASSES_RANDOMLY(20, 100, RNG_FROZEN); + STATUS_ICON(player, none: TRUE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + } else { + MESSAGE("Wobbuffet's status returned to normal!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + } + } +} diff --git a/test/move_effect_triple_arrows.c b/test/move_effect_triple_arrows.c new file mode 100644 index 000000000000..a899ab7a3464 --- /dev/null +++ b/test/move_effect_triple_arrows.c @@ -0,0 +1,91 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_TRIPLE_ARROWS].effect == EFFECT_TRIPLE_ARROWS); +} + +SINGLE_BATTLE_TEST("Triple Arrows may lower Defense by one stage") +{ + u32 ability; + u32 chance; + PARAMETRIZE { ability = ABILITY_HUSTLE; chance = 50; } + PARAMETRIZE { ability = ABILITY_SERENE_GRACE; chance = 100; } + PASSES_RANDOMLY(chance, 100, RNG_TRIPLE_ARROWS_DEFENSE_DOWN); + GIVEN { + PLAYER(SPECIES_TOGEPI) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TRIPLE_ARROWS); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRIPLE_ARROWS, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Wobbuffet's Defense fell!"); + } +} + +SINGLE_BATTLE_TEST("Triple Arrows makes the foe flinch 30% of the time") +{ + u32 ability; + u32 chance; + PARAMETRIZE { ability = ABILITY_HUSTLE; chance = 30; } + PARAMETRIZE { ability = ABILITY_SERENE_GRACE; chance = 60; } + PASSES_RANDOMLY(chance, 100, RNG_TRIPLE_ARROWS_FLINCH); + GIVEN { + PLAYER(SPECIES_TOGEPI) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TRIPLE_ARROWS); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRIPLE_ARROWS, player); + MESSAGE("Foe Wobbuffet flinched!"); + } +} + +SINGLE_BATTLE_TEST("Triple Arrows lands a critical hit") +{ + ASSUME(B_CRIT_CHANCE >= GEN_7); + ASSUME(gBattleMoves[MOVE_TRIPLE_ARROWS].flags & FLAG_HIGH_CRIT); + PASSES_RANDOMLY(1, 8, RNG_CRITICAL_HIT); + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TRIPLE_ARROWS); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRIPLE_ARROWS, player); + MESSAGE("A critical hit!"); + } +} + +SINGLE_BATTLE_TEST("Triple Arrows can lower Defense and cause flinch at the time") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TRIPLE_ARROWS); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRIPLE_ARROWS, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Wobbuffet's Defense fell!"); + MESSAGE("Foe Wobbuffet flinched!"); + } +} + +SINGLE_BATTLE_TEST("Triple Arrows's flinching is prevented by Inner Focus") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_RIOLU) { Ability(ABILITY_INNER_FOCUS); } + } WHEN { + TURN { MOVE(player, MOVE_TRIPLE_ARROWS); + MOVE(opponent, MOVE_TACKLE); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRIPLE_ARROWS, player); + NONE_OF { MESSAGE("Foe Wobbuffet flinched!"); } + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + } +}