Fixes Eject Pack not resolving correctly during switch in effects (#7002)

This commit is contained in:
Alex 2025-06-25 21:15:40 +02:00 committed by GitHub
parent c6221fa50e
commit 2531613a52
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 162 additions and 57 deletions

View File

@ -1401,10 +1401,6 @@
.byte \battler
.endm
.macro tryintimidateejectpack
callnative BS_TryIntimidateEjectPack
.endm
.macro allyswitchswapbattlers
callnative BS_AllySwitchSwapBattler
.endm

View File

@ -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

View File

@ -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;

View File

@ -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,
};

View File

@ -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[];

View File

@ -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);

View File

@ -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++)
{

View File

@ -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);

View File

@ -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

View File

@ -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);
}
}
}