diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 23a7632bd..ace2f707b 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -10024,23 +10024,42 @@ BattleScript_PrintPlayerForfeitedLinkBattle:: end2 BattleScript_TotemFlaredToLife:: - playanimation BS_ATTACKER, B_ANIM_TOTEM_FLARE + playanimation BS_ATTACKER, B_ANIM_TOTEM_FLARE, NULL printstring STRINGID_AURAFLAREDTOLIFE waitmessage B_WAIT_TIME_LONG - goto BattleScript_ApplyTotemVarBoost + call BattleScript_ApplyTotemVarBoost + end2 + +@ remove the mirror herb, do totem loop +BattleScript_MirrorHerbCopyStatChangeEnd2:: + call BattleScript_MirrorHerbCopyStatChange + end2 + +BattleScript_MirrorHerbCopyStatChange:: + playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT, NULL + printstring STRINGID_MIRRORHERBCOPIED + waitmessage B_WAIT_TIME_LONG + removeitem BS_SCRIPTING + call BattleScript_TotemVar_Ret + copybyte gBattlerAttacker, sSAVED_BATTLER @ restore the original attacker just to be safe + return BattleScript_TotemVar:: + call BattleScript_TotemVar_Ret + end2 + +BattleScript_TotemVar_Ret:: gettotemboost BattleScript_ApplyTotemVarBoost BattleScript_TotemVarEnd: - end2 + return BattleScript_ApplyTotemVarBoost: statbuffchange STAT_CHANGE_ALLOW_PTR, BattleScript_TotemVarEnd setgraphicalstatchangevalues playanimation BS_SCRIPTING, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1 -BattleScript_TotemVarPrintStatMsg: printfromtable gStatUpStringIds waitmessage B_WAIT_TIME_LONG - goto BattleScript_TotemVar @loop until stats bitfield is empty + goto BattleScript_TotemVar_Ret @loop until stats bitfield is empty + BattleScript_AnnounceAirLockCloudNine:: call BattleScript_AbilityPopUp diff --git a/include/battle.h b/include/battle.h index 35c3525b8..466a9138d 100644 --- a/include/battle.h +++ b/include/battle.h @@ -147,6 +147,7 @@ struct ProtectStruct u16 quash:1; u16 shellTrap:1; u16 silkTrapped:1; + u16 eatMirrorHerb:1; u32 physicalDmg; u32 specialDmg; u8 physicalBattlerId; diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 8cd8606a5..d387fd155 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -1,6 +1,8 @@ #ifndef GUARD_BATTLE_SCRIPTS_H #define GUARD_BATTLE_SCRIPTS_H +extern const u8 BattleScript_MirrorHerbCopyStatChange[]; +extern const u8 BattleScript_MirrorHerbCopyStatChangeEnd2[]; extern const u8 BattleScript_NotAffected[]; extern const u8 BattleScript_HitFromCritCalc[]; extern const u8 BattleScript_MoveEnd[]; diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 48abc03b9..9ce49894e 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -659,8 +659,9 @@ #define STRINGID_PKMNFROSTBITEHEALED 657 #define STRINGID_PKMNFROSTBITEHEALED2 658 #define STRINGID_PKMNFROSTBITEHEALEDBY 659 +#define STRINGID_MIRRORHERBCOPIED 660 -#define BATTLESTRINGS_COUNT 660 +#define BATTLESTRINGS_COUNT 661 // 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_message.c b/src/battle_message.c index d4709235f..be3442d6c 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -794,9 +794,11 @@ static const u8 sText_ItemRestoredSpeciesHealth[] = _("{B_BUFF1} had its\nHP res static const u8 sText_ItemCuredSpeciesStatus[] = _("{B_BUFF1} had\nits status healed!"); static const u8 sText_ItemRestoredSpeciesPP[] = _("{B_BUFF1} had its\nPP restored!"); static const u8 sText_AtkTrappedDef[] = _("{B_ATK_NAME_WITH_PREFIX} trapped\nthe {B_DEF_NAME_WITH_PREFIX}!"); +static const u8 sText_MirrorHerbCopied[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} used its {B_LAST_ITEM}\nto mirror its opponent's stat changes!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { + [STRINGID_MIRRORHERBCOPIED - BATTLESTRINGS_TABLE_START] = sText_MirrorHerbCopied, [STRINGID_THUNDERCAGETRAPPED - BATTLESTRINGS_TABLE_START] = sText_AtkTrappedDef, [STRINGID_ITEMRESTOREDSPECIESHEALTH - BATTLESTRINGS_TABLE_START] = sText_ItemRestoredSpeciesHealth, [STRINGID_ITEMCUREDSPECIESSTATUS - BATTLESTRINGS_TABLE_START] = sText_ItemCuredSpeciesStatus, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 593d14031..1187267b2 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -12130,6 +12130,20 @@ static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr { gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == gActiveBattler); gProtectStructs[gActiveBattler].statRaised = TRUE; + + // check mirror herb + for (index = 0; index < gBattlersCount; index++) + { + if (GetBattlerSide(index) == GetBattlerSide(gActiveBattler)) + continue; // Only triggers on opposing side + 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; + } + } } } diff --git a/src/battle_util.c b/src/battle_util.c index 831e65829..abdeed756 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6780,6 +6780,26 @@ static bool32 GetMentalHerbEffect(u8 battlerId) return ret; } +static u8 TryConsumeMirrorHerb(u8 battlerId, bool32 execute) +{ + u8 effect = 0; + + if (gProtectStructs[battlerId].eatMirrorHerb) { + gLastUsedItem = gBattleMons[battlerId].item; + gBattleScripting.savedBattler = gBattlerAttacker; + gBattleScripting.battler = gBattlerAttacker = battlerId; + gProtectStructs[battlerId].eatMirrorHerb = 0; + if (execute) { + BattleScriptExecute(BattleScript_MirrorHerbCopyStatChangeEnd2); + } else { + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_MirrorHerbCopyStatChange; + } + effect = ITEM_STATS_CHANGE; + } + return effect; +} + static u8 ItemEffectMoveEnd(u32 battlerId, u16 holdEffect) { u8 effect = 0; @@ -6983,6 +7003,9 @@ static u8 ItemEffectMoveEnd(u32 battlerId, u16 holdEffect) BattleScriptPushCursorAndCallback(BattleScript_BerserkGeneRet); effect = ITEM_STATS_CHANGE; break; + case HOLD_EFFECT_MIRROR_HERB: + effect = TryConsumeMirrorHerb(battlerId, FALSE); + break; } return effect; @@ -7547,6 +7570,9 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) BattleScriptPushCursorAndCallback(BattleScript_BerserkGeneRet); effect = ITEM_STATS_CHANGE; break; + case HOLD_EFFECT_MIRROR_HERB: + effect = TryConsumeMirrorHerb(battlerId, TRUE); + break; } if (effect != 0) diff --git a/test/hold_effect_mirror_herb.c b/test/hold_effect_mirror_herb.c new file mode 100644 index 000000000..fc0a6de95 --- /dev/null +++ b/test/hold_effect_mirror_herb.c @@ -0,0 +1,49 @@ +#include "global.h" +#include "test_battle.h" + +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) +{ + u32 item; + PARAMETRIZE{ item = ITEM_NONE; } + PARAMETRIZE{ item = ITEM_MIRROR_HERB; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(5); Item(item); } + } WHEN { + TURN { MOVE(player, MOVE_DRAGON_DANCE); } + TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE); } + } SCENE { + if (item == ITEM_NONE) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, 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(1.5), results[1].damage); + EXPECT_EQ(player->statStages[STAT_ATK], opponent->statStages[STAT_ATK]); + EXPECT_EQ(player->statStages[STAT_SPEED], opponent->statStages[STAT_SPEED]); + } +} + +SINGLE_BATTLE_TEST("Mirror Herb copies all of of Stuff Cheeks", s16 damage) +{ + GIVEN { + ASSUME(gItems[ITEM_LIECHI_BERRY].holdEffect == HOLD_EFFECT_ATTACK_UP); + PLAYER(SPECIES_SKWOVET) { Item(ITEM_LIECHI_BERRY); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_MIRROR_HERB); } + } WHEN { + TURN { MOVE(player, MOVE_STUFF_CHEEKS); } + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], opponent->statStages[STAT_ATK]); + EXPECT_EQ(player->statStages[STAT_DEF], opponent->statStages[STAT_DEF]); + } +}