Fixes Emergency Exit and Eject Pack (#6459)
This commit is contained in:
parent
7ead90aeff
commit
79a2ec1ce2
@ -7557,35 +7557,37 @@ BattleScript_MoodyEnd:
|
||||
end3
|
||||
|
||||
BattleScript_EmergencyExit::
|
||||
.if B_ABILITY_POP_UP == TRUE
|
||||
pause 5
|
||||
call BattleScript_AbilityPopUp
|
||||
call BattleScript_AbilityPopUpScripting
|
||||
pause B_WAIT_TIME_LONG
|
||||
BattleScript_EmergencyExitNoPopUp::
|
||||
playanimation BS_TARGET, B_ANIM_SLIDE_OFFSCREEN
|
||||
.endif
|
||||
playanimation BS_SCRIPTING, B_ANIM_SLIDE_OFFSCREEN
|
||||
waitanimation
|
||||
openpartyscreen BS_TARGET, BattleScript_EmergencyExitRet
|
||||
switchoutabilities BS_TARGET
|
||||
openpartyscreen BS_SCRIPTING, BattleScript_EmergencyExitRet
|
||||
switchoutabilities BS_SCRIPTING
|
||||
waitstate
|
||||
switchhandleorder BS_TARGET, 2
|
||||
switchhandleorder BS_SCRIPTING, 2
|
||||
returntoball BS_TARGET, FALSE
|
||||
getswitchedmondata BS_TARGET
|
||||
switchindataupdate BS_TARGET
|
||||
hpthresholds BS_TARGET
|
||||
getswitchedmondata BS_SCRIPTING
|
||||
switchindataupdate BS_SCRIPTING
|
||||
hpthresholds BS_SCRIPTING
|
||||
printstring STRINGID_SWITCHINMON
|
||||
switchinanim BS_TARGET, FALSE, TRUE
|
||||
switchinanim BS_SCRIPTING, FALSE, TRUE
|
||||
waitstate
|
||||
switchineffects BS_TARGET
|
||||
switchineffects BS_SCRIPTING
|
||||
BattleScript_EmergencyExitRet:
|
||||
return
|
||||
|
||||
BattleScript_EmergencyExitWild::
|
||||
.if B_ABILITY_POP_UP == TRUE
|
||||
pause 5
|
||||
call BattleScript_AbilityPopUp
|
||||
call BattleScript_AbilityPopUpScripting
|
||||
pause B_WAIT_TIME_LONG
|
||||
BattleScript_EmergencyExitWildNoPopUp::
|
||||
playanimation BS_TARGET, B_ANIM_SLIDE_OFFSCREEN
|
||||
.endif
|
||||
playanimation BS_SCRIPTING, B_ANIM_SLIDE_OFFSCREEN
|
||||
waitanimation
|
||||
setoutcomeonteleport BS_TARGET
|
||||
setoutcomeonteleport BS_SCRIPTING
|
||||
finishaction
|
||||
return
|
||||
|
||||
|
||||
@ -130,10 +130,9 @@ struct DisableStruct
|
||||
u8 boosterEnergyActivates:1;
|
||||
u8 roostActive:1;
|
||||
u8 unburdenActive:1;
|
||||
u8 startEmergencyExit:1;
|
||||
u8 neutralizingGas:1;
|
||||
u8 iceFaceActivationPrevention:1; // fixes hit escape move edge case
|
||||
u8 padding:2;
|
||||
u8 padding:3;
|
||||
};
|
||||
|
||||
// Fully Cleared each turn after end turn effects are done. A few things are cleared before end turn effects
|
||||
@ -814,9 +813,8 @@ struct BattleStruct
|
||||
u32 stellarBoostFlags[NUM_BATTLE_SIDES]; // stored as a bitfield of flags for all types for each side
|
||||
u8 monCausingSleepClause[NUM_BATTLE_SIDES]; // Stores which pokemon on a given side is causing Sleep Clause to be active as the mon's index in the party
|
||||
u8 additionalEffectsCounter:4; // A counter for the additionalEffects applied by the current move in Cmd_setadditionaleffects
|
||||
u8 redCardActivates:1;
|
||||
u8 cheekPouchActivated:1;
|
||||
u8 padding2:1; // padding in the middle so pursuit fields are together
|
||||
u8 padding2:3;
|
||||
u8 pursuitStoredSwitch; // Stored id for the Pursuit target's switch
|
||||
s32 battlerExpReward;
|
||||
u16 prevTurnSpecies[MAX_BATTLERS_COUNT]; // Stores species the AI has in play at start of turn
|
||||
|
||||
@ -384,9 +384,7 @@ extern const u8 BattleScript_FriskMsg[];
|
||||
extern const u8 BattleScript_FriskMsgWithPopup[];
|
||||
extern const u8 BattleScript_MoodyActivates[];
|
||||
extern const u8 BattleScript_EmergencyExit[];
|
||||
extern const u8 BattleScript_EmergencyExitNoPopUp[];
|
||||
extern const u8 BattleScript_EmergencyExitWild[];
|
||||
extern const u8 BattleScript_EmergencyExitWildNoPopUp[];
|
||||
extern const u8 BattleScript_CheekPouchActivates[];
|
||||
extern const u8 BattleScript_TotemVar[];
|
||||
extern const u8 BattleScript_TotemFlaredToLife[];
|
||||
|
||||
@ -349,5 +349,7 @@ void ClearPursuitValuesIfSet(u32 battler);
|
||||
void ClearPursuitValues(void);
|
||||
bool32 HasWeatherEffect(void);
|
||||
bool32 IsMovePowderBlocked(u32 battlerAtk, u32 battlerDef, u32 move);
|
||||
bool32 EmergencyExitCanBeTriggered(u32 battler);
|
||||
u32 RestoreWhiteHerbStats(u32 battler);
|
||||
|
||||
#endif // GUARD_BATTLE_UTIL_H
|
||||
|
||||
@ -271,6 +271,7 @@ enum MoveEndEffects
|
||||
MOVEEND_ITEM_EFFECTS_TARGET,
|
||||
MOVEEND_MOVE_EFFECTS2,
|
||||
MOVEEND_ITEM_EFFECTS_ALL,
|
||||
MOVEEND_SYMBIOSIS,
|
||||
MOVEEND_HIT_SWITCH_TARGET,
|
||||
MOVEEND_KINGSROCK, // These item effects will occur each strike of a multi-hit move
|
||||
MOVEEND_NUM_HITS,
|
||||
@ -285,15 +286,16 @@ enum MoveEndEffects
|
||||
MOVEEND_RAPID_SPIN,
|
||||
MOVEEND_ITEM_EFFECTS_ATTACKER,
|
||||
MOVEEND_MAGICIAN, // Occurs after final multi-hit strike, and after other items/abilities would activate
|
||||
MOVEEND_SHEER_FORCE, // If move is Sheer Force affected, skip until Eject Pack
|
||||
MOVEEND_RED_CARD, // Red Card triggers before Eject Pack
|
||||
MOVEEND_EJECT_ITEMS,
|
||||
MOVEEND_WHITE_HERB,
|
||||
MOVEEND_EJECT_BUTTON,
|
||||
MOVEEND_LIFEORB_SHELLBELL, // Includes shell bell, throat spray, etc
|
||||
MOVEEND_CHANGED_ITEMS,
|
||||
MOVEEND_PICKPOCKET,
|
||||
MOVEEND_EMERGENCY_EXIT,
|
||||
MOVEEND_SYMBIOSIS,
|
||||
MOVEEND_EJECT_PACK,
|
||||
MOVEEND_OPPORTUNIST, // Occurs after other stat change items/abilities to try and copy the boosts
|
||||
MOVEEND_PICKPOCKET,
|
||||
MOVEEND_WHITE_HERB,
|
||||
MOVEEND_CHANGED_ITEMS,
|
||||
MOVEEND_SAME_MOVE_TURNS,
|
||||
MOVEEND_SET_EVOLUTION_TRACKER,
|
||||
MOVEEND_CLEAR_BITS,
|
||||
|
||||
@ -6170,7 +6170,7 @@ static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent)
|
||||
return battler;
|
||||
}
|
||||
|
||||
static inline bool32 IsProtectivePadsProtected(u32 battler, u32 move, u32 holdEffect)
|
||||
static inline bool32 IsProtectivePadsProtected(u32 battler, u32 holdEffect)
|
||||
{
|
||||
if (holdEffect != HOLD_EFFECT_PROTECTIVE_PADS)
|
||||
return FALSE;
|
||||
@ -6182,7 +6182,7 @@ static inline bool32 IsProtectivePadsProtected(u32 battler, u32 move, u32 holdEf
|
||||
static inline bool32 IsProtectEffectAffected(u32 battler, u32 move)
|
||||
{
|
||||
u32 holdEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
|
||||
if (IsProtectivePadsProtected(battler, move, holdEffect))
|
||||
if (IsProtectivePadsProtected(battler, holdEffect))
|
||||
return TRUE;
|
||||
|
||||
if (holdEffect == HOLD_EFFECT_CLEAR_AMULET)
|
||||
@ -6201,6 +6201,33 @@ static inline bool32 IsProtectEffectAffected(u32 battler, u32 move)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static inline bool32 CanEjectButtonTrigger(u32 battlerAtk, u32 battlerDef, u32 moveEffect)
|
||||
{
|
||||
if (GetBattlerHoldEffect(battlerDef, TRUE) == HOLD_EFFECT_EJECT_BUTTON
|
||||
&& battlerAtk != battlerDef
|
||||
&& IsBattlerTurnDamaged(battlerDef)
|
||||
&& IsBattlerAlive(battlerDef)
|
||||
&& CountUsablePartyMons(battlerDef) > 0
|
||||
&& !(moveEffect == EFFECT_HIT_SWITCH_TARGET && CanBattlerSwitch(battlerAtk)))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static inline bool32 CanEjectPackTrigger(u32 battlerAtk, u32 battlerDef, u32 moveEffect)
|
||||
{
|
||||
if (gProtectStructs[battlerDef].statFell
|
||||
&& GetBattlerHoldEffect(battlerDef, TRUE) == HOLD_EFFECT_EJECT_PACK
|
||||
&& IsBattlerAlive(battlerDef)
|
||||
&& CountUsablePartyMons(battlerDef) > 0
|
||||
&& !gProtectStructs[battlerDef].disableEjectPack
|
||||
&& !(moveEffect == EFFECT_HIT_SWITCH_TARGET && CanBattlerSwitch(battlerAtk))
|
||||
&& !(moveEffect == EFFECT_PARTING_SHOT && CanBattlerSwitch(battlerAtk)))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void Cmd_moveend(void)
|
||||
{
|
||||
CMD_ARGS(u8 endMode, u8 endState);
|
||||
@ -6237,7 +6264,7 @@ static void Cmd_moveend(void)
|
||||
if (gProtectStructs[gBattlerAttacker].touchedProtectLike)
|
||||
{
|
||||
if (gProtectStructs[gBattlerTarget].spikyShielded
|
||||
&& !IsProtectivePadsProtected(gBattlerAttacker, gCurrentMove, GetBattlerHoldEffect(gBattlerAttacker, TRUE))
|
||||
&& !IsProtectivePadsProtected(gBattlerAttacker, GetBattlerHoldEffect(gBattlerAttacker, TRUE))
|
||||
&& !IsMagicGuardProtected(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)))
|
||||
{
|
||||
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
|
||||
@ -6265,7 +6292,7 @@ static void Cmd_moveend(void)
|
||||
effect = 1;
|
||||
}
|
||||
else if (gProtectStructs[gBattlerTarget].banefulBunkered
|
||||
&& !IsProtectivePadsProtected(gBattlerAttacker, gCurrentMove, GetBattlerHoldEffect(gBattlerAttacker, TRUE)))
|
||||
&& !IsProtectivePadsProtected(gBattlerAttacker, GetBattlerHoldEffect(gBattlerAttacker, TRUE)))
|
||||
{
|
||||
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
|
||||
gBattleScripting.moveEffect = MOVE_EFFECT_POISON | MOVE_EFFECT_AFFECTS_USER;
|
||||
@ -6301,7 +6328,7 @@ static void Cmd_moveend(void)
|
||||
effect = 1;
|
||||
}
|
||||
else if (gProtectStructs[gBattlerTarget].burningBulwarked
|
||||
&& !IsProtectivePadsProtected(gBattlerAttacker, gCurrentMove, GetBattlerHoldEffect(gBattlerAttacker, TRUE)))
|
||||
&& !IsProtectivePadsProtected(gBattlerAttacker, GetBattlerHoldEffect(gBattlerAttacker, TRUE)))
|
||||
{
|
||||
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
|
||||
gBattleScripting.moveEffect = MOVE_EFFECT_BURN | MOVE_EFFECT_AFFECTS_USER;
|
||||
@ -6994,13 +7021,6 @@ static void Cmd_moveend(void)
|
||||
gBattleScripting.moveendState++;
|
||||
break;
|
||||
}
|
||||
// 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
|
||||
@ -7012,7 +7032,6 @@ static void Cmd_moveend(void)
|
||||
&& !gSpecialStatuses[gBattlerAttacker].gemBoost // In base game, gems are consumed after magician would activate.
|
||||
&& !(gWishFutureKnock.knockedOffMons[GetBattlerSide(gBattlerTarget)] & (1u << gBattlerPartyIndexes[gBattlerTarget]))
|
||||
&& !DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove)
|
||||
&& !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)
|
||||
&& (GetBattlerAbility(gBattlerTarget) != ABILITY_STICKY_HOLD || !IsBattlerAlive(gBattlerTarget)))
|
||||
{
|
||||
StealTargetItem(gBattlerAttacker, gBattlerTarget);
|
||||
@ -7025,92 +7044,121 @@ static void Cmd_moveend(void)
|
||||
}
|
||||
gBattleScripting.moveendState++;
|
||||
break;
|
||||
case MOVEEND_EJECT_ITEMS:
|
||||
case MOVEEND_SHEER_FORCE:
|
||||
if (TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove))
|
||||
gBattleScripting.moveendState = MOVEEND_EJECT_PACK;
|
||||
else
|
||||
gBattleScripting.moveendState++;
|
||||
break;
|
||||
case MOVEEND_EJECT_BUTTON:
|
||||
{
|
||||
// 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;
|
||||
u32 numEjectButtonBattlers = 0;
|
||||
u32 ejectButtonBattlers = 0;
|
||||
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
u32 holdEffect;
|
||||
holdEffect = GetBattlerHoldEffect(i, TRUE);
|
||||
if (holdEffect == HOLD_EFFECT_EJECT_BUTTON)
|
||||
if (CanEjectButtonTrigger(gBattlerAttacker, i, moveEffect))
|
||||
{
|
||||
ejectButtonBattlers |= 1u << i;
|
||||
else if (holdEffect == HOLD_EFFECT_EJECT_PACK)
|
||||
ejectPackBattlers |= 1u << i;
|
||||
numEjectButtonBattlers++;
|
||||
}
|
||||
}
|
||||
if (ejectButtonBattlers || ejectPackBattlers)
|
||||
|
||||
if (numEjectButtonBattlers == 0)
|
||||
{
|
||||
u8 battlers[4] = {0, 1, 2, 3};
|
||||
gBattleScripting.moveendState++;
|
||||
break;
|
||||
}
|
||||
|
||||
u8 battlers[4] = {0, 1, 2, 3};
|
||||
if (numEjectButtonBattlers > 1)
|
||||
SortBattlersBySpeed(battlers, FALSE);
|
||||
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
u32 battler = battlers[i];
|
||||
|
||||
if (!(ejectButtonBattlers & 1u << battler))
|
||||
continue;
|
||||
|
||||
gBattleScripting.battler = battler;
|
||||
gLastUsedItem = gBattleMons[battler].item;
|
||||
if (moveEffect == EFFECT_HIT_ESCAPE)
|
||||
gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection
|
||||
|
||||
effect = TRUE;
|
||||
gBattleScripting.moveendState = MOVEEND_OPPORTUNIST;
|
||||
gBattleStruct->battlerState[battler].usedEjectItem = TRUE;
|
||||
BattleScriptPushCursor();
|
||||
gBattlescriptCurrInstr = BattleScript_EjectButtonActivates;
|
||||
AI_DATA->ejectButtonSwitch = TRUE;
|
||||
break; // Only the fastest Eject Button activates
|
||||
}
|
||||
}
|
||||
if (!effect)
|
||||
gBattleScripting.moveendState++;
|
||||
break;
|
||||
case MOVEEND_EJECT_PACK:
|
||||
{
|
||||
// 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 (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
if (CanEjectPackTrigger(gBattlerAttacker, i, moveEffect))
|
||||
{
|
||||
u32 battler = battlers[i];
|
||||
|
||||
if (battler != gBattlerAttacker && ejectButtonBattlers & (1u << 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 (!IsBattlerTurnDamaged(battler))
|
||||
continue;
|
||||
}
|
||||
else if (ejectPackBattlers & (1u << 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
|
||||
&& !(moveEffect == EFFECT_HIT_SWITCH_TARGET && CanBattlerSwitch(gBattlerAttacker))
|
||||
)
|
||||
{
|
||||
gBattleScripting.battler = battler;
|
||||
gLastUsedItem = gBattleMons[battler].item;
|
||||
if (moveEffect == EFFECT_HIT_ESCAPE)
|
||||
gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection
|
||||
if (ejectButtonBattlers & (1u << battler))
|
||||
{
|
||||
effect = TRUE;
|
||||
gBattleStruct->battlerState[battler].usedEjectItem = TRUE;
|
||||
BattleScriptPushCursor();
|
||||
gBattlescriptCurrInstr = BattleScript_EjectButtonActivates;
|
||||
AI_DATA->ejectButtonSwitch = TRUE;
|
||||
}
|
||||
else // Eject Pack
|
||||
{
|
||||
if (!gDisableStructs[gBattlerTarget].startEmergencyExit
|
||||
&& !(GetMoveEffect(gCurrentMove) == EFFECT_PARTING_SHOT && CanBattlerSwitch(gBattlerAttacker)))
|
||||
{
|
||||
effect = TRUE;
|
||||
gBattleStruct->battlerState[battler].usedEjectItem = TRUE;
|
||||
BattleScriptPushCursor();
|
||||
gBattlescriptCurrInstr = BattleScript_EjectPackActivates;
|
||||
AI_DATA->ejectPackSwitch = TRUE;
|
||||
gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage = TRUE;
|
||||
}
|
||||
gProtectStructs[battler].statFell = FALSE;
|
||||
}
|
||||
break; // Only the fastest Eject item activates
|
||||
}
|
||||
ejectPackBattlers |= 1u << i;
|
||||
numEjectPackBattlers++;
|
||||
}
|
||||
}
|
||||
|
||||
if (numEjectPackBattlers == 0)
|
||||
{
|
||||
gBattleScripting.moveendState++;
|
||||
break;
|
||||
}
|
||||
|
||||
u8 battlers[4] = {0, 1, 2, 3};
|
||||
if (numEjectPackBattlers > 1)
|
||||
SortBattlersBySpeed(battlers, FALSE);
|
||||
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
u32 battler = battlers[i];
|
||||
|
||||
if (!(ejectPackBattlers & 1u << battler))
|
||||
continue;
|
||||
|
||||
gBattleScripting.battler = battler;
|
||||
gLastUsedItem = gBattleMons[battler].item;
|
||||
|
||||
if (moveEffect == EFFECT_HIT_ESCAPE)
|
||||
gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection
|
||||
|
||||
effect = TRUE;
|
||||
gBattleStruct->battlerState[battler].usedEjectItem = TRUE;
|
||||
BattleScriptPushCursor();
|
||||
gBattlescriptCurrInstr = BattleScript_EjectPackActivates;
|
||||
AI_DATA->ejectPackSwitch = TRUE;
|
||||
gProtectStructs[battler].statFell = FALSE;
|
||||
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))
|
||||
if (!IsBattlerAlive(i))
|
||||
continue;
|
||||
|
||||
if (GetBattlerHoldEffect(i, TRUE) == HOLD_EFFECT_RESTORE_STATS
|
||||
&& RestoreWhiteHerbStats(i))
|
||||
{
|
||||
BattleScriptPushCursor();
|
||||
gBattlescriptCurrInstr = BattleScript_WhiteHerbRet;
|
||||
effect = TRUE;
|
||||
break;
|
||||
}
|
||||
@ -7130,8 +7178,7 @@ static void Cmd_moveend(void)
|
||||
}
|
||||
if (redCardBattlers
|
||||
&& (moveEffect != EFFECT_HIT_SWITCH_TARGET || gBattleStruct->hitSwitchTargetFailed)
|
||||
&& IsBattlerAlive(gBattlerAttacker)
|
||||
&& !TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove))
|
||||
&& IsBattlerAlive(gBattlerAttacker))
|
||||
{
|
||||
// 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
|
||||
@ -7148,33 +7195,29 @@ static void Cmd_moveend(void)
|
||||
&& IsBattlerTurnDamaged(battler)
|
||||
&& CanBattlerSwitch(gBattlerAttacker))
|
||||
{
|
||||
effect = TRUE;
|
||||
gBattleScripting.moveendState = MOVEEND_OPPORTUNIST;
|
||||
gLastUsedItem = gBattleMons[battler].item;
|
||||
SaveBattlerTarget(battler); // save battler with red card
|
||||
SaveBattlerAttacker(gBattlerAttacker);
|
||||
gBattleStruct->savedMove = gCurrentMove;
|
||||
gBattleScripting.battler = battler;
|
||||
gEffectBattler = gBattlerAttacker;
|
||||
gBattleStruct->redCardActivates = TRUE;
|
||||
if (moveEffect == EFFECT_HIT_ESCAPE)
|
||||
gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection
|
||||
BattleScriptPushCursor();
|
||||
if (gBattleStruct->commanderActive[gBattlerAttacker] != SPECIES_NONE
|
||||
|| GetBattlerAbility(gBattlerAttacker) == ABILITY_GUARD_DOG)
|
||||
{
|
||||
gBattlescriptCurrInstr = BattleScript_RedCardActivationNoSwitch;
|
||||
}
|
||||
else
|
||||
{
|
||||
gBattlescriptCurrInstr = BattleScript_RedCardActivates;
|
||||
gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage = TRUE;
|
||||
}
|
||||
effect = TRUE;
|
||||
break; // Only fastest red card activates
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
gBattleScripting.moveendState++;
|
||||
if (!effect)
|
||||
gBattleScripting.moveendState++;
|
||||
break;
|
||||
case MOVEEND_LIFEORB_SHELLBELL:
|
||||
if (ItemBattleEffects(ITEMEFFECT_LIFEORB_SHELLBELL, 0, FALSE))
|
||||
@ -7219,37 +7262,57 @@ static void Cmd_moveend(void)
|
||||
gBattleScripting.moveendState++;
|
||||
break;
|
||||
case MOVEEND_EMERGENCY_EXIT: // Special case, because moves hitting multiple opponents stop after switching out
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
if (gBattleStruct->redCardActivates)
|
||||
// Because sorting the battlers by speed takes lots of cycles,
|
||||
// we check if EE can be activated and cound how many.
|
||||
u32 numEmergencyExitBattlers = 0;
|
||||
u32 emergencyExitBattlers = 0;
|
||||
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
gDisableStructs[i].startEmergencyExit = FALSE;
|
||||
continue;
|
||||
if (EmergencyExitCanBeTriggered(i))
|
||||
{
|
||||
emergencyExitBattlers |= 1u << i;
|
||||
numEmergencyExitBattlers++;
|
||||
}
|
||||
}
|
||||
if (gDisableStructs[i].startEmergencyExit)
|
||||
|
||||
if (numEmergencyExitBattlers == 0)
|
||||
{
|
||||
gDisableStructs[i].startEmergencyExit = FALSE;
|
||||
gSpecialStatuses[i].emergencyExited = TRUE;
|
||||
gBattlerTarget = gBattlerAbility = i;
|
||||
gBattleScripting.moveendState++;
|
||||
break;
|
||||
}
|
||||
|
||||
u8 battlers[4] = {0, 1, 2, 3};
|
||||
if (numEmergencyExitBattlers > 1)
|
||||
SortBattlersBySpeed(battlers, FALSE);
|
||||
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
u32 battler = battlers[i];
|
||||
|
||||
if (!(emergencyExitBattlers & 1u << battler))
|
||||
continue;
|
||||
|
||||
if (moveEffect == EFFECT_HIT_ESCAPE)
|
||||
gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection
|
||||
|
||||
effect = TRUE;
|
||||
gBattleScripting.moveendState = MOVEEND_OPPORTUNIST;
|
||||
gSpecialStatuses[battler].emergencyExited = TRUE;
|
||||
gBattleScripting.battler = battler;
|
||||
BattleScriptPushCursor();
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER || GetBattlerSide(i) == B_SIDE_PLAYER)
|
||||
{
|
||||
if (B_ABILITY_POP_UP == TRUE)
|
||||
gBattlescriptCurrInstr = BattleScript_EmergencyExit;
|
||||
else
|
||||
gBattlescriptCurrInstr = BattleScript_EmergencyExitNoPopUp;
|
||||
}
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER || GetBattlerSide(battler) == B_SIDE_PLAYER)
|
||||
gBattlescriptCurrInstr = BattleScript_EmergencyExit;
|
||||
else
|
||||
{
|
||||
if (B_ABILITY_POP_UP == TRUE)
|
||||
gBattlescriptCurrInstr = BattleScript_EmergencyExitWild;
|
||||
else
|
||||
gBattlescriptCurrInstr = BattleScript_EmergencyExitWildNoPopUp;
|
||||
}
|
||||
return;
|
||||
gBattlescriptCurrInstr = BattleScript_EmergencyExitWild;
|
||||
|
||||
break; // Only the fastest Emergency Exit / Wimp Out activates
|
||||
}
|
||||
}
|
||||
gBattleScripting.moveendState++;
|
||||
if (!effect)
|
||||
gBattleScripting.moveendState++;
|
||||
break;
|
||||
case MOVEEND_SYMBIOSIS:
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
@ -7326,7 +7389,6 @@ static void Cmd_moveend(void)
|
||||
gBattleStruct->additionalEffectsCounter = 0;
|
||||
gBattleStruct->poisonPuppeteerConfusion = FALSE;
|
||||
gBattleStruct->fickleBeamBoosted = FALSE;
|
||||
gBattleStruct->redCardActivates = FALSE;
|
||||
gBattleStruct->battlerState[gBattlerAttacker].usedMicleBerry = FALSE;
|
||||
gBattleStruct->noTargetPresent = FALSE;
|
||||
if (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
|
||||
|
||||
@ -5683,25 +5683,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
||||
effect++;
|
||||
}
|
||||
break;
|
||||
case ABILITY_EMERGENCY_EXIT:
|
||||
case ABILITY_WIMP_OUT:
|
||||
if (!(gBattleStruct->moveResultFlags[battler] & MOVE_RESULT_NO_EFFECT)
|
||||
&& IsBattlerTurnDamaged(gBattlerTarget)
|
||||
&& IsBattlerAlive(battler)
|
||||
// Had more than half of hp before, now has less
|
||||
&& HadMoreThanHalfHpNowDoesnt(battler)
|
||||
&& (gMultiHitCounter == 0 || gMultiHitCounter == 1)
|
||||
&& !(TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove))
|
||||
&& (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER))
|
||||
&& !(gBattleTypeFlags & BATTLE_TYPE_ARENA)
|
||||
&& CountUsablePartyMons(battler) > 0
|
||||
// Not currently held by Sky Drop
|
||||
&& !(gStatuses3[battler] & STATUS3_SKY_DROPPED))
|
||||
{
|
||||
gDisableStructs[battler].startEmergencyExit = TRUE;
|
||||
effect++;
|
||||
}
|
||||
break;
|
||||
case ABILITY_WEAK_ARMOR:
|
||||
if (!(gBattleStruct->moveResultFlags[battler] & MOVE_RESULT_NO_EFFECT)
|
||||
&& IsBattlerTurnDamaged(gBattlerTarget)
|
||||
@ -7228,8 +7209,6 @@ static u32 ItemHealHp(u32 battler, u32 itemId, enum ItemCaseId caseID, bool32 pe
|
||||
BattleScriptPushCursor();
|
||||
gBattlescriptCurrInstr = BattleScript_ItemHealHP_RemoveItemRet;
|
||||
}
|
||||
if (gDisableStructs[battler].startEmergencyExit && GetNonDynamaxHP(battler) >= GetNonDynamaxMaxHP(battler) / 2)
|
||||
gDisableStructs[battler].startEmergencyExit = FALSE;
|
||||
|
||||
return ITEM_HP_CHANGE;
|
||||
}
|
||||
@ -7350,7 +7329,7 @@ static inline u32 TryBoosterEnergy(u32 battler, enum ItemCaseId caseID)
|
||||
return ITEM_NO_EFFECT;
|
||||
}
|
||||
|
||||
static u32 RestoreWhiteHerbStats(u32 battler)
|
||||
u32 RestoreWhiteHerbStats(u32 battler)
|
||||
{
|
||||
u32 i, effect = 0;
|
||||
|
||||
@ -12302,3 +12281,22 @@ bool32 IsMovePowderBlocked(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
|
||||
return effect;
|
||||
}
|
||||
|
||||
bool32 EmergencyExitCanBeTriggered(u32 battler)
|
||||
{
|
||||
u32 ability = GetBattlerAbility(battler);
|
||||
|
||||
if (ability != ABILITY_EMERGENCY_EXIT && ability != ABILITY_WIMP_OUT)
|
||||
return FALSE;
|
||||
|
||||
if (IsBattlerTurnDamaged(battler)
|
||||
&& IsBattlerAlive(battler)
|
||||
&& HadMoreThanHalfHpNowDoesnt(battler)
|
||||
&& (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER))
|
||||
&& !(gBattleTypeFlags & BATTLE_TYPE_ARENA)
|
||||
&& CountUsablePartyMons(battler) > 0
|
||||
&& !(gStatuses3[battler] & STATUS3_SKY_DROPPED))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -47,3 +47,22 @@ SINGLE_BATTLE_TEST("Emergency Exit switches out when going below 50% max-HP but
|
||||
ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Only the fastest Wimp Out (Emergency Exit) user switches out")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZAPDOS) { Speed(10); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(10); }
|
||||
OPPONENT(SPECIES_WIMPOD) { Speed(1); Ability(ABILITY_WIMP_OUT); Item(ITEM_FOCUS_SASH); };
|
||||
OPPONENT(SPECIES_WIMPOD) { Speed(2); Ability(ABILITY_WIMP_OUT); Item(ITEM_FOCUS_SASH); };
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(10); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(10); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_HYPER_VOICE); SEND_OUT(opponentRight, 2); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPER_VOICE, playerLeft);
|
||||
HP_BAR(opponentLeft);
|
||||
HP_BAR(opponentRight);
|
||||
ABILITY_POPUP(opponentRight, ABILITY_WIMP_OUT);
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,3 +137,26 @@ DOUBLE_BATTLE_TEST("Eject Pack will not trigger if the conditions are not met")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Eject Pack will miss timing to switch out user if Eject Button was activated on target")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(10); Item(ITEM_EJECT_PACK); }
|
||||
PLAYER(SPECIES_WYNAUT) { Speed(10); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(5); Item(ITEM_EJECT_BUTTON); }
|
||||
OPPONENT(SPECIES_WYNAUT) { Speed(10); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_OVERHEAT); SEND_OUT(opponent, 1); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_OVERHEAT, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
NONE_OF {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
|
||||
MESSAGE("Wobbuffet is switched out with the Eject Pack!");
|
||||
}
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
|
||||
} THEN {
|
||||
EXPECT(player->species == SPECIES_WOBBUFFET);
|
||||
EXPECT(opponent->species == SPECIES_WYNAUT);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,15 +16,16 @@ DOUBLE_BATTLE_TEST("Spread Moves: Ability and Item effects activate correctly af
|
||||
MOVE(opponentRight, MOVE_HEAT_WAVE);
|
||||
MOVE(playerLeft, MOVE_HYPER_VOICE);
|
||||
SEND_OUT(opponentRight, 3);
|
||||
SEND_OUT(opponentLeft, 2);
|
||||
}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPER_VOICE, playerLeft);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentRight);
|
||||
MESSAGE("The opposing Wobbuffet is switched out with the Eject Button!");
|
||||
MESSAGE("2 sent out Pikachu!");
|
||||
ABILITY_POPUP(opponentLeft, ABILITY_EMERGENCY_EXIT);
|
||||
MESSAGE("2 sent out Wynaut!");
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(opponentLeft, ABILITY_EMERGENCY_EXIT);
|
||||
MESSAGE("2 sent out Wynaut!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user