diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index a4e0a0cbf2..11cca995c0 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1401,10 +1401,6 @@ .byte \battler .endm - .macro tryintimidateejectpack - callnative BS_TryIntimidateEjectPack - .endm - .macro allyswitchswapbattlers callnative BS_AllySwitchSwapBattler .endm diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 13503b0f91..0a908236f6 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7462,7 +7462,6 @@ BattleScript_IntimidateLoopIncrement: destroyabilitypopup restoretarget pause B_WAIT_TIME_MED - tryintimidateejectpack end3 BattleScript_IntimidatePrevented:: @@ -7520,7 +7519,6 @@ BattleScript_SupersweetSyrupLoopIncrement: destroyabilitypopup restoretarget pause B_WAIT_TIME_MED - tryintimidateejectpack end3 BattleScript_SupersweetSyrupWontDecrease: @@ -9215,6 +9213,10 @@ BattleScript_EjectPackActivate_End2:: call BattleScript_EjectPackActivate_Ret end2 +BattleScript_EjectPackActivate_End3:: + call BattleScript_EjectPackActivate_Ret + end3 + BattleScript_EjectPackActivates:: jumpifcantswitch BS_SCRIPTING, BattleScript_EjectButtonEnd goto BattleScript_EjectPackActivate_Ret diff --git a/include/battle.h b/include/battle.h index 52af53effc..e113edd6d4 100644 --- a/include/battle.h +++ b/include/battle.h @@ -160,7 +160,7 @@ struct ProtectStruct u32 unused:8; // End of 32-bit bitfield u16 disableEjectPack:1; - u16 statFell:1; + u16 tryEjectPack:1; u16 pranksterElevated:1; u16 quickDraw:1; u16 beakBlastCharge:1; diff --git a/include/battle_main.h b/include/battle_main.h index 42c2dc2b32..23e030f6d3 100644 --- a/include/battle_main.h +++ b/include/battle_main.h @@ -60,6 +60,7 @@ enum FirstTurnEventsStates FIRST_TURN_EVENTS_OPPORTUNIST_1, FIRST_TURN_EVENTS_ITEM_EFFECTS, FIRST_TURN_EVENTS_OPPORTUNIST_2, + FIRST_TURN_EVENTS_EJECT_PACK, FIRST_TURN_EVENTS_END, }; diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 50cfbc1420..80d6693bd5 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -420,6 +420,7 @@ extern const u8 BattleScript_RedCardActivates[]; extern const u8 BattleScript_EjectButtonActivates[]; extern const u8 BattleScript_EjectPackActivate_Ret[]; extern const u8 BattleScript_EjectPackActivate_End2[]; +extern const u8 BattleScript_EjectPackActivate_End3[]; extern const u8 BattleScript_EjectPackActivates[]; extern const u8 BattleScript_MentalHerbCureRet[]; extern const u8 BattleScript_MentalHerbCureEnd2[]; diff --git a/include/battle_util.h b/include/battle_util.h index d1ab994132..f198857994 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -394,6 +394,7 @@ bool32 IsFutureSightAttackerInParty(u32 battlerAtk, u32 battlerDef, u32 move); bool32 HadMoreThanHalfHpNowDoesnt(u32 battler); void UpdateStallMons(void); bool32 TryRestoreHPBerries(u32 battler, enum ItemCaseId caseId); +bool32 TrySwitchInEjectPack(enum ItemCaseId caseID); u32 GetMonVolatile(u32 battler, enum Volatile volatile); void SetMonVolatile(u32 battler, enum Volatile volatile, u32 newValue); u32 TryBoosterEnergy(u32 battler, u32 ability, enum ItemCaseId caseID); diff --git a/src/battle_main.c b/src/battle_main.c index 7b86856a3e..4427093e30 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3325,7 +3325,7 @@ const u8* FaintClearSetData(u32 battler) gProtectStructs[battler].fleeType = 0; gProtectStructs[battler].notFirstStrike = FALSE; gProtectStructs[battler].statRaised = FALSE; - gProtectStructs[battler].statFell = FALSE; + gProtectStructs[battler].tryEjectPack = FALSE; gProtectStructs[battler].pranksterElevated = FALSE; gDisableStructs[battler].isFirstTurn = 2; @@ -3880,6 +3880,11 @@ static void TryDoEventsBeforeFirstTurn(void) return; gBattleStruct->eventsBeforeFirstTurnState++; break; + case FIRST_TURN_EVENTS_EJECT_PACK: + gBattleStruct->eventsBeforeFirstTurnState++; + if (TrySwitchInEjectPack(ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN)) + return; + break; case FIRST_TURN_EVENTS_END: for (i = 0; i < MAX_BATTLERS_COUNT; i++) { diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 3008680337..240ed0367b 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5999,7 +5999,7 @@ static inline bool32 CanEjectButtonTrigger(u32 battlerAtk, u32 battlerDef, enum static inline bool32 CanEjectPackTrigger(u32 battlerAtk, u32 battlerDef, enum BattleMoveEffects moveEffect) { - if (gProtectStructs[battlerDef].statFell + if (gProtectStructs[battlerDef].tryEjectPack && GetBattlerHoldEffect(battlerDef, TRUE) == HOLD_EFFECT_EJECT_PACK && IsBattlerAlive(battlerDef) && CountUsablePartyMons(battlerDef) > 0 @@ -6901,6 +6901,9 @@ static void Cmd_moveend(void) break; } + for (u32 i = 0; i < gBattlersCount; i++) + gProtectStructs[i].tryEjectPack = FALSE; + u8 battlers[4] = {0, 1, 2, 3}; if (numEjectButtonBattlers > 1) SortBattlersBySpeed(battlers, FALSE); @@ -6952,7 +6955,7 @@ static void Cmd_moveend(void) SortBattlersBySpeed(battlers, FALSE); for (i = 0; i < gBattlersCount; i++) - gProtectStructs[i].statFell = FALSE; // restore for every possible eject pack battler + gProtectStructs[i].tryEjectPack = FALSE; for (i = 0; i < gBattlersCount; i++) { @@ -7024,6 +7027,8 @@ static void Cmd_moveend(void) && CanBattlerSwitch(gBattlerAttacker) && !(moveEffect == EFFECT_HIT_SWITCH_TARGET && CanBattlerSwitch(battler))) { + for (u32 i = 0; i < gBattlersCount; i++) + gProtectStructs[i].tryEjectPack = FALSE; effect = TRUE; gLastUsedItem = gBattleMons[battler].item; SaveBattlerTarget(battler); // save battler with red card @@ -7116,6 +7121,9 @@ static void Cmd_moveend(void) break; } + for (u32 i = 0; i < gBattlersCount; i++) + gProtectStructs[i].tryEjectPack = FALSE; + u8 battlers[4] = {0, 1, 2, 3}; if (numEmergencyExitBattlers > 1) SortBattlersBySpeed(battlers, FALSE); @@ -7261,6 +7269,7 @@ static void Cmd_moveend(void) for (i = 0; i < gBattlersCount; i++) { gBattleStruct->battlerState[gBattlerAttacker].targetsDone[i] = FALSE; + gProtectStructs[i].tryEjectPack = FALSE; if (gBattleStruct->commanderActive[i] != SPECIES_NONE && !IsBattlerAlive(i)) { @@ -8236,6 +8245,8 @@ static void Cmd_switchineffects(void) return; } } + if (TrySwitchInEjectPack(ITEMEFFECT_NONE)) + return; // All battlers done, end for (i = 0; i < gBattlersCount; i++) gBattleStruct->battlerState[i].multipleSwitchInBattlers = FALSE; @@ -8246,7 +8257,7 @@ static void Cmd_switchineffects(void) break; default: UpdateSentMonFlags(battler); - if (!DoSwitchInEffectsForBattler(battler)) + if (!DoSwitchInEffectsForBattler(battler) && !TrySwitchInEjectPack(ITEMEFFECT_NONE)) gBattlescriptCurrInstr = cmd->nextInstr; break; } @@ -12272,7 +12283,7 @@ static u32 ChangeStatBuffs(u32 battler, s8 statValue, u32 statId, union StatChan } else if (!flags.onlyChecking) { - gProtectStructs[battler].statFell = TRUE; + gProtectStructs[battler].tryEjectPack = TRUE; gProtectStructs[battler].lashOutAffected = TRUE; } } @@ -18526,49 +18537,6 @@ void BS_JumpIfIntimidateAbilityPrevented(void) } } -void BS_TryIntimidateEjectPack(void) -{ - NATIVE_ARGS(); - - u32 affectedBattler = 0xFF; - u32 battler = BATTLE_OPPOSITE(gBattlerAttacker); - u32 partnerBattler = BATTLE_PARTNER(battler); - - bool32 ejectPackBattler = CanEjectPackTrigger(gBattlerAttacker, battler, MOVE_NONE); - bool32 ejectPackPartnerBattler = CanEjectPackTrigger(gBattlerAttacker, partnerBattler, MOVE_NONE); - - if (ejectPackBattler && ejectPackPartnerBattler) - { - u32 battlerSpeed = GetBattlerTotalSpeedStat(battler); - u32 partnerbattlerSpeed = GetBattlerTotalSpeedStat(partnerBattler); - - if (battlerSpeed >= partnerbattlerSpeed) - affectedBattler = battler; - else - affectedBattler = partnerBattler; - } - else if (ejectPackBattler) - { - affectedBattler = battler; - } - else if (ejectPackPartnerBattler) - { - affectedBattler = partnerBattler; - } - - gBattlescriptCurrInstr = cmd->nextInstr; - if (affectedBattler != 0xFF) - { - gProtectStructs[battler].statFell = FALSE; - gProtectStructs[partnerBattler].statFell = FALSE; - gAiLogicData->ejectPackSwitch = TRUE; - gBattleScripting.battler = affectedBattler; - gLastUsedItem = gBattleMons[affectedBattler].item; - RecordItemEffectBattle(affectedBattler, HOLD_EFFECT_EJECT_PACK); - BattleScriptCall(BattleScript_EjectPackActivate_Ret); - } -} - void BS_JumpIfCanGigantamax(void) { NATIVE_ARGS(u8 battler, const u8 *jumpInstr); diff --git a/src/battle_util.c b/src/battle_util.c index 8ea025f534..1580e2addc 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6073,12 +6073,12 @@ enum ItemEffect TryHandleSeed(u32 battler, u32 terrainFlag, u32 statId, u32 item static enum ItemEffect TryEjectPack(u32 battler, enum ItemCaseId caseID) { - if (gProtectStructs[battler].statFell + if (gProtectStructs[battler].tryEjectPack && !gProtectStructs[battler].disableEjectPack && CountUsablePartyMons(battler) > 0 && !(GetMoveEffect(gCurrentMove) == EFFECT_PARTING_SHOT && CanBattlerSwitch(gBattlerAttacker))) // Does not activate if attacker used Parting Shot and can switch out { - gProtectStructs[battler].statFell = FALSE; + gProtectStructs[battler].tryEjectPack = FALSE; gBattleScripting.battler = battler; gAiLogicData->ejectPackSwitch = TRUE; if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN) @@ -11380,6 +11380,59 @@ bool32 TryRestoreHPBerries(u32 battler, enum ItemCaseId caseId) return FALSE; } +bool32 TrySwitchInEjectPack(enum ItemCaseId caseID) +{ + // 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; + u32 numEjectPackBattlers = 0; + + for (u32 i = 0; i < gBattlersCount; i++) + { + if (gProtectStructs[i].tryEjectPack + && GetBattlerHoldEffect(i, TRUE) == HOLD_EFFECT_EJECT_PACK + && IsBattlerAlive(i) + && CountUsablePartyMons(i) > 0) + { + ejectPackBattlers |= 1u << i; + numEjectPackBattlers++; + } + } + + if (numEjectPackBattlers == 0) + return FALSE; + + u8 battlers[4] = {0, 1, 2, 3}; + if (numEjectPackBattlers > 1) + SortBattlersBySpeed(battlers, FALSE); + + for (u32 i = 0; i < gBattlersCount; i++) + gProtectStructs[i].tryEjectPack = FALSE; + + for (u32 i = 0; i < gBattlersCount; i++) + { + u32 battler = battlers[i]; + + if (!(ejectPackBattlers & 1u << battler)) + continue; + + gBattleScripting.battler = battler; + gLastUsedItem = gBattleMons[battler].item; + if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN) + { + BattleScriptPushCursorAndCallback(BattleScript_EjectPackActivate_End3); + } + else + { + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_EjectPackActivate_Ret; + } + gAiLogicData->ejectPackSwitch = TRUE; + return TRUE; + } + + return FALSE; +} + #define UNPACK_VOLATILE_GETTERS(_enum, _fieldName, _typeBitSize, ...) case _enum: return gBattleMons[battler].volatiles._fieldName; // Gets the value of a volatile status flag for a certain battler diff --git a/test/battle/hold_effect/eject_pack.c b/test/battle/hold_effect/eject_pack.c index f33a59de21..39724bdffb 100644 --- a/test/battle/hold_effect/eject_pack.c +++ b/test/battle/hold_effect/eject_pack.c @@ -239,3 +239,81 @@ DOUBLE_BATTLE_TEST("Eject Pack: Only the fastest Eject Pack will activate after } } } + +DOUBLE_BATTLE_TEST("Eject Pack: Only the fastest Eject Pack will activate after intimidate (switch in after fainting)") +{ + u32 speed; + + PARAMETRIZE { speed = 1; } + PARAMETRIZE { speed = 11; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(10); Item(ITEM_EJECT_PACK); } + PLAYER(SPECIES_WYNAUT) { Speed(speed); Item(ITEM_EJECT_PACK); } + PLAYER(SPECIES_WOBBUFFET) { Speed(3); } + OPPONENT(SPECIES_WYNAUT) { HP(1); Speed(4); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(5); } + OPPONENT(SPECIES_EKANS) { Speed(6); Ability(ABILITY_INTIMIDATE); } + } WHEN { + TURN { + MOVE(playerLeft, MOVE_POUND, target: opponentLeft); + SEND_OUT(opponentLeft, 2); + if (speed == 11) + SEND_OUT(playerRight, 2); + else + SEND_OUT(playerLeft, 2); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, playerLeft); + ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + if (speed == 11) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft); + } else { + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft); + } + } +} + +DOUBLE_BATTLE_TEST("Eject Pack: Only the fastest Eject Pack will activate after intimidate (switch in after 2 mons fainted)") +{ + u32 speed; + + PARAMETRIZE { speed = 1; } + PARAMETRIZE { speed = 11; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(10); Item(ITEM_EJECT_PACK); } + PLAYER(SPECIES_WYNAUT) { Speed(speed); Item(ITEM_EJECT_PACK); } + PLAYER(SPECIES_WOBBUFFET) { Speed(1); } + OPPONENT(SPECIES_WYNAUT) { HP(1); Speed(4); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(5); } + OPPONENT(SPECIES_WYNAUT) { Speed(4); } + OPPONENT(SPECIES_EKANS) { Speed(6); Ability(ABILITY_INTIMIDATE); } + } WHEN { + TURN { + MOVE(playerLeft, MOVE_HYPER_VOICE); + SEND_OUT(opponentLeft, 3); + SEND_OUT(opponentRight, 2); + if (speed == 11) + SEND_OUT(playerRight, 2); + else + SEND_OUT(playerLeft, 2); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPER_VOICE, playerLeft); + ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + if (speed == 11) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft); + } else { + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft); + } + } +}