diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index eb0756bd40..d081d3a163 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -308,6 +308,7 @@ enum BattleMoveEffects EFFECT_COURT_CHANGE, EFFECT_MAX_HP_50_RECOIL, EFFECT_MIND_BLOWN, // Same as EFFECT_MAX_HP_50_RECOIL but is cancelled by Damp + EFFECT_CHLOROBLAST, // Same effect as EFFECT_MAX_HP_50_RECOIL but follows the same rules as EFFECT_RECOIL EFFECT_EXTREME_EVOBOOST, EFFECT_HIT_SET_REMOVE_TERRAIN, EFFECT_DARK_VOID, diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 2805a10692..18c140c2d2 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -5510,6 +5510,7 @@ static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) break; case EFFECT_MAX_HP_50_RECOIL: case EFFECT_MIND_BLOWN: + case EFFECT_CHLOROBLAST: case EFFECT_SWAGGER: case EFFECT_FLATTER: case EFFECT_ATTRACT: diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index e71f11c70f..703335b326 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -1014,6 +1014,7 @@ static bool32 AI_IsMoveEffectInMinus(u32 battlerAtk, u32 battlerDef, u32 move, s { case EFFECT_MAX_HP_50_RECOIL: case EFFECT_MIND_BLOWN: + case EFFECT_CHLOROBLAST: case EFFECT_EXPLOSION: case EFFECT_FINAL_GAMBIT: return TRUE; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 88f3cfba54..83e2622ed6 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -7024,6 +7024,15 @@ static void Cmd_moveend(void) effect = TRUE; } break; + case EFFECT_CHLOROBLAST: + if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker)) + { + gBattleStruct->moveDamage[gBattlerAttacker] = (GetNonDynamaxMaxHP(gBattlerAttacker) + 1) / 2; // Half of Max HP Rounded UP + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_MoveEffectRecoil; + effect = TRUE; + } + break; case EFFECT_RAPID_SPIN: if (IsBattlerTurnDamaged(gBattlerTarget)) { diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index 06b1fb3fd6..3b3a8f8e2c 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -1966,6 +1966,12 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleTvScore = 0, // TODO: Assign points }, + [EFFECT_CHLOROBLAST] = + { + .battleScript = BattleScript_EffectHit, + .battleTvScore = 0, // TODO: Assign points + }, + [EFFECT_EXTREME_EVOBOOST] = { .battleScript = BattleScript_EffectExtremeEvoboost, diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 0b61900109..d2e80d787b 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -19319,7 +19319,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .description = COMPOUND_STRING( "A user-hurting blast of\n" "amassed chlorophyll."), - .effect = EFFECT_MAX_HP_50_RECOIL, + .effect = EFFECT_CHLOROBLAST, .power = B_UPDATED_MOVE_DATA >= GEN_9 ? 150 : 120, .type = TYPE_GRASS, .accuracy = 95, diff --git a/test/battle/move_effect/chloroblast.c b/test/battle/move_effect/chloroblast.c new file mode 100644 index 0000000000..6452736478 --- /dev/null +++ b/test/battle/move_effect/chloroblast.c @@ -0,0 +1,153 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_CHLOROBLAST) == EFFECT_CHLOROBLAST); +} + +SINGLE_BATTLE_TEST("Chloroblast makes the user lose 1/2 of its Max HP") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(400); MaxHP(400); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CHLOROBLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player); + HP_BAR(player, damage: 200); + NOT MESSAGE("Wobbuffet fainted!"); // Wobb had more than 1/2 of its HP, so it can't faint. + } +} + +SINGLE_BATTLE_TEST("Chloroblast causes the user to faint when below 1/2 of its Max HP") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(200); MaxHP(400); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CHLOROBLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player); + HP_BAR(player, hp: 0); + MESSAGE("Wobbuffet fainted!"); + } +} + +SINGLE_BATTLE_TEST("Chloroblast causes the user & the target to faint when below 1/2 of its Max HP") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(200) ; MaxHP(400); } + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CHLOROBLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player); + HP_BAR(opponent, hp: 0); + MESSAGE("The opposing Wobbuffet fainted!"); + HP_BAR(player, hp: 0); + MESSAGE("Wobbuffet fainted!"); + } +} + +SINGLE_BATTLE_TEST("Chloroblast hp loss is prevented by Magic Guard") +{ + GIVEN { + PLAYER(SPECIES_CLEFAIRY) { Ability(ABILITY_MAGIC_GUARD); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CHLOROBLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player); + HP_BAR(opponent); + NOT HP_BAR(player); + } +} + +SINGLE_BATTLE_TEST("Chloroblast does not cause recoil damage if the user has Rock Head") +{ + GIVEN { + PLAYER(SPECIES_AERODACTYL) { Ability(ABILITY_ROCK_HEAD); } + OPPONENT(SPECIES_WOBBUFFET) { HP(400); MaxHP(400); } + } WHEN { + TURN { MOVE(player, MOVE_CHLOROBLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player); + HP_BAR(opponent); + NOT HP_BAR(player); + } +} + +SINGLE_BATTLE_TEST("Chloroblast does not cause the user to lose HP even if the opposing mon protected") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_PROTECT); MOVE(player, MOVE_CHLOROBLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player); + HP_BAR(player); + } + } +} + +SINGLE_BATTLE_TEST("Chloroblast does not cause the user to lose HP even if it is absorbed by Sap Sipper") +{ + GIVEN { + ASSUME(GetMoveType(MOVE_CHLOROBLAST) == TYPE_GRASS); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GOGOAT) { Ability(ABILITY_SAP_SIPPER); } + } WHEN { + TURN { MOVE(player, MOVE_CHLOROBLAST); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_SAP_SIPPER); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player); + HP_BAR(player); + } + } +} + +SINGLE_BATTLE_TEST("Chloroblast does not cause the user to lose HP if there is no target") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_MEMENTO); MOVE(player, MOVE_CHLOROBLAST); SEND_OUT(opponent, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MEMENTO, opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player); + HP_BAR(player); + } + MESSAGE("Wobbuffet used Chloroblast!"); + MESSAGE("But it failed!"); + MESSAGE("2 sent out Wobbuffet!"); + } +} + +SINGLE_BATTLE_TEST("Chloroblast is not affected by Reckless", s16 damage) +{ + u32 move; + + PARAMETRIZE { move = MOVE_CHLOROBLAST; } + PARAMETRIZE { move = MOVE_FRENZY_PLANT; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + } +}