From 34e91dd96ff2949683ee7cc125ed11622fc5c888 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Thu, 24 Apr 2025 14:26:23 +0100 Subject: [PATCH 1/3] Fixes Unburden doubling speed when affected by Neutralizing Gas (#6691) --- src/battle_main.c | 4 +- test/battle/ability/unburden.c | 101 +++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 test/battle/ability/unburden.c diff --git a/src/battle_main.c b/src/battle_main.c index 85194a0d54..7bb13fa5c2 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4750,6 +4750,8 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect) speed = (GetHighestStatId(battler) == STAT_SPEED) ? (speed * 150) / 100 : speed; else if (ability == ABILITY_QUARK_DRIVE && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) && (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gDisableStructs[battler].boosterEnergyActivates)) speed = (GetHighestStatId(battler) == STAT_SPEED) ? (speed * 150) / 100 : speed; + else if (ability == ABILITY_UNBURDEN && gDisableStructs[battler].unburdenActive) + speed *= 2; // stat stages speed *= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPEED]][0]; @@ -4776,8 +4778,6 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect) // various effects if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_TAILWIND) speed *= 2; - if (gDisableStructs[battler].unburdenActive) - speed *= 2; // paralysis drop if (gBattleMons[battler].status1 & STATUS1_PARALYSIS && ability != ABILITY_QUICK_FEET) diff --git a/test/battle/ability/unburden.c b/test/battle/ability/unburden.c new file mode 100644 index 0000000000..63a72e2ace --- /dev/null +++ b/test/battle/ability/unburden.c @@ -0,0 +1,101 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Unburden doubles speed once user uses item") +{ + GIVEN { + ASSUME(ItemId_GetHoldEffect(ITEM_GRASSY_SEED) == HOLD_EFFECT_SEEDS); + ASSUME(GetMoveEffect(MOVE_U_TURN) == EFFECT_HIT_ESCAPE); + PLAYER(SPECIES_DRIFBLIM) { Ability(ABILITY_UNBURDEN); Item(ITEM_GRASSY_SEED); Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(7); } + OPPONENT(SPECIES_RILLABOOM) { Speed(7); Ability(ABILITY_GRASSY_SURGE); } + } WHEN { + TURN { MOVE(opponent, MOVE_U_TURN); SEND_OUT(opponent, 1); } + TURN { } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, opponent); + ABILITY_POPUP(opponent, ABILITY_GRASSY_SURGE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + // Turn 2, doubled speed + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + } +} + +SINGLE_BATTLE_TEST("Unburden doubles speed once user gets their item knocked off") +{ + GIVEN { + ASSUME(MoveHasAdditionalEffect(MOVE_KNOCK_OFF, MOVE_EFFECT_KNOCK_OFF)); + PLAYER(SPECIES_DRIFBLIM) { Ability(ABILITY_UNBURDEN); Item(ITEM_POTION); Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(7); } + } WHEN { + TURN { MOVE(opponent, MOVE_KNOCK_OFF); } + TURN { } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + // Turn 2, doubled speed + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + } +} + +SINGLE_BATTLE_TEST("Unburden doesn't activate when item is consumed in Neutralizing Gas") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_U_TURN) == EFFECT_HIT_ESCAPE); + ASSUME(MoveHasAdditionalEffect(MOVE_KNOCK_OFF, MOVE_EFFECT_KNOCK_OFF)); + PLAYER(SPECIES_DRIFBLIM) { Ability(ABILITY_UNBURDEN); Item(ITEM_POTION); Speed(5); } + OPPONENT(SPECIES_WEEZING) { Speed(7); Ability(ABILITY_NEUTRALIZING_GAS); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(7); } + } WHEN { + TURN { MOVE(opponent, MOVE_KNOCK_OFF); } + TURN { MOVE(opponent, MOVE_U_TURN); SEND_OUT(opponent, 1); } + TURN { } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_NEUTRALIZING_GAS); + ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + // Turn 2, no speed increase + ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, opponent); + MESSAGE("The effects of the neutralizing gas wore off!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + // Turn 3, no speed increase + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + } +} + +SINGLE_BATTLE_TEST("Unburden doubling speed effect is ignored by Neutralizing Gas") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_U_TURN) == EFFECT_HIT_ESCAPE); + ASSUME(MoveHasAdditionalEffect(MOVE_KNOCK_OFF, MOVE_EFFECT_KNOCK_OFF)); + PLAYER(SPECIES_DRIFBLIM) { Ability(ABILITY_UNBURDEN); Item(ITEM_POTION); Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(7); } + OPPONENT(SPECIES_WEEZING) { Speed(7); Ability(ABILITY_NEUTRALIZING_GAS); } + } WHEN { + TURN { MOVE(opponent, MOVE_KNOCK_OFF); } + TURN { MOVE(opponent, MOVE_U_TURN); SEND_OUT(opponent, 1); } + TURN { MOVE(opponent, MOVE_U_TURN); SEND_OUT(opponent, 0); } + TURN { } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + // Turn 2, doubled speed + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, opponent); + ABILITY_POPUP(opponent, ABILITY_NEUTRALIZING_GAS); + // Turn 3, no speed increase + ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, opponent); + MESSAGE("The effects of the neutralizing gas wore off!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + // Turn 4, doubled speed + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + } +} From 5cd7bd77b32187ee82d0bb60611980b16299f1cb Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Thu, 24 Apr 2025 17:06:47 +0200 Subject: [PATCH 2/3] Fix various Toxic Spikes interactions (#6690) Co-authored-by: Hedara Co-authored-by: PhallenTree <168426989+PhallenTree@users.noreply.github.com> --- src/battle_script_commands.c | 8 +------ test/battle/ability/comatose.c | 32 ++++++++++++++++++++++++++++ test/battle/ability/immunity.c | 17 +++++++++++++++ test/battle/ability/purifying_salt.c | 17 +++++++++++++++ 4 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index ede0a7749d..72e05cfb68 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -8240,13 +8240,7 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler) else if (IsBattlerAffectedByHazards(battler, TRUE)) { i = GetBattlerAbility(battler); - if (!(gBattleMons[battler].status1 & STATUS1_ANY) - && !IS_BATTLER_OF_TYPE(battler, TYPE_STEEL) - && i != ABILITY_IMMUNITY - && i != ABILITY_PURIFYING_SALT - && !IsAbilityOnSide(battler, ABILITY_PASTEL_VEIL) - && !(gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD) - && !(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) + if (CanBePoisoned(gBattlerAttacker, battler, i)) { if (gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount >= 2) gBattleMons[battler].status1 |= STATUS1_TOXIC_POISON; diff --git a/test/battle/ability/comatose.c b/test/battle/ability/comatose.c index b23a6ca85f..d714ee5e3d 100644 --- a/test/battle/ability/comatose.c +++ b/test/battle/ability/comatose.c @@ -55,3 +55,35 @@ SINGLE_BATTLE_TEST("Comatose may be suppressed if pokemon transformed into a pok else if (move == MOVE_SLEEP_POWDER) { STATUS_ICON(opponent, sleep: TRUE); } } } + +SINGLE_BATTLE_TEST("Comatose pokemon doesn't get poisoned by Toxic Spikes on switch-in") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_KOMALA) { Ability(ABILITY_COMATOSE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TOXIC_SPIKES); } + TURN { SWITCH(player, 1); } + } SCENE { + NOT STATUS_ICON(player, STATUS1_POISON); + ABILITY_POPUP(player, ABILITY_COMATOSE); + NOT HP_BAR(player); + } +} + +SINGLE_BATTLE_TEST("Comatose pokemon don't get poisoned by Toxic Spikes on switch-in if forced in by phazing with Mold Breaker") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_KOMALA) { Ability(ABILITY_COMATOSE); } + OPPONENT(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); } + } WHEN { + TURN { MOVE(opponent, MOVE_TOXIC_SPIKES); } + TURN { MOVE(opponent, MOVE_DRAGON_TAIL); } + } SCENE { + NOT STATUS_ICON(player, STATUS1_POISON); + ABILITY_POPUP(player, ABILITY_COMATOSE); + NOT HP_BAR(player); + } +} diff --git a/test/battle/ability/immunity.c b/test/battle/ability/immunity.c index 92e32d31f3..365b5d0d50 100644 --- a/test/battle/ability/immunity.c +++ b/test/battle/ability/immunity.c @@ -45,3 +45,20 @@ SINGLE_BATTLE_TEST("Immunity prevents Toxic Spikes poison") NOT STATUS_ICON(opponent, poison: TRUE); } } + +SINGLE_BATTLE_TEST("Immunity doesn't prevent pokemon from being poisoned by Toxic Spikes on switch-in if forced in by phazing with Mold Breaker, but it cures it immediately") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_DRAGON_TAIL) == EFFECT_HIT_SWITCH_TARGET); + ASSUME(GetMoveEffect(MOVE_TOXIC_SPIKES) == EFFECT_TOXIC_SPIKES); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_SNORLAX) { Ability(ABILITY_IMMUNITY); } + OPPONENT(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); } + } WHEN { + TURN { MOVE(opponent, MOVE_TOXIC_SPIKES); } + TURN { MOVE(opponent, MOVE_DRAGON_TAIL); } + } SCENE { + STATUS_ICON(player, STATUS1_POISON); + NOT HP_BAR(player); + } +} diff --git a/test/battle/ability/purifying_salt.c b/test/battle/ability/purifying_salt.c index cb8fc6ca56..85d2e9c009 100644 --- a/test/battle/ability/purifying_salt.c +++ b/test/battle/ability/purifying_salt.c @@ -100,3 +100,20 @@ SINGLE_BATTLE_TEST("Purifying Salt user can't be poisoned by Toxic Spikes") EXPECT_EQ(player->status1, STATUS1_NONE); } } + +SINGLE_BATTLE_TEST("Purifying Salt doesn't prevent pokemon from being poisoned by Toxic Spikes on switch-in if forced in by phazing with Mold Breaker") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_DRAGON_TAIL) == EFFECT_HIT_SWITCH_TARGET); + ASSUME(GetMoveEffect(MOVE_TOXIC_SPIKES) == EFFECT_TOXIC_SPIKES); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_GARGANACL) { Ability(ABILITY_PURIFYING_SALT); } + OPPONENT(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); } + } WHEN { + TURN { MOVE(opponent, MOVE_TOXIC_SPIKES); } + TURN { MOVE(opponent, MOVE_DRAGON_TAIL); } + } SCENE { + STATUS_ICON(player, STATUS1_POISON); + HP_BAR(player); + } +} From d53c616e068c73713ffc1932947a4e4d60f8aedf Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Thu, 24 Apr 2025 20:13:03 +0200 Subject: [PATCH 3/3] Adds a whole bunch of new tests (#6685) --- test/battle/ability/analytic.c | 20 +++- test/battle/move_effect/aqua_ring.c | 30 +++++- test/battle/move_effect/attract.c | 106 +++++++++++++++++++- test/battle/move_effect/bestow.c | 134 ++++++++++++++++++++++++-- test/battle/move_effect/captivate.c | 113 +++++++++++++++++++++- test/battle/move_effect/entrainment.c | 48 ++++++++- 6 files changed, 430 insertions(+), 21 deletions(-) diff --git a/test/battle/ability/analytic.c b/test/battle/ability/analytic.c index 44c42ae2d5..526e0983f4 100644 --- a/test/battle/ability/analytic.c +++ b/test/battle/ability/analytic.c @@ -1,7 +1,25 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Analytic increases the power of moves by 30% if it's the last one that uses its move"); +SINGLE_BATTLE_TEST("Analytic increases the power of moves by 30% if it's the last one that uses its move", s16 damage) +{ + u32 speed; + + PARAMETRIZE { speed = 3; } + PARAMETRIZE { speed = 1; } + + GIVEN { + PLAYER(SPECIES_MAGNEMITE) { Ability(ABILITY_ANALYTIC); Speed(speed); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(2); } + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.3), results[1].damage); + } +} + TO_DO_BATTLE_TEST("Analytic takes into account modifications to speeed an priority (Gen 5-8)"); //Eg. Paralysis, Power Weight, Stall TO_DO_BATTLE_TEST("Analytic does not take into account modifications to speeed an priority (Gen 8)"); //Eg. Paralysis, Power Weight, Stall TO_DO_BATTLE_TEST("Analytic takes into account the turn order of what fainted Pokémon would've moved"); diff --git a/test/battle/move_effect/aqua_ring.c b/test/battle/move_effect/aqua_ring.c index 158c839d79..d137a35276 100644 --- a/test/battle/move_effect/aqua_ring.c +++ b/test/battle/move_effect/aqua_ring.c @@ -1,6 +1,32 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Aqua Ring recovers 1/16th HP at end of turn"); -TO_DO_BATTLE_TEST("Aqua Ring can be used under Heal Block but will not heal the user"); +SINGLE_BATTLE_TEST("Aqua Ring recovers 1/16th HP at end of turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(128); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_AQUA_RING); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_AQUA_RING, player); + } THEN { + EXPECT(player->hp == 58); + } +} + +SINGLE_BATTLE_TEST("Aqua Ring can be used under Heal Block but will not heal the user") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(128); Speed(50); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(100); } + } WHEN { + TURN { MOVE(opponent, MOVE_HEAL_BLOCK); MOVE(player, MOVE_AQUA_RING); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_AQUA_RING, player); + } THEN { + EXPECT(player->hp == 50); + } +} + TO_DO_BATTLE_TEST("Baton Pass passes Aqua Ring's effect"); diff --git a/test/battle/move_effect/attract.c b/test/battle/move_effect/attract.c index 748a88a950..be30483358 100644 --- a/test/battle/move_effect/attract.c +++ b/test/battle/move_effect/attract.c @@ -1,7 +1,105 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Attract causes the target to become infatuated with the user if they have opposite genders"); -TO_DO_BATTLE_TEST("Attract ignores type immunity"); -TO_DO_BATTLE_TEST("Attract bypasses Substitute"); -TO_DO_BATTLE_TEST("Attract fails if the target is already infatuated"); +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_ATTRACT) == EFFECT_ATTRACT); + ASSUME(gSpeciesInfo[SPECIES_NIDOKING].genderRatio == MON_MALE); + ASSUME(gSpeciesInfo[SPECIES_NIDOQUEEN].genderRatio == MON_FEMALE); +} + +SINGLE_BATTLE_TEST("Attract causes the target to become infatuated with the user if they have opposite genders") +{ + GIVEN { + PLAYER(SPECIES_NIDOQUEEN); + OPPONENT(SPECIES_NIDOKING); + } WHEN { + TURN { MOVE(player, MOVE_ATTRACT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ATTRACT, player); + MESSAGE("The opposing Nidoking fell in love!"); + } THEN { + EXPECT(opponent->status2 & STATUS2_INFATUATION); + } +} + +SINGLE_BATTLE_TEST("Attract ignores type immunity") +{ + GIVEN { + ASSUME(GetMoveType(MOVE_ATTRACT) == TYPE_NORMAL); + PLAYER(SPECIES_NIDOQUEEN); + OPPONENT(SPECIES_MISDREAVUS) { Gender(MON_MALE); } + } WHEN { + TURN { MOVE(player, MOVE_ATTRACT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ATTRACT, player); + MESSAGE("The opposing Misdreavus fell in love!"); + } THEN { + EXPECT(opponent->status2 & STATUS2_INFATUATION); + } +} + +SINGLE_BATTLE_TEST("Attract bypasses Substitute") +{ + GIVEN { + PLAYER(SPECIES_NIDOQUEEN) { Speed(90); } + OPPONENT(SPECIES_NIDOKING) { Speed(100); } + } WHEN { + TURN { MOVE(opponent, MOVE_SUBSTITUTE); } + TURN { MOVE(player, MOVE_ATTRACT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ATTRACT, player); + MESSAGE("The opposing Nidoking fell in love!"); + } THEN { + EXPECT(opponent->status2 & STATUS2_INFATUATION); + } +} + +SINGLE_BATTLE_TEST("Attract fails if the target is already infatuated") +{ + GIVEN { + PLAYER(SPECIES_NIDOQUEEN); + OPPONENT(SPECIES_NIDOKING); + } WHEN { + TURN { MOVE(player, MOVE_ATTRACT); } + TURN { MOVE(player, MOVE_ATTRACT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ATTRACT, player); + MESSAGE("The opposing Nidoking fell in love!"); + MESSAGE("Nidoqueen used Attract!"); + MESSAGE("But it failed!"); + } THEN { + EXPECT(opponent->status2 & STATUS2_INFATUATION); + } +} + +SINGLE_BATTLE_TEST("Attract fails when used on a Pokémon of the same gender") +{ + GIVEN { + PLAYER(SPECIES_NIDOQUEEN); + OPPONENT(SPECIES_NIDOQUEEN); + } WHEN { + TURN { MOVE(player, MOVE_ATTRACT); } + } SCENE { + MESSAGE("Nidoqueen used Attract!"); + MESSAGE("But it failed!"); + } THEN { + EXPECT(!(opponent->status2 & STATUS2_INFATUATION)); + } +} + +SINGLE_BATTLE_TEST("Attract fails when used on a genderless Pokémon") +{ + GIVEN { + ASSUME(gSpeciesInfo[SPECIES_STARMIE].genderRatio == MON_GENDERLESS); + PLAYER(SPECIES_NIDOQUEEN); + OPPONENT(SPECIES_STARMIE); + } WHEN { + TURN { MOVE(player, MOVE_ATTRACT); } + } SCENE { + MESSAGE("Nidoqueen used Attract!"); + MESSAGE("But it failed!"); + } THEN { + EXPECT(!(opponent->status2 & STATUS2_INFATUATION)); + } +} diff --git a/test/battle/move_effect/bestow.c b/test/battle/move_effect/bestow.c index 1161cdf8ae..24abdb3bea 100644 --- a/test/battle/move_effect/bestow.c +++ b/test/battle/move_effect/bestow.c @@ -1,10 +1,130 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Bestow transfers its held item to the target"); -TO_DO_BATTLE_TEST("Bestow fails if the user has no held item"); -TO_DO_BATTLE_TEST("Bestow fails if the target already has a held item"); -TO_DO_BATTLE_TEST("Bestow fails if the target is behind a Substitute"); -TO_DO_BATTLE_TEST("Bestow fails if the user is holding Mail"); -TO_DO_BATTLE_TEST("Bestow fails if the user's held item changes its form"); -TO_DO_BATTLE_TEST("Bestow fails if the user's held item is a Z-Crystal"); +SINGLE_BATTLE_TEST("Bestow transfers its held item to the target") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BESTOW); } + } THEN { + EXPECT(player->item == ITEM_NONE); + EXPECT(opponent->item == ITEM_SITRUS_BERRY); + } +} + +SINGLE_BATTLE_TEST("Bestow fails if the user has no held item") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BESTOW); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(player->item == ITEM_NONE); + EXPECT(opponent->item == ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Bestow fails if the target already has a held item") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_LUM_BERRY); } + } WHEN { + TURN { MOVE(player, MOVE_BESTOW); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(player->item == ITEM_SITRUS_BERRY); + EXPECT(opponent->item == ITEM_LUM_BERRY); + } +} + +#include "mail.h" +SINGLE_BATTLE_TEST("Bestow fails if the user is holding Mail") +{ + KNOWN_FAILING; + + GIVEN { + ASSUME(ItemIsMail(ITEM_ORANGE_MAIL)); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_ORANGE_MAIL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BESTOW); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(player->item == ITEM_ORANGE_MAIL); + EXPECT(opponent->item == ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Bestow fails if the user's held item is a Mega Stone") +{ + GIVEN { + PLAYER(SPECIES_BLAZIKEN) { Item(ITEM_BLAZIKENITE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BESTOW); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(player->item == ITEM_BLAZIKENITE); + EXPECT(opponent->item == ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Bestow fails if the user's held item is a Z-Crystal") +{ + GIVEN { + ASSUME(ItemId_GetHoldEffect(ITEM_FIGHTINIUM_Z) == HOLD_EFFECT_Z_CRYSTAL); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_FIGHTINIUM_Z); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BESTOW); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(player->item == ITEM_FIGHTINIUM_Z); + EXPECT(opponent->item == ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Bestow fails if the target is behind a Substitute") +{ + KNOWN_FAILING; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); Speed(50); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(100); } + } WHEN { + TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_BESTOW); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(player->item == ITEM_SITRUS_BERRY); + EXPECT(opponent->item == ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Bestow fails if the user's held item changes its form") +{ + KNOWN_FAILING; + + GIVEN { + PLAYER(SPECIES_GIRATINA_ORIGIN) { Item(ITEM_GRISEOUS_ORB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BESTOW); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(player->item == ITEM_GRISEOUS_ORB); + EXPECT(opponent->item == ITEM_NONE); + } +} + diff --git a/test/battle/move_effect/captivate.c b/test/battle/move_effect/captivate.c index 0da58983c7..224e6bef07 100644 --- a/test/battle/move_effect/captivate.c +++ b/test/battle/move_effect/captivate.c @@ -1,7 +1,112 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Captivate decreases the target's Sp. Attack if they're opposite gender from the user"); -TO_DO_BATTLE_TEST("Captivate fails if the target and user share gender"); -TO_DO_BATTLE_TEST("Captivate fails if the target is genderless"); -TO_DO_BATTLE_TEST("Captivate fails if the user is genderless"); +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_CAPTIVATE) == EFFECT_CAPTIVATE); + ASSUME(gSpeciesInfo[SPECIES_NIDOKING].genderRatio == MON_MALE); + ASSUME(gSpeciesInfo[SPECIES_NIDOQUEEN].genderRatio == MON_FEMALE); + ASSUME(gSpeciesInfo[SPECIES_STARMIE].genderRatio == MON_GENDERLESS); +} + +SINGLE_BATTLE_TEST("Captivate decreases the target's Sp. Attack if they're opposite gender from the user") +{ + GIVEN { + PLAYER(SPECIES_NIDOQUEEN); + OPPONENT(SPECIES_NIDOKING); + } WHEN { + TURN { MOVE(player, MOVE_CAPTIVATE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CAPTIVATE, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("The opposing Nidoking's Sp. Atk harshly fell!"); + } THEN { + EXPECT(opponent->statStages[STAT_SPATK] == 4); + } +} + +SINGLE_BATTLE_TEST("Captivate fails if the target and user share gender") +{ + GIVEN { + PLAYER(SPECIES_NIDOKING); + OPPONENT(SPECIES_NIDOKING); + } WHEN { + TURN { MOVE(player, MOVE_CAPTIVATE); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(opponent->statStages[STAT_SPATK] == 6); + } +} + +SINGLE_BATTLE_TEST("Captivate fails if the target is genderless") +{ + GIVEN { + PLAYER(SPECIES_NIDOQUEEN); + OPPONENT(SPECIES_STARMIE); + } WHEN { + TURN { MOVE(player, MOVE_CAPTIVATE); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(opponent->statStages[STAT_SPATK] == 6); + } +} + +SINGLE_BATTLE_TEST("Captivate fails if the user is genderless") +{ + GIVEN { + PLAYER(SPECIES_STARMIE); + OPPONENT(SPECIES_NIDOQUEEN); + } WHEN { + TURN { MOVE(player, MOVE_CAPTIVATE); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(opponent->statStages[STAT_SPATK] == 6); + } +} + +SINGLE_BATTLE_TEST("Captivate fails if both the user and the opponent are genderless") +{ + GIVEN { + PLAYER(SPECIES_STARMIE); + OPPONENT(SPECIES_STARMIE); + } WHEN { + TURN { MOVE(player, MOVE_CAPTIVATE); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(opponent->statStages[STAT_SPATK] == 6); + } +} + +SINGLE_BATTLE_TEST("Attract fails when used by a genderless Pokémon") +{ + GIVEN { + PLAYER(SPECIES_STARMIE); + OPPONENT(SPECIES_NIDOQUEEN); + } WHEN { + TURN { MOVE(player, MOVE_ATTRACT); } + } SCENE { + MESSAGE("Starmie used Attract!"); + MESSAGE("But it failed!"); + } THEN { + EXPECT(!(opponent->status2 & STATUS2_INFATUATION)); + } +} + +SINGLE_BATTLE_TEST("Attract fails if both the user and the target are genderless") +{ + GIVEN { + PLAYER(SPECIES_STARMIE); + OPPONENT(SPECIES_STARMIE); + } WHEN { + TURN { MOVE(player, MOVE_ATTRACT); } + } SCENE { + MESSAGE("Starmie used Attract!"); + MESSAGE("But it failed!"); + } THEN { + EXPECT(!(opponent->status2 & STATUS2_INFATUATION)); + } +} diff --git a/test/battle/move_effect/entrainment.c b/test/battle/move_effect/entrainment.c index cccae86759..fd47937f04 100644 --- a/test/battle/move_effect/entrainment.c +++ b/test/battle/move_effect/entrainment.c @@ -14,6 +14,48 @@ AI_DOUBLE_BATTLE_TEST("AI prefers Entrainment'ing good abilities onto partner wi } } -TO_DO_BATTLE_TEST("Entrainment changes the target's Ability to match the user's"); -TO_DO_BATTLE_TEST("Entrainment fails if the user's ability has cantBeCopied flag"); -TO_DO_BATTLE_TEST("Entrainment fails if the targets's ability has cantBeOverwritten flag"); +SINGLE_BATTLE_TEST("Entrainment changes the target's Ability to match the user's") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_SHADOW_TAG); } + } WHEN { + TURN { MOVE(player, MOVE_ENTRAINMENT); } + } THEN { + EXPECT(opponent->ability == ABILITY_TELEPATHY); + } +} + +SINGLE_BATTLE_TEST("Entrainment fails if the user's ability has cantBeCopied flag") +{ + GIVEN { + ASSUME(gAbilitiesInfo[ABILITY_MULTITYPE].cantBeCopied); + PLAYER(SPECIES_ARCEUS) { Ability(ABILITY_MULTITYPE); } + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_SHADOW_TAG); } + } WHEN { + TURN { MOVE(player, MOVE_ENTRAINMENT); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(player->ability == ABILITY_MULTITYPE); + EXPECT(opponent->ability == ABILITY_SHADOW_TAG); + } +} + +SINGLE_BATTLE_TEST("Entrainment fails if the target's ability has cantBeOverwritten flag") +{ + GIVEN { + ASSUME(gAbilitiesInfo[ABILITY_MULTITYPE].cantBeOverwritten); + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } + OPPONENT(SPECIES_ARCEUS) { Ability(ABILITY_MULTITYPE); } + } WHEN { + TURN { MOVE(player, MOVE_ENTRAINMENT); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(player->ability == ABILITY_TELEPATHY); + EXPECT(opponent->ability == ABILITY_MULTITYPE); + } +} + +TO_DO_BATTLE_TEST("Entrainment fails on Dynamaxed Pokémon");