Fixes Intimidate / Eject Pack interaction (#6645)

Co-authored-by: Bassoonian <iasperbassoonian@gmail.com>
This commit is contained in:
Alex 2025-04-21 17:33:29 +02:00 committed by GitHub
parent 4e7fa359ef
commit d220459a01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 227 additions and 20 deletions

View File

@ -1367,6 +1367,10 @@
callnative BS_RestoreAttacker
.endm
.macro jumpifintimidateabilityprevented
callnative BS_JumpIfIntimidateAbilityPrevented
.endm
.macro metalburstdamagecalculator failInstr:req
callnative BS_CalcMetalBurstDmg
.4byte \failInstr
@ -1383,6 +1387,10 @@
.byte \battler
.endm
.macro tryintimidatejectpack
callnative BS_TryIntimidatEjectpack
.endm
.macro allyswitchswapbattlers
callnative BS_AllySwitchSwapBattler
.endm

View File

@ -7735,13 +7735,7 @@ BattleScript_IntimidateLoop:
jumpiftargetally BattleScript_IntimidateLoopIncrement
jumpifabsent BS_TARGET, BattleScript_IntimidateLoopIncrement
jumpifstatus2 BS_TARGET, STATUS2_SUBSTITUTE, BattleScript_IntimidateLoopIncrement
.if B_UPDATED_INTIMIDATE >= GEN_8 @These abilties specifically prevent just intimidate, without blocking stat decreases
jumpifability BS_TARGET, ABILITY_INNER_FOCUS, BattleScript_IntimidatePrevented
jumpifability BS_TARGET, ABILITY_SCRAPPY, BattleScript_IntimidatePrevented
jumpifability BS_TARGET, ABILITY_OWN_TEMPO, BattleScript_IntimidatePrevented
jumpifability BS_TARGET, ABILITY_OBLIVIOUS, BattleScript_IntimidatePrevented
.endif
jumpifability BS_TARGET, ABILITY_GUARD_DOG, BattleScript_IntimidateInReverse
jumpifintimidateabilityprevented
BattleScript_IntimidateEffect:
copybyte sBATTLER, gBattlerAttacker
setstatchanger STAT_ATK, 1, TRUE
@ -7766,9 +7760,10 @@ BattleScript_IntimidateLoopIncrement:
destroyabilitypopup
restoretarget
pause B_WAIT_TIME_MED
tryintimidatejectpack
end3
BattleScript_IntimidatePrevented:
BattleScript_IntimidatePrevented::
copybyte sBATTLER, gBattlerTarget
call BattleScript_AbilityPopUp
printstring STRINGID_PKMNPREVENTSSTATLOSSWITH
@ -7788,7 +7783,7 @@ BattleScript_IntimidateContrary_WontIncrease:
printstring STRINGID_TARGETSTATWONTGOHIGHER
goto BattleScript_IntimidateEffect_WaitString
BattleScript_IntimidateInReverse:
BattleScript_IntimidateInReverse::
copybyte sBATTLER, gBattlerTarget
call BattleScript_AbilityPopUpTarget
pause B_WAIT_TIME_SHORT
@ -9533,7 +9528,7 @@ BattleScript_EjectButtonActivates::
printstring STRINGID_EJECTBUTTONACTIVATE
waitmessage B_WAIT_TIME_LONG
removeitem BS_SCRIPTING
undodynamax BS_SCRIPTING
undodynamax BS_SCRIPTING
makeinvisible BS_SCRIPTING
openpartyscreen BS_SCRIPTING, BattleScript_EjectButtonEnd
copybyte sSAVED_BATTLER, sBATTLER

View File

@ -183,7 +183,8 @@ struct ProtectStruct
u16 eatMirrorHerb:1;
u16 activateOpportunist:2; // 2 - to copy stats. 1 - stats copied (do not repeat). 0 - no stats to copy
u16 usedAllySwitch:1;
u16 padding:2;
u16 lashOutAffected:1;
u16 padding:1;
// End of 16-bit bitfield
u32 physicalDmg;
u32 specialDmg;

View File

@ -170,6 +170,8 @@ extern const u8 BattleScript_RainDishActivates[];
extern const u8 BattleScript_SandstreamActivates[];
extern const u8 BattleScript_ShedSkinActivates[];
extern const u8 BattleScript_IntimidateActivates[];
extern const u8 BattleScript_IntimidatePrevented[];
extern const u8 BattleScript_IntimidateInReverse[];
extern const u8 BattleScript_DroughtActivates[];
extern const u8 BattleScript_TookAttack[];
extern const u8 BattleScript_SturdyPreventsOHKO[];

View File

@ -3751,6 +3751,7 @@ static void TryDoEventsBeforeFirstTurn(void)
{
for (i = 0; i < gBattlersCount; i++)
{
gBattleStruct->monToSwitchIntoId[i] = PARTY_SIZE; // Included here because switches can happen before during set ups (eg. eject pack)
struct Pokemon *party = GetBattlerParty(i);
struct Pokemon *mon = &party[gBattlerPartyIndexes[i]];
if (!IsBattlerAlive(i) || gBattleMons[i].species == SPECIES_NONE || GetMonData(mon, MON_DATA_IS_EGG))
@ -4846,7 +4847,6 @@ s8 GetBattleMovePriority(u32 battler, u16 move)
return priority;
}
// Function for AI with variables provided as arguments to speed the computation time
s32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMoves, u32 ability1, u32 ability2,
u32 holdEffectBattler1, u32 holdEffectBattler2, u32 speedBattler1, u32 speedBattler2, s32 priority1, s32 priority2)
{
@ -5135,6 +5135,7 @@ static void TurnValuesCleanUp(bool8 var0)
gSpecialStatuses[i].parentalBondState = PARENTAL_BOND_OFF;
gBattleStruct->battlerState[i].usedEjectItem = FALSE;
gProtectStructs[i].lashOutAffected = FALSE;
}
gSideStatuses[B_SIDE_PLAYER] &= ~(SIDE_STATUS_QUICK_GUARD | SIDE_STATUS_WIDE_GUARD | SIDE_STATUS_CRAFTY_SHIELD | SIDE_STATUS_MAT_BLOCK);

View File

@ -7149,6 +7149,9 @@ static void Cmd_moveend(void)
if (numEjectPackBattlers > 1)
SortBattlersBySpeed(battlers, FALSE);
for (i = 0; i < gBattlersCount; i++)
gProtectStructs[i].statFell = FALSE; // restore for every possible eject pack battler
for (i = 0; i < gBattlersCount; i++)
{
u32 battler = battlers[i];
@ -7167,7 +7170,6 @@ static void Cmd_moveend(void)
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_EjectPackActivates;
AI_DATA->ejectPackSwitch = TRUE;
gProtectStructs[battler].statFell = FALSE;
break; // Only the fastest Eject item activates
}
}
@ -12588,7 +12590,8 @@ static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr
}
else
{
gProtectStructs[battler].statFell = TRUE; // Eject pack, lash out
gProtectStructs[battler].statFell = TRUE;
gProtectStructs[battler].lashOutAffected = TRUE;
gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == battler); // B_MSG_ATTACKER_STAT_FELL or B_MSG_DEFENDER_STAT_FELL
}
}
@ -18811,3 +18814,87 @@ void BS_SetSteelsurge(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
void BS_JumpIfIntimidateAbilityPrevented(void)
{
NATIVE_ARGS();
u32 hasAbility = FALSE;
u32 ability = GetBattlerAbility(gBattlerTarget);
switch (ability)
{
case ABILITY_INNER_FOCUS:
case ABILITY_SCRAPPY:
case ABILITY_OWN_TEMPO:
case ABILITY_OBLIVIOUS:
if (B_UPDATED_INTIMIDATE >= GEN_8)
{
hasAbility = TRUE;
gBattlescriptCurrInstr = BattleScript_IntimidatePrevented;
}
else
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
break;
case ABILITY_GUARD_DOG:
hasAbility = TRUE;
gBattlescriptCurrInstr = BattleScript_IntimidateInReverse;
break;
default:
gBattlescriptCurrInstr = cmd->nextInstr;
break;
}
if (hasAbility)
{
gLastUsedAbility = ability;
gBattlerAbility = gBattlerTarget;
RecordAbilityBattle(gBattlerTarget, gLastUsedAbility);
}
}
void BS_TryIntimidatEjectpack(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;
AI_DATA->ejectPackSwitch = TRUE;
gBattleScripting.battler = affectedBattler;
gLastUsedItem = gBattleMons[affectedBattler].item;
RecordItemEffectBattle(affectedBattler, HOLD_EFFECT_EJECT_PACK);
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_EjectPackActivate_Ret;
}
}

View File

@ -8449,9 +8449,6 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn)
gBattlescriptCurrInstr = BattleScript_WhiteHerbRet;
}
break;
case HOLD_EFFECT_EJECT_PACK:
effect = TryEjectPack(battler, ITEMEFFECT_ON_SWITCH_IN);
break;
}
break;
}
@ -9229,7 +9226,7 @@ static inline u32 CalcMoveBasePower(struct DamageCalculationData *damageCalcData
basePower *= 2;
break;
case EFFECT_LASH_OUT:
if (gProtectStructs[battlerAtk].statFell)
if (gProtectStructs[battlerAtk].lashOutAffected)
basePower *= 2;
break;
case EFFECT_EXPLOSION:

View File

@ -104,7 +104,7 @@ DOUBLE_BATTLE_TEST("Intimidate doesn't activate on an empty field in a double ba
}
}
SINGLE_BATTLE_TEST("Intimidate and Eject Button force the opponent to Attack")
SINGLE_BATTLE_TEST("Intimidate and Eject Button don't force the opponent to Attack")
{
GIVEN {
ASSUME(gItemsInfo[ITEM_EJECT_BUTTON].holdEffect == HOLD_EFFECT_EJECT_BUTTON);
@ -365,6 +365,7 @@ DOUBLE_BATTLE_TEST("Intimidate will correctly decrease the attack of the second
} SCENE {
ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
ABILITY_POPUP(playerLeft, ABILITY_PROTOSYNTHESIS);
@ -372,7 +373,6 @@ DOUBLE_BATTLE_TEST("Intimidate will correctly decrease the attack of the second
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
}
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
}
}
@ -390,3 +390,48 @@ SINGLE_BATTLE_TEST("Intimdate does not lose timing after mega evolution and swit
ABILITY_POPUP(opponent, ABILITY_INTIMIDATE);
}
}
DOUBLE_BATTLE_TEST("Intimidate drop down both opposing atk before eject pack has the chance to activate")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); }
PLAYER(SPECIES_WYNAUT);
PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }
} WHEN {
TURN { SWITCH(opponentLeft, 2); SEND_OUT(playerLeft, 2); }
} SCENE {
ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
}
}
DOUBLE_BATTLE_TEST("Intimidate will not miss timing for competitive")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); }
PLAYER(SPECIES_MILOTIC) { Ability(ABILITY_COMPETITIVE); }
PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }
} WHEN {
TURN { SWITCH(opponentLeft, 2); SEND_OUT(playerLeft, 2); }
} SCENE {
ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
ABILITY_POPUP(playerRight, ABILITY_COMPETITIVE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
}
}

View File

@ -160,3 +160,74 @@ SINGLE_BATTLE_TEST("Eject Pack will miss timing to switch out user if Eject Butt
EXPECT(opponent->species == SPECIES_WYNAUT);
}
}
DOUBLE_BATTLE_TEST("Eject Pack: Only the fastest Eject Pack will activate after intimidate")
{
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) { Speed(4); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(5); }
OPPONENT(SPECIES_EKANS) { Speed(6); Ability(ABILITY_INTIMIDATE); }
} WHEN {
TURN {
SWITCH(opponentLeft, 2);
if (speed == 11)
SEND_OUT(playerRight, 2);
else
SEND_OUT(playerLeft, 2);
}
} SCENE {
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 a move stat drop")
{
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) { Speed(4); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(5); }
} WHEN {
TURN {
MOVE(opponentLeft, MOVE_BUBBLE);
if (speed == 11)
SEND_OUT(playerRight, 2);
else
SEND_OUT(playerLeft, 2);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BUBBLE, opponentLeft);
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);
}
}
}