From 46e6324fe2b8b10d36f8a9fafdea87ca862c92a7 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Fri, 29 Mar 2024 17:43:05 +0100 Subject: [PATCH] Fix White Herb Known Failing tests (#4258) * Fix White Herb Known Failing tests * get out agbcc * remove unneeded white herb test --- asm/macros/battle_script.inc | 5 + data/battle_scripts_1.s | 17 +- include/battle_util.h | 1 + include/constants/battle_script_commands.h | 6 +- src/battle_script_commands.c | 252 +++++++++++++-------- src/battle_util.c | 74 +++--- test/battle/hold_effect/white_herb.c | 74 ++---- 7 files changed, 229 insertions(+), 200 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index d2a8115089..561ea6d146 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1360,6 +1360,11 @@ .byte \battler .4byte \jumpInstr .endm + + .macro itemstatchangeeffects battler:req + callnative BS_RunStatChangeItems + .byte \battler + .endm .macro allyswitchswapbattlers callnative BS_AllySwitchSwapBattler diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index a23423b42e..508366e9b7 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7742,11 +7742,12 @@ BattleScript_ActivateWeatherAbilities_Increment: restoretarget return -BattleScript_TryAdrenalineOrb: - jumpifnoholdeffect BS_TARGET, HOLD_EFFECT_ADRENALINE_ORB, BattleScript_TryAdrenalineOrbRet - jumpifstat BS_TARGET, CMP_EQUAL, STAT_SPEED, 12, BattleScript_TryAdrenalineOrbRet +BattleScript_TryIntimidateHoldEffects: + itemstatchangeeffects BS_TARGET + jumpifnoholdeffect BS_TARGET, HOLD_EFFECT_ADRENALINE_ORB, BattleScript_TryIntimidateHoldEffectsRet + jumpifstat BS_TARGET, CMP_EQUAL, STAT_SPEED, 12, BattleScript_TryIntimidateHoldEffectsRet setstatchanger STAT_SPEED, 1, FALSE - statbuffchange STAT_CHANGE_NOT_PROTECT_AFFECTED | MOVE_EFFECT_CERTAIN | STAT_CHANGE_ALLOW_PTR, BattleScript_TryAdrenalineOrbRet + statbuffchange STAT_CHANGE_NOT_PROTECT_AFFECTED | MOVE_EFFECT_CERTAIN | STAT_CHANGE_ALLOW_PTR, BattleScript_TryIntimidateHoldEffectsRet playanimation BS_TARGET, B_ANIM_HELD_ITEM_EFFECT setgraphicalstatchangevalues playanimation BS_TARGET, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1 @@ -7755,7 +7756,7 @@ BattleScript_TryAdrenalineOrb: printstring STRINGID_USINGITEMSTATOFPKMNROSE waitmessage B_WAIT_TIME_LONG removeitem BS_TARGET -BattleScript_TryAdrenalineOrbRet: +BattleScript_TryIntimidateHoldEffectsRet: return BattleScript_IntimidateActivates:: @@ -7781,7 +7782,7 @@ BattleScript_IntimidateEffect: BattleScript_IntimidateEffect_WaitString: waitmessage B_WAIT_TIME_LONG copybyte sBATTLER, gBattlerTarget - call BattleScript_TryAdrenalineOrb + call BattleScript_TryIntimidateHoldEffects BattleScript_IntimidateLoopIncrement: addbyte gBattlerTarget, 1 jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_IntimidateLoop @@ -7807,7 +7808,7 @@ BattleScript_IntimidateInReverse: call BattleScript_AbilityPopUpTarget pause B_WAIT_TIME_SHORT modifybattlerstatstage BS_TARGET, STAT_ATK, INCREASE, 1, BattleScript_IntimidateLoopIncrement, ANIM_ON - call BattleScript_TryAdrenalineOrb + call BattleScript_TryIntimidateHoldEffects goto BattleScript_IntimidateLoopIncrement BattleScript_SupersweetSyrupActivates:: @@ -7834,7 +7835,7 @@ BattleScript_SupersweetSyrupEffect: BattleScript_SupersweetSyrupEffect_WaitString: waitmessage B_WAIT_TIME_LONG copybyte sBATTLER, gBattlerTarget - call BattleScript_TryAdrenalineOrb + call BattleScript_TryIntimidateHoldEffects BattleScript_SupersweetSyrupLoopIncrement: addbyte gBattlerTarget, 1 jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_SupersweetSyrupLoop diff --git a/include/battle_util.h b/include/battle_util.h index 8606e7ae0b..13e03c58dd 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -54,6 +54,7 @@ #define ITEMEFFECT_ORBS 6 #define ITEMEFFECT_LIFEORB_SHELLBELL 7 #define ITEMEFFECT_USE_LAST_ITEM 8 // move end effects for just the battler, not whole field +#define ITEMEFFECT_STATS_CHANGED 9 // For White Herb and Eject Pack #define WEATHER_HAS_EFFECT ((!IsAbilityOnField(ABILITY_CLOUD_NINE) && !IsAbilityOnField(ABILITY_AIR_LOCK))) diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 6d635fe848..641e644ebc 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -304,9 +304,9 @@ #define MOVEEND_DEFROST 23 #define MOVEEND_RECOIL 24 #define MOVEEND_MAGICIAN 25 // Occurs after final multi-hit strike, and after other items/abilities would activate -#define MOVEEND_EJECT_BUTTON 26 -#define MOVEEND_RED_CARD 27 -#define MOVEEND_EJECT_PACK 28 +#define MOVEEND_EJECT_ITEMS 26 +#define MOVEEND_WHITE_HERB 27 +#define MOVEEND_RED_CARD 28 #define MOVEEND_LIFEORB_SHELLBELL 29 // Includes shell bell, throat spray, etc #define MOVEEND_CHANGED_ITEMS 30 #define MOVEEND_PICKPOCKET 31 diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index c4d67e7024..f3d739e782 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5870,31 +5870,7 @@ static void Cmd_moveend(void) } gBattleScripting.moveendState++; break; - case MOVEEND_MAGICIAN: - if (GetBattlerAbility(gBattlerAttacker) == ABILITY_MAGICIAN - && gCurrentMove != MOVE_FLING && gCurrentMove != MOVE_NATURAL_GIFT - && gBattleMons[gBattlerAttacker].item == ITEM_NONE - && gBattleMons[gBattlerTarget].item != ITEM_NONE - && IsBattlerAlive(gBattlerAttacker) - && TARGET_TURN_DAMAGED - && CanStealItem(gBattlerAttacker, gBattlerTarget, gBattleMons[gBattlerTarget].item) - && !gSpecialStatuses[gBattlerAttacker].gemBoost // In base game, gems are consumed after magician would activate. - && !(gWishFutureKnock.knockedOffMons[GetBattlerSide(gBattlerTarget)] & gBitTable[gBattlerPartyIndexes[gBattlerTarget]]) - && !DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove) - && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) - && (GetBattlerAbility(gBattlerTarget) != ABILITY_STICKY_HOLD || !IsBattlerAlive(gBattlerTarget))) - { - StealTargetItem(gBattlerAttacker, gBattlerTarget); - gBattleScripting.battler = gBattlerAbility = gBattlerAttacker; - gEffectBattler = gBattlerTarget; - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_MagicianActivates; - gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage = TRUE; - effect = TRUE; - } - gBattleScripting.moveendState++; - break; - case MOVEEND_NEXT_TARGET: // For moves hitting two opposing Pokémon. + case MOVEEND_NEXT_TARGET: // For moves hitting two opposing Pokemon. { u16 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); // Set a flag if move hits either target (for throat spray that can't check damage) @@ -6014,98 +5990,165 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; } - case MOVEEND_EJECT_BUTTON: - if (gMovesInfo[gCurrentMove].effect != EFFECT_HIT_SWITCH_TARGET + // The order of abilities/items activating after moves hitting multiple targets is + // 1. Magician + // 2. The fastest mon gets switched out using Eject Button / Eject Pack + // 3. White Herb activates + // 4. Red Card activates + // 5. Life Orb / Shell Bell + // 6. Pickpocket + case MOVEEND_MAGICIAN: + if (GetBattlerAbility(gBattlerAttacker) == ABILITY_MAGICIAN + && gCurrentMove != MOVE_FLING && gCurrentMove != MOVE_NATURAL_GIFT + && gBattleMons[gBattlerAttacker].item == ITEM_NONE + && gBattleMons[gBattlerTarget].item != ITEM_NONE && IsBattlerAlive(gBattlerAttacker) - && !TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove) - && (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER || (gBattleTypeFlags & BATTLE_TYPE_TRAINER))) + && TARGET_TURN_DAMAGED + && CanStealItem(gBattlerAttacker, gBattlerTarget, gBattleMons[gBattlerTarget].item) + && !gSpecialStatuses[gBattlerAttacker].gemBoost // In base game, gems are consumed after magician would activate. + && !(gWishFutureKnock.knockedOffMons[GetBattlerSide(gBattlerTarget)] & gBitTable[gBattlerPartyIndexes[gBattlerTarget]]) + && !DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove) + && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && (GetBattlerAbility(gBattlerTarget) != ABILITY_STICKY_HOLD || !IsBattlerAlive(gBattlerTarget))) { - // Since we check if battler was damaged, we don't need to check move result. - // In fact, doing so actually prevents multi-target moves from activating eject button properly - u8 battlers[4] = {0, 1, 2, 3}; - SortBattlersBySpeed(battlers, FALSE); + StealTargetItem(gBattlerAttacker, gBattlerTarget); + gBattleScripting.battler = gBattlerAbility = gBattlerAttacker; + gEffectBattler = gBattlerTarget; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_MagicianActivates; + gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage = TRUE; + effect = TRUE; + } + gBattleScripting.moveendState++; + break; + case MOVEEND_EJECT_ITEMS: + { + // Because sorting the battlers by speed takes lots of cycles, it's better to just check if any of the battlers has the Eject items. + u32 ejectPackBattlers = 0, ejectButtonBattlers = 0, i; for (i = 0; i < gBattlersCount; i++) { - u8 battler = battlers[i]; - // Attacker is the damage-dealer, battler is mon to be switched out - if (IsBattlerAlive(battler) - && gBattlerAttacker != battler - && GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_EJECT_BUTTON - && BATTLER_TURN_DAMAGED(battler) - && CountUsablePartyMons(battler) > 0) // Has mon to switch into + u32 holdEffect; + if (i == gBattlerAttacker) + continue; + holdEffect = GetBattlerHoldEffect(i, TRUE); + if (holdEffect == HOLD_EFFECT_EJECT_BUTTON) + ejectButtonBattlers |= gBitTable[i]; + else if (holdEffect == HOLD_EFFECT_EJECT_PACK) + ejectPackBattlers |= gBitTable[i]; + } + if (ejectButtonBattlers || ejectPackBattlers) + { + u8 battlers[4] = {0, 1, 2, 3}; + SortBattlersBySpeed(battlers, FALSE); + + for (i = 0; i < gBattlersCount; i++) { - gBattleScripting.battler = battler; - gLastUsedItem = gBattleMons[battler].item; - if (gMovesInfo[gCurrentMove].effect == EFFECT_HIT_ESCAPE) - gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_EjectButtonActivates; - effect = TRUE; - break; // Only the fastest Eject Button activates + u32 battler = battlers[i]; + + if (ejectButtonBattlers & gBitTable[battler]) + { + if (TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove)) // Apparently Sheer Force blocks Eject Button, but not Eject Pack + continue; + // Since we check if battler was damaged, we don't need to check move result. + // In fact, doing so actually prevents multi-target moves from activating eject button properly + if (!BATTLER_TURN_DAMAGED(battler)) + continue; + } + else if (ejectPackBattlers & gBitTable[battler]) + { + if (!gProtectStructs[battler].statFell || gProtectStructs[battler].disableEjectPack) + continue; + } + else + { + continue; + } + + if (IsBattlerAlive(battler) + && CountUsablePartyMons(battler) > 0 // Has mon to switch into + // Does not activate if attacker used Parting Shot and can switch out + && !(gMovesInfo[gCurrentMove].effect == EFFECT_HIT_SWITCH_TARGET && CanBattlerSwitch(gBattlerAttacker)) + ) + { + gBattleScripting.battler = battler; + gLastUsedItem = gBattleMons[battler].item; + if (gMovesInfo[gCurrentMove].effect == EFFECT_HIT_ESCAPE) + gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection + effect = TRUE; + BattleScriptPushCursor(); + if (ejectButtonBattlers & gBitTable[battler]) + { + gBattlescriptCurrInstr = BattleScript_EjectButtonActivates; + } + else // Eject Pack + { + gBattlescriptCurrInstr = BattleScript_EjectPackActivates; + // Are these 2 lines below needed? + gProtectStructs[battler].statFell = FALSE; + gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage = TRUE; + } + break; // Only the fastest Eject item activates + } } } } gBattleScripting.moveendState++; break; + case MOVEEND_WHITE_HERB: + for (i = 0; i < gBattlersCount; i++) + { + if (IsBattlerAlive(i) + && ItemBattleEffects(ITEMEFFECT_STATS_CHANGED, i, FALSE)) + { + effect = TRUE; + break; + } + } + if (!effect) + gBattleScripting.moveendState++; + break; case MOVEEND_RED_CARD: - if ((gMovesInfo[gCurrentMove].effect != EFFECT_HIT_SWITCH_TARGET || gBattleStruct->hitSwitchTargetFailed) - && IsBattlerAlive(gBattlerAttacker) - && !TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove) - && GetBattlerAbility(gBattlerAttacker) != ABILITY_GUARD_DOG) { - // Since we check if battler was damaged, we don't need to check move result. - // In fact, doing so actually prevents multi-target moves from activating red card properly - u8 battlers[4] = {0, 1, 2, 3}; - SortBattlersBySpeed(battlers, FALSE); + u32 redCardBattlers = 0, i; for (i = 0; i < gBattlersCount; i++) { - u8 battler = battlers[i]; - // Search for fastest hit pokemon with a red card - // Attacker is the one to be switched out, battler is one with red card - if (battler != gBattlerAttacker - && IsBattlerAlive(battler) - && !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) - && GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_RED_CARD - && BATTLER_TURN_DAMAGED(battler) - && CanBattlerSwitch(gBattlerAttacker)) - { - gLastUsedItem = gBattleMons[battler].item; - gBattleStruct->savedBattlerTarget = gBattleScripting.battler = battler; // Battler with red card - gEffectBattler = gBattlerAttacker; - if (gMovesInfo[gCurrentMove].effect == EFFECT_HIT_ESCAPE) - gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_RedCardActivates; - gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage = TRUE; - effect = TRUE; - break; // Only fastest red card activates - } + if (i == gBattlerAttacker) + continue; + if (GetBattlerHoldEffect(i, TRUE) == HOLD_EFFECT_RED_CARD) + redCardBattlers |= gBitTable[i]; } - } - gBattleScripting.moveendState++; - break; - case MOVEEND_EJECT_PACK: - { - u8 battlers[4] = {0, 1, 2, 3}; - SortBattlersBySpeed(battlers, FALSE); - for (i = 0; i < gBattlersCount; i++) + if (redCardBattlers + && (gMovesInfo[gCurrentMove].effect != EFFECT_HIT_SWITCH_TARGET || gBattleStruct->hitSwitchTargetFailed) + && IsBattlerAlive(gBattlerAttacker) + && !TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove) + && GetBattlerAbility(gBattlerAttacker) != ABILITY_GUARD_DOG) { - u8 battler = battlers[i]; - if (IsBattlerAlive(battler) - && gProtectStructs[battler].statFell - && gProtectStructs[battler].disableEjectPack == 0 - && GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_EJECT_PACK - && !(gCurrentMove == MOVE_PARTING_SHOT && CanBattlerSwitch(gBattlerAttacker)) // Does not activate if attacker used Parting Shot and can switch out - && CountUsablePartyMons(battler) > 0) // Has mon to switch into + // Since we check if battler was damaged, we don't need to check move result. + // In fact, doing so actually prevents multi-target moves from activating red card properly + u8 battlers[4] = {0, 1, 2, 3}; + SortBattlersBySpeed(battlers, FALSE); + for (i = 0; i < gBattlersCount; i++) { - gProtectStructs[battler].statFell = FALSE; - gBattleScripting.battler = battler; - gLastUsedItem = gBattleMons[battler].item; - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_EjectPackActivates; - gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage = TRUE; - effect = TRUE; - break; // Only fastest eject pack activates + u32 battler = battlers[i]; + // Search for fastest hit pokemon with a red card + // Attacker is the one to be switched out, battler is one with red card + if (redCardBattlers & gBitTable[battler] + && IsBattlerAlive(battler) + && !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) + && BATTLER_TURN_DAMAGED(battler) + && CanBattlerSwitch(gBattlerAttacker)) + { + gLastUsedItem = gBattleMons[battler].item; + gBattleStruct->savedBattlerTarget = gBattleScripting.battler = battler; // Battler with red card + gEffectBattler = gBattlerAttacker; + if (gMovesInfo[gCurrentMove].effect == EFFECT_HIT_ESCAPE) + gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_RedCardActivates; + gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage = TRUE; + effect = TRUE; + break; // Only fastest red card activates + } } } } @@ -16708,6 +16751,15 @@ void BS_SetPhotonGeyserCategory(void) gBattlescriptCurrInstr = cmd->nextInstr; } +void BS_RunStatChangeItems(void) +{ + NATIVE_ARGS(u8 battler); + + // Change instruction before calling ItemBattleEffects. + gBattlescriptCurrInstr = cmd->nextInstr; + ItemBattleEffects(ITEMEFFECT_STATS_CHANGED, GetBattlerForBattleScript(cmd->battler), FALSE); +} + static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount) { u32 i; diff --git a/src/battle_util.c b/src/battle_util.c index 08562e4295..ec849753ff 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6762,10 +6762,29 @@ static u8 TryConsumeMirrorHerb(u32 battler, bool32 execute) return effect; } +static u32 RestoreWhiteHerbStats(u32 battler) +{ + u32 i, effect = 0; + + for (i = 0; i < NUM_BATTLE_STATS; i++) + { + if (gBattleMons[battler].statStages[i] < DEFAULT_STAT_STAGE) + { + gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE; + effect = ITEM_STATS_CHANGE; + } + } + if (effect != 0) + { + gBattleScripting.battler = battler; + gPotentialItemEffectBattler = battler; + } + return effect; +} + static u8 ItemEffectMoveEnd(u32 battler, u16 holdEffect) { u8 effect = 0; - u32 i; switch (holdEffect) { @@ -6945,24 +6964,6 @@ static u8 ItemEffectMoveEnd(u32 battler, u16 holdEffect) effect = ITEM_STATUS_CHANGE; } break; - case HOLD_EFFECT_RESTORE_STATS: - for (i = 0; i < NUM_BATTLE_STATS; i++) - { - if (gBattleMons[battler].statStages[i] < DEFAULT_STAT_STAGE) - { - gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE; - effect = ITEM_STATS_CHANGE; - } - } - if (effect != 0) - { - gBattleScripting.battler = battler; - gPotentialItemEffectBattler = battler; - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_WhiteHerbRet; - return effect; - } - break; case HOLD_EFFECT_CRITICAL_UP: // lansat berry if (B_BERRIES_INSTANT >= GEN_4 && !(gBattleMons[battler].status2 & STATUS2_FOCUS_ENERGY_ANY) @@ -7032,18 +7033,9 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) } break; case HOLD_EFFECT_RESTORE_STATS: - for (i = 0; i < NUM_BATTLE_STATS; i++) - { - if (gBattleMons[battler].statStages[i] < DEFAULT_STAT_STAGE) - { - gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE; - effect = ITEM_STATS_CHANGE; - } - } + effect = RestoreWhiteHerbStats(battler); if (effect != 0) { - gBattleScripting.battler = battler; - gPotentialItemEffectBattler = battler; gBattlerAttacker = battler; BattleScriptExecute(BattleScript_WhiteHerbEnd2); } @@ -7314,18 +7306,9 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) effect = ItemRestorePp(battler, gLastUsedItem, TRUE); break; case HOLD_EFFECT_RESTORE_STATS: - for (i = 0; i < NUM_BATTLE_STATS; i++) - { - if (gBattleMons[battler].statStages[i] < DEFAULT_STAT_STAGE) - { - gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE; - effect = ITEM_STATS_CHANGE; - } - } + effect = RestoreWhiteHerbStats(battler); if (effect != 0) { - gBattleScripting.battler = battler; - gPotentialItemEffectBattler = battler; gBattlerAttacker = battler; BattleScriptExecute(BattleScript_WhiteHerbEnd2); } @@ -7885,6 +7868,19 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) } } break; + case ITEMEFFECT_STATS_CHANGED: + switch (battlerHoldEffect) + { + case HOLD_EFFECT_RESTORE_STATS: + effect = RestoreWhiteHerbStats(battler); + if (effect != 0) + { + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_WhiteHerbRet; + } + break; + } + break; } // Berry was successfully used on a Pokemon. diff --git a/test/battle/hold_effect/white_herb.c b/test/battle/hold_effect/white_herb.c index 0103115658..fb455f47c6 100644 --- a/test/battle/hold_effect/white_herb.c +++ b/test/battle/hold_effect/white_herb.c @@ -55,10 +55,10 @@ DOUBLE_BATTLE_TEST("White Herb restores stats after Attack was lowered by Intimi ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentLeft); MESSAGE("Foe Wobbuffet's White Herb restored its status!"); + + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentRight); MESSAGE("Foe Wynaut's White Herb restored its status!"); } THEN { @@ -71,7 +71,6 @@ DOUBLE_BATTLE_TEST("White Herb restores stats after Attack was lowered by Intimi SINGLE_BATTLE_TEST("White Herb restores stats after Attack was lowered by Intimidate while switching in") { - KNOWN_FAILING; GIVEN { PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_WHITE_HERB); } OPPONENT(SPECIES_WOBBUFFET); @@ -95,7 +94,6 @@ SINGLE_BATTLE_TEST("White Herb restores stats after Attack was lowered by Intimi } } - SINGLE_BATTLE_TEST("White Herb restores stats after all hits of a multi hit move happened") { u16 species; @@ -104,7 +102,6 @@ SINGLE_BATTLE_TEST("White Herb restores stats after all hits of a multi hit move PARAMETRIZE { species = SPECIES_SLIGGOO_HISUIAN; ability = ABILITY_GOOEY; } PARAMETRIZE { species = SPECIES_DUGTRIO_ALOLAN; ability = ABILITY_TANGLING_HAIR; } - KNOWN_FAILING; GIVEN { ASSUME(gMovesInfo[MOVE_DUAL_WINGBEAT].strikeCount == 2); PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_WHITE_HERB); } @@ -114,10 +111,11 @@ SINGLE_BATTLE_TEST("White Herb restores stats after all hits of a multi hit move } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_DUAL_WINGBEAT, player); ABILITY_POPUP(opponent, ability); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); MESSAGE("Wobbuffet's Speed fell!"); ABILITY_POPUP(opponent, ability); - MESSAGE("Wobbuffet's Speed fell!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Speed fell!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); MESSAGE("Wobbuffet's White Herb restored its status!"); } THEN { @@ -133,7 +131,6 @@ SINGLE_BATTLE_TEST("White Herb wont have time to activate if it is knocked off o PARAMETRIZE { move = MOVE_THIEF; } PARAMETRIZE { move = MOVE_KNOCK_OFF; } - KNOWN_FAILING; // Knock off fails, Thief is fine GIVEN { ASSUME(MoveHasAdditionalEffect(MOVE_THIEF, MOVE_EFFECT_STEAL_ITEM) == TRUE); ASSUME(gMovesInfo[MOVE_KNOCK_OFF].effect == EFFECT_KNOCK_OFF); @@ -143,14 +140,16 @@ SINGLE_BATTLE_TEST("White Herb wont have time to activate if it is knocked off o TURN { MOVE(opponent, move); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, move, opponent); - if (move == MOVE_THIEF) + if (move == MOVE_THIEF) { MESSAGE("Foe Wobbuffet stole Slugma's White Herb!"); - else - MESSAGE("Foe Wobbuffet knocked off Slugma's White Herb!"); + } ABILITY_POPUP(player, ABILITY_WEAK_ARMOR); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); MESSAGE("Slugma's Weak Armor lowered its Defense!"); MESSAGE("Slugma's Weak Armor raised its Speed!"); + if (move == MOVE_KNOCK_OFF) { + MESSAGE("Foe Wobbuffet knocked off Slugma's White Herb!"); + } NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); MESSAGE("Wobbuffet's White Herb restored its status!"); @@ -163,7 +162,6 @@ SINGLE_BATTLE_TEST("White Herb wont have time to activate if it is knocked off o SINGLE_BATTLE_TEST("White Herb wont have time to activate if Magician steals it") { - KNOWN_FAILING; // White Herb is activated GIVEN { PLAYER(SPECIES_SLUGMA) { Ability(ABILITY_WEAK_ARMOR); Item(ITEM_WHITE_HERB); } OPPONENT(SPECIES_FENNEKIN) { Ability(ABILITY_MAGICIAN); } @@ -171,11 +169,12 @@ SINGLE_BATTLE_TEST("White Herb wont have time to activate if Magician steals it" TURN { MOVE(opponent, MOVE_TACKLE); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + ABILITY_POPUP(player, ABILITY_WEAK_ARMOR); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Slugma's Weak Armor lowered its Defense!"); + MESSAGE("Slugma's Weak Armor raised its Speed!"); ABILITY_POPUP(opponent, ABILITY_MAGICIAN); - ABILITY_POPUP(player, ABILITY_WEAK_ARMOR); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Slugma's Weak Armor lowered its Defense!"); - MESSAGE("Slugma's Weak Armor raised its Speed!"); + MESSAGE("Foe Fennekin stole Slugma's White Herb!"); NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); MESSAGE("Wobbuffet's White Herb restored its status!"); @@ -186,33 +185,7 @@ SINGLE_BATTLE_TEST("White Herb wont have time to activate if Magician steals it" } } -SINGLE_BATTLE_TEST("White Herb wont have time to activate if Pickpocket steals it") -{ - KNOWN_FAILING; // White Herb is activated - GIVEN { - ASSUME(MoveHasAdditionalEffectSelf(MOVE_LEAF_STORM, MOVE_EFFECT_SP_ATK_TWO_DOWN)); - PLAYER(SPECIES_SLUGMA) { Ability(ABILITY_WEAK_ARMOR); Item(ITEM_WHITE_HERB); } - OPPONENT(SPECIES_SNEASEL) { Ability(ABILITY_PICKPOCKET); } - } WHEN { - TURN { MOVE(player, MOVE_LEAF_STORM); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_LEAF_STORM, player); - ABILITY_POPUP(player, ABILITY_PICKPOCKET); - ABILITY_POPUP(player, ABILITY_WEAK_ARMOR); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Slugma's Weak Armor lowered its Defense!"); - MESSAGE("Slugma's Weak Armor raised its Speed!"); - NONE_OF { - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); - MESSAGE("Wobbuffet's White Herb restored its status!"); - } - } THEN { - EXPECT(player->statStages[STAT_DEF] = DEFAULT_STAT_STAGE - 1); - EXPECT(player->statStages[STAT_SPEED] = DEFAULT_STAT_STAGE + 1); - } -} - -SINGLE_BATTLE_TEST("White Herb restores stats after Defiant or Competitive were triggered") +SINGLE_BATTLE_TEST("White Herb has correct interactions with Intimidate triggered Defiant and Competitive") { u16 species; u16 ability; @@ -220,7 +193,6 @@ SINGLE_BATTLE_TEST("White Herb restores stats after Defiant or Competitive were PARAMETRIZE { species = SPECIES_IGGLYBUFF; ability = ABILITY_COMPETITIVE; } PARAMETRIZE { species = SPECIES_MANKEY; ability = ABILITY_DEFIANT; } - KNOWN_FAILING; GIVEN { PLAYER(species) { Ability(ability); Item(ITEM_WHITE_HERB); } OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); } @@ -230,16 +202,18 @@ SINGLE_BATTLE_TEST("White Herb restores stats after Defiant or Competitive were ABILITY_POPUP(opponent, ABILITY_INTIMIDATE); ABILITY_POPUP(player, ability); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); - MESSAGE("Wobbuffet's White Herb restored its status!"); + // Defiant activates first, so White Herb doesn't have a chance to trigger. + if (ability == ABILITY_COMPETITIVE) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Igglybuff's White Herb restored its status!"); + } } THEN { - EXPECT(player->item == ITEM_NONE); - if (species == SPECIES_IGGLYBUFF) - { + if (ability == ABILITY_COMPETITIVE) { + EXPECT(player->item == ITEM_NONE); EXPECT(player->statStages[STAT_ATK] = DEFAULT_STAT_STAGE); EXPECT(player->statStages[STAT_SPATK] = DEFAULT_STAT_STAGE + 2); + } else { + EXPECT(player->statStages[STAT_ATK] = DEFAULT_STAT_STAGE + 1); } - else - EXPECT(player->statStages[STAT_ATK] = DEFAULT_STAT_STAGE + 3); } }