diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 15ebaf7ece..f1161c7340 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -8712,28 +8712,6 @@ BattleScript_ActivateTerrainEffects_Increment: restoretarget return -BattleScript_ActivateSwitchInAbilities: - copybyte sBATTLER, gBattlerAttacker - setbyte gBattlerAttacker, 0 -BattleScript_ActivateSwitchInAbilities_Loop: - switchinabilities BS_ATTACKER -BattleScript_ActivateSwitchInAbilities_Increment: - addbyte gBattlerAttacker, 1 - jumpifbytenotequal gBattlerAttacker, gBattlersCount, BattleScript_ActivateSwitchInAbilities_Loop - copybyte gBattlerAttacker, sBATTLER - return - -BattleScript_ActivateTerrainAbilities: - savetarget - setbyte gBattlerTarget, 0 -BattleScript_ActivateTerrainAbilities_Loop: - activateterrainchangeabilities BS_ATTACKER -BattleScript_ActivateTerrainAbilities_Increment: - addbyte gBattlerTarget, 1 - jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_ActivateTerrainAbilities_Loop - restoretarget - return - BattleScript_ElectricSurgeActivates:: pause B_WAIT_TIME_SHORT call BattleScript_AbilityPopUp @@ -9902,6 +9880,14 @@ BattleScript_MirrorHerbCopyStatChange:: copybyte gBattlerAttacker, sSAVED_BATTLER @ restore the original attacker just to be safe return +BattleScript_OpportunistCopyStatChange:: + call BattleScript_AbilityPopUp + printstring STRINGID_OPPORTUNISTCOPIED + waitmessage B_WAIT_TIME_LONG + call BattleScript_TotemVar_Ret + copybyte gBattlerAttacker, sSAVED_BATTLER @ restore the original attacker just to be safe + end3 + BattleScript_TotemVar:: call BattleScript_TotemVar_Ret end2 diff --git a/include/battle.h b/include/battle.h index 9bb3fb7597..a9b83098e5 100644 --- a/include/battle.h +++ b/include/battle.h @@ -149,6 +149,7 @@ struct ProtectStruct u16 shellTrap:1; u16 silkTrapped:1; u16 eatMirrorHerb:1; + u16 activateOpportunist:2; // 2 - to copy stats. 1 - stats copied (do not repeat). 0 - no stats to copy u32 physicalDmg; u32 specialDmg; u8 physicalBattlerId; @@ -901,7 +902,7 @@ struct MonSpritesGfx u16 *buffer; }; -struct TotemBoost +struct QueuedStatBoost { u8 stats; // bitfield for each battle stat that is set if the stat changes s8 statChanges[NUM_BATTLE_STATS - 1]; // highest bit being set decreases the stat @@ -1014,7 +1015,7 @@ extern u32 gFieldStatuses; extern struct FieldTimer gFieldTimers; extern u8 gBattlerAbility; extern u16 gPartnerSpriteId; -extern struct TotemBoost gTotemBoosts[MAX_BATTLERS_COUNT]; +extern struct QueuedStatBoost gQueuedStatBoosts[MAX_BATTLERS_COUNT]; extern void (*gPreBattleCallback1)(void); extern void (*gBattleMainFunc)(void); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 335aafb589..252e208cbb 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -1,6 +1,7 @@ #ifndef GUARD_BATTLE_SCRIPTS_H #define GUARD_BATTLE_SCRIPTS_H +extern const u8 BattleScript_OpportunistCopyStatChange[]; extern const u8 BattleScript_MirrorHerbCopyStatChange[]; extern const u8 BattleScript_MirrorHerbCopyStatChangeEnd2[]; extern const u8 BattleScript_NotAffected[]; diff --git a/include/battle_util.h b/include/battle_util.h index 275a27303b..d420f58ad2 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -38,6 +38,7 @@ #define ABILITYEFFECT_ON_TERRAIN 15 #define ABILITYEFFECT_SWITCH_IN_TERRAIN 16 #define ABILITYEFFECT_SWITCH_IN_WEATHER 17 +#define ABILITYEFFECT_OPPORTUNIST 18 // Special cases #define ABILITYEFFECT_MUD_SPORT 252 // Only used if B_SPORT_TURNS < GEN_6 #define ABILITYEFFECT_WATER_SPORT 253 // Only used if B_SPORT_TURNS < GEN_6 diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index ee98bccec7..1775b9528e 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -323,8 +323,9 @@ #define MOVEEND_DANCER 31 #define MOVEEND_EMERGENCY_EXIT 32 #define MOVEEND_SYMBIOSIS 33 -#define MOVEEND_CLEAR_BITS 34 -#define MOVEEND_COUNT 35 +#define MOVEEND_OPPORTUNIST 34 // Occurs after other stat change items/abilities to try and copy the boosts +#define MOVEEND_CLEAR_BITS 35 +#define MOVEEND_COUNT 36 // switch cases #define B_SWITCH_NORMAL 0 diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 86a80c33cb..3d3018d96b 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -670,8 +670,9 @@ #define STRINGID_CURRENTMOVECANTSELECT 668 #define STRINGID_TARGETISBEINGSALTCURED 669 #define STRINGID_TARGETISHURTBYSALTCURE 670 +#define STRINGID_OPPORTUNISTCOPIED 671 -#define BATTLESTRINGS_COUNT 671 +#define BATTLESTRINGS_COUNT 672 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, diff --git a/src/battle_main.c b/src/battle_main.c index 6671513253..f5fb6222d5 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -236,7 +236,7 @@ EWRAM_DATA u32 gFieldStatuses = 0; EWRAM_DATA struct FieldTimer gFieldTimers = {0}; EWRAM_DATA u8 gBattlerAbility = 0; EWRAM_DATA u16 gPartnerSpriteId = 0; -EWRAM_DATA struct TotemBoost gTotemBoosts[MAX_BATTLERS_COUNT] = {0}; +EWRAM_DATA struct QueuedStatBoost gQueuedStatBoosts[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA bool8 gHasFetchedBall = FALSE; EWRAM_DATA u8 gLastUsedBall = 0; EWRAM_DATA u16 gLastThrownBall = 0; @@ -3790,14 +3790,13 @@ static void TryDoEventsBeforeFirstTurn(void) // Totem boosts for (i = 0; i < gBattlersCount; i++) { - if (gTotemBoosts[i].stats != 0) + if (gQueuedStatBoosts[i].stats != 0 && !gProtectStructs[i].eatMirrorHerb && gProtectStructs[i].activateOpportunist == 0) { gBattlerAttacker = i; BattleScriptExecute(BattleScript_TotemVar); return; } } - memset(gTotemBoosts, 0, sizeof(gTotemBoosts)); // erase all totem boosts just to be safe // Check neutralizing gas if (AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, 0, 0, 0, 0) != 0) @@ -3821,6 +3820,9 @@ static void TryDoEventsBeforeFirstTurn(void) if (ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, gBattlerByTurnOrder[gBattleStruct->switchInItemsCounter++], FALSE)) return; } + + if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, 0, 0, 0, 0)) + return; for (i = 0; i < MAX_BATTLERS_COUNT; i++) { @@ -3854,6 +3856,8 @@ static void TryDoEventsBeforeFirstTurn(void) gMoveResultFlags = 0; gRandomTurnNumber = Random(); + + memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts)); // erase all totem boosts just to be safe SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers @@ -5737,9 +5741,9 @@ void SetTotemBoost(void) { if (*(&gSpecialVar_0x8001 + i)) { - gTotemBoosts[battler].stats |= (1 << i); - gTotemBoosts[battler].statChanges[i] = *(&gSpecialVar_0x8001 + i); - gTotemBoosts[battler].stats |= 0x80; // used as a flag for the "totem flared to life" script + gQueuedStatBoosts[battler].stats |= (1 << i); + gQueuedStatBoosts[battler].statChanges[i] = *(&gSpecialVar_0x8001 + i); + gQueuedStatBoosts[battler].stats |= 0x80; // used as a flag for the "totem flared to life" script } } } diff --git a/src/battle_message.c b/src/battle_message.c index 9ca01f8cf9..54c330a683 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -807,9 +807,11 @@ static const u8 sText_TeamGainedEXP[] = _("The rest of your team gained EXP.\nPo static const u8 sText_CurrentMoveCantSelect[] = _("{B_BUFF1} cannot be used!\p"); static const u8 sText_TargetIsBeingSaltCured[] = _("{B_DEF_NAME_WITH_PREFIX} is being salt cured!"); static const u8 sText_TargetIsHurtBySaltCure[] = _("{B_DEF_NAME_WITH_PREFIX} is hurt by {B_BUFF1}!"); +static const u8 sText_OpportunistCopied[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} copied its\nopponent's stat changes!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { + [STRINGID_OPPORTUNISTCOPIED - BATTLESTRINGS_TABLE_START] = sText_OpportunistCopied, [STRINGID_TARGETISHURTBYSALTCURE - BATTLESTRINGS_TABLE_START] = sText_TargetIsHurtBySaltCure, [STRINGID_TARGETISBEINGSALTCURED - BATTLESTRINGS_TABLE_START] = sText_TargetIsBeingSaltCured, [STRINGID_CURRENTMOVECANTSELECT - BATTLESTRINGS_TABLE_START] = sText_CurrentMoveCantSelect, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 7952a75b4e..8fc37471de 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5429,6 +5429,12 @@ static void Cmd_moveend(void) effect = TRUE; gBattleScripting.moveendState++; break; + case MOVEEND_OPPORTUNIST: + if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, 0, 0, 0, 0)) + effect = TRUE; // it loops through all battlers, so we increment after its done with all battlers + else + gBattleScripting.moveendState++; + break; case MOVEEND_STATUS_IMMUNITY_ABILITIES: // status immunities if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, 0, 0, 0, 0)) effect = TRUE; // it loops through all battlers, so we increment after its done with all battlers @@ -6963,6 +6969,8 @@ static void Cmd_switchineffects(void) { if (DoSwitchInAbilitiesItems(battler)) return; + else if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0)) + return; } gDisableStructs[battler].stickyWebDone = FALSE; @@ -9062,6 +9070,7 @@ static void Cmd_various(void) AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, battler, 0, 0, 0); AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, 0, 0, 0); AbilityBattleEffects(ABILITYEFFECT_TRACE2, battler, 0, 0, 0); + AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0); return; } case VARIOUS_SAVE_TARGET: @@ -9883,7 +9892,7 @@ static void Cmd_various(void) { VARIOUS_ARGS(const u8 *jumpInstr); battler = gBattlerAttacker; - if (gTotemBoosts[battler].stats == 0) + if (gQueuedStatBoosts[battler].stats == 0) { gBattlescriptCurrInstr = cmd->nextInstr; // stats done, exit } @@ -9891,19 +9900,19 @@ static void Cmd_various(void) { for (i = 0; i < (NUM_BATTLE_STATS - 1); i++) { - if (gTotemBoosts[battler].stats & (1 << i)) + if (gQueuedStatBoosts[battler].stats & (1 << i)) { - if (gTotemBoosts[battler].statChanges[i] <= -1) - SET_STATCHANGER(i + 1, abs(gTotemBoosts[battler].statChanges[i]), TRUE); + if (gQueuedStatBoosts[battler].statChanges[i] <= -1) + SET_STATCHANGER(i + 1, abs(gQueuedStatBoosts[battler].statChanges[i]), TRUE); else - SET_STATCHANGER(i + 1, gTotemBoosts[battler].statChanges[i], FALSE); + SET_STATCHANGER(i + 1, gQueuedStatBoosts[battler].statChanges[i], FALSE); - gTotemBoosts[battler].stats &= ~(1 << i); + gQueuedStatBoosts[battler].stats &= ~(1 << i); gBattleScripting.battler = battler; gBattlerTarget = battler; - if (gTotemBoosts[battler].stats & 0x80) + if (gQueuedStatBoosts[battler].stats & 0x80) { - gTotemBoosts[battler].stats &= ~0x80; // set 'aura flared to life' flag + gQueuedStatBoosts[battler].stats &= ~0x80; // set 'aura flared to life' flag gBattlescriptCurrInstr = BattleScript_TotemFlaredToLife; } else @@ -11617,12 +11626,19 @@ static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr { if (GetBattlerSide(index) == GetBattlerSide(battler)) continue; // Only triggers on opposing side - if (GetBattlerHoldEffect(index, TRUE) == HOLD_EFFECT_MIRROR_HERB + if (GetBattlerAbility(index) == ABILITY_OPPORTUNIST + && gProtectStructs[battler].activateOpportunist == 0) // don't activate opportunist on other mon's opportunist raises + { + gProtectStructs[index].activateOpportunist = 2; // set stats to copy + gQueuedStatBoosts[index].stats |= (1 << (statId - 1)); // -1 to start at atk + gQueuedStatBoosts[index].statChanges[statId - 1] += statValue; // cumulative in case of multiple opponent boosts + } + else if (GetBattlerHoldEffect(index, TRUE) == HOLD_EFFECT_MIRROR_HERB && gBattleMons[index].statStages[statId] < MAX_STAT_STAGE) { gProtectStructs[index].eatMirrorHerb = 1; - gTotemBoosts[index].stats |= (1 << (statId - 1)); // -1 to start at atk - gTotemBoosts[index].statChanges[statId - 1] = statValue; + gQueuedStatBoosts[index].stats |= (1 << (statId - 1)); // -1 to start at atk + gQueuedStatBoosts[index].statChanges[statId - 1] = statValue; } } } diff --git a/src/battle_util.c b/src/battle_util.c index 5de2523c29..51b4338e8d 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5789,6 +5789,27 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 break; } break; + case ABILITYEFFECT_OPPORTUNIST: + /* Similar to ABILITYEFFECT_IMMUNITY in that it loops through all battlers. + * Is called after ABILITYEFFECT_ON_SWITCHIN to copy any boosts + * from switch in abilities e.g. intrepid sword, as + */ + for (battler = 0; battler < gBattlersCount; battler++) + { + switch (GetBattlerAbility(battler)) + { + case ABILITY_OPPORTUNIST: + if (gProtectStructs[battler].activateOpportunist == 2) { + gBattleScripting.savedBattler = gBattlerAttacker; + gBattleScripting.battler = gBattlerAttacker = gBattlerAbility = battler; + gProtectStructs[battler].activateOpportunist--; + BattleScriptPushCursorAndCallback(BattleScript_OpportunistCopyStatChange); + effect = 1; + } + break; + } + } + break; case ABILITYEFFECT_IMMUNITY: // 5 for (battler = 0; battler < gBattlersCount; battler++) { @@ -5847,6 +5868,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect = 4; break; } + if (effect != 0) { switch (effect) diff --git a/test/battle/ability/opportunist.c b/test/battle/ability/opportunist.c new file mode 100644 index 0000000000..f344bd026f --- /dev/null +++ b/test/battle/ability/opportunist.c @@ -0,0 +1,106 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Opportunist only copies foe's positive stat changes in a turn", s16 damage) +{ + u32 ability; + PARAMETRIZE { ability = ABILITY_NONE; } + PARAMETRIZE { ability = ABILITY_OPPORTUNIST; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(5); Ability(ability); } + } WHEN { + TURN { MOVE(player, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE); } + } SCENE { + if (ability == ABILITY_NONE) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SMASH, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + } + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage); + // stat boosts should be the same + EXPECT_EQ(player->statStages[STAT_ATK], opponent->statStages[STAT_ATK]); + EXPECT_EQ(player->statStages[STAT_SPATK], opponent->statStages[STAT_SPATK]); + EXPECT_EQ(player->statStages[STAT_SPEED], opponent->statStages[STAT_SPEED]); + // opportunist should not copy stat drops from shell smash + EXPECT_LT(player->statStages[STAT_DEF], opponent->statStages[STAT_DEF]); + EXPECT_LT(player->statStages[STAT_SPDEF], opponent->statStages[STAT_SPDEF]); + } +} + + +DOUBLE_BATTLE_TEST("Opportunist raises Attack only once when partner has Intimidate against Contrary foe in a double battle", s16 damageLeft, s16 damageRight) +{ + u32 abilityLeft, abilityRight; + + PARAMETRIZE { abilityLeft = ABILITY_CONTRARY; abilityRight = ABILITY_CONTRARY; } + PARAMETRIZE { abilityLeft = ABILITY_TANGLED_FEET; abilityRight = ABILITY_TANGLED_FEET; } + PARAMETRIZE { abilityLeft = ABILITY_CONTRARY; abilityRight = ABILITY_TANGLED_FEET; } + PARAMETRIZE { abilityLeft = ABILITY_TANGLED_FEET; abilityRight = ABILITY_CONTRARY; } + + GIVEN { + PLAYER(SPECIES_MIGHTYENA) { Ability(ABILITY_INTIMIDATE); } + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_OPPORTUNIST); } + OPPONENT(SPECIES_SPINDA) { Ability(abilityLeft); } + OPPONENT(SPECIES_SPINDA) { Ability(abilityRight); } + } WHEN { + TURN { MOVE(opponentLeft, MOVE_TACKLE, target: playerLeft); MOVE(opponentRight, MOVE_TACKLE, target: playerRight); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE); + if (abilityLeft == ABILITY_CONTRARY) { + ABILITY_POPUP(opponentLeft, ABILITY_CONTRARY); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + MESSAGE("Foe Spinda's Attack rose!"); + } else { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + MESSAGE("Mightyena's Intimidate cuts Foe Spinda's attack!"); + } + if (abilityRight == ABILITY_CONTRARY) { + ABILITY_POPUP(opponentRight, ABILITY_CONTRARY); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + MESSAGE("Foe Spinda's Attack rose!"); + } else { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + MESSAGE("Mightyena's Intimidate cuts Foe Spinda's attack!"); + } + + if ((abilityLeft == ABILITY_CONTRARY && abilityRight != ABILITY_CONTRARY) + || (abilityLeft != ABILITY_CONTRARY && abilityRight == ABILITY_CONTRARY)) { + ABILITY_POPUP(playerRight, ABILITY_OPPORTUNIST); + MESSAGE("Wobbuffet copied its opponent's stat changes!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + MESSAGE("Wobbuffet's Attack rose!"); + } else if (abilityLeft == ABILITY_CONTRARY && abilityRight == ABILITY_CONTRARY) { + ABILITY_POPUP(playerRight, ABILITY_OPPORTUNIST); + MESSAGE("Wobbuffet copied its opponent's stat changes!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + MESSAGE("Wobbuffet's Attack sharply rose!"); + } + + HP_BAR(playerLeft, captureDamage: &results[i].damageLeft); + HP_BAR(playerRight, captureDamage: &results[i].damageRight); + } THEN { + EXPECT_EQ(opponentLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE + (abilityLeft == ABILITY_CONTRARY ? 1 : - 1)); + EXPECT_EQ(opponentRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + (abilityRight == ABILITY_CONTRARY ? 1 : - 1)); + if ((abilityLeft == ABILITY_CONTRARY && abilityRight != ABILITY_CONTRARY) + || (abilityLeft != ABILITY_CONTRARY && abilityRight == ABILITY_CONTRARY)) { + EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + } else if (abilityLeft == ABILITY_CONTRARY && abilityRight == ABILITY_CONTRARY) { + EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2); + } + } + FINALLY { + EXPECT_MUL_EQ(results[1].damageLeft, Q_4_12(2.25), results[0].damageLeft); + EXPECT_MUL_EQ(results[1].damageRight, Q_4_12(2.25), results[0].damageRight); + } +} + +TO_DO_BATTLE_TEST("Opportunist doesn't copy ally stat increases"); +TO_DO_BATTLE_TEST("Opportunist doesn't copy foe stat increases gained via Opportunist"); +TO_DO_BATTLE_TEST("Opportunist copies foe stat increased gained via Swagger and Flatter"); diff --git a/test/battle/hold_effect/mirror_herb.c b/test/battle/hold_effect/mirror_herb.c index 1e9058b08e..9191f3fee1 100644 --- a/test/battle/hold_effect/mirror_herb.c +++ b/test/battle/hold_effect/mirror_herb.c @@ -6,7 +6,7 @@ ASSUMPTIONS ASSUME(gItems[ITEM_MIRROR_HERB].holdEffect == HOLD_EFFECT_MIRROR_HERB); } -SINGLE_BATTLE_TEST("Mirror Herb copies all of foe's stat changes in a turn", s16 damage) +SINGLE_BATTLE_TEST("Mirror Herb copies all of foe's positive stat changes in a turn", s16 damage) { u32 item; PARAMETRIZE { item = ITEM_NONE; } @@ -34,7 +34,7 @@ SINGLE_BATTLE_TEST("Mirror Herb copies all of foe's stat changes in a turn", s16 } } -SINGLE_BATTLE_TEST("Mirror Herb copies all of of Stuff Cheeks") +SINGLE_BATTLE_TEST("Mirror Herb copies all of Stuff Cheeks' stat boosts") { GIVEN { ASSUME(gItems[ITEM_LIECHI_BERRY].holdEffect == HOLD_EFFECT_ATTACK_UP);