Sky Drop clean up and tests (#6218)

Co-authored-by: Bassoonian <iasperbassoonian@gmail.com>
This commit is contained in:
Alex 2025-04-20 23:07:46 +02:00 committed by GitHub
parent 6b8e680139
commit db7e45f1d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 249 additions and 113 deletions

View File

@ -172,6 +172,19 @@ enum SleepClauseBlock
BLOCKED_BY_SLEEP_CLAUSE,
};
enum SkyDropState
{
SKY_DROP_IGNORE,
SKY_DROP_ATTACKCANCELLER_CHECK,
SKY_DROP_GRAVITY_ON_AIRBORNE,
SKY_DROP_CANCEL_MULTI_TURN_MOVES,
SKY_DROP_STATUS_YAWN,
SKY_DROP_STATUS_FREEZE_SLEEP,
};
#define SKY_DROP_NO_TARGET 0xFF
#define SKY_DROP_RELEASED_TARGET 0xFE
void HandleAction_ThrowBall(void);
u32 GetCurrentBattleWeather(void);
bool32 EndOrContinueWeather(void);
@ -195,7 +208,7 @@ u8 GetBattlerForBattleScript(u8 caseId);
bool32 IsBattlerMarkedForControllerExec(u32 battler);
void MarkBattlerForControllerExec(u32 battler);
void MarkBattlerReceivedLinkData(u32 battler);
const u8* CancelMultiTurnMoves(u32 battler);
const u8 *CancelMultiTurnMoves(u32 battler, enum SkyDropState skyDropState);
bool32 WasUnableToUseMove(u32 battler);
void PrepareStringBattle(enum StringID stringId, u32 battler);
void ResetSentPokesToOpponentValue(void);

View File

@ -445,7 +445,7 @@ static bool32 HandleEndTurnFirstEventBlock(u32 battler)
gBattleMons[battler].status2 -= STATUS2_LOCK_CONFUSE_TURN(1);
if (WasUnableToUseMove(battler))
{
CancelMultiTurnMoves(battler);
CancelMultiTurnMoves(battler, SKY_DROP_IGNORE);
}
else if (!(gBattleMons[battler].status2 & STATUS2_LOCK_CONFUSE) && (gBattleMons[battler].status2 & STATUS2_MULTIPLETURNS))
{
@ -993,7 +993,7 @@ static bool32 HandleEndTurnYawn(u32 battler)
&& !UproarWakeUpCheck(battler)
&& !IsLeafGuardProtected(battler, ability))
{
CancelMultiTurnMoves(battler);
CancelMultiTurnMoves(battler, SKY_DROP_STATUS_YAWN);
gEffectBattler = gBattlerTarget = battler;
if (IsBattlerTerrainAffected(battler, STATUS_FIELD_ELECTRIC_TERRAIN))
{
@ -1379,7 +1379,7 @@ static bool32 HandleEndTurnThirdEventBlock(u32 battler)
gBattleMons[battler].status2 -= STATUS2_UPROAR_TURN(1); // uproar timer goes down
if (WasUnableToUseMove(battler))
{
CancelMultiTurnMoves(battler);
CancelMultiTurnMoves(battler, SKY_DROP_IGNORE);
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_ENDS;
}
else if (gBattleMons[battler].status2 & STATUS2_UPROAR)
@ -1390,7 +1390,7 @@ static bool32 HandleEndTurnThirdEventBlock(u32 battler)
else
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_ENDS;
CancelMultiTurnMoves(battler);
CancelMultiTurnMoves(battler, SKY_DROP_IGNORE);
}
BattleScriptExecute(BattleScript_PrintUproarOverTurns);
effect = TRUE;

View File

@ -3007,7 +3007,7 @@ static void BattleStartClearSetData(void)
gBattleStruct->lastTakenMoveFrom[i][2] = MOVE_NONE;
gBattleStruct->lastTakenMoveFrom[i][3] = MOVE_NONE;
gBattleStruct->AI_monToSwitchIntoId[i] = PARTY_SIZE;
gBattleStruct->skyDropTargets[i] = 0xFF;
gBattleStruct->skyDropTargets[i] = SKY_DROP_NO_TARGET;
}
gLastUsedMove = 0;
@ -3357,14 +3357,14 @@ const u8* FaintClearSetData(u32 battler)
TryBattleFormChange(battler, FORM_CHANGE_FAINT);
// If the fainted mon was involved in a Sky Drop
if (gBattleStruct->skyDropTargets[battler] != 0xFF)
if (gBattleStruct->skyDropTargets[battler] != SKY_DROP_NO_TARGET)
{
// Get battler id of the other Pokemon involved in this Sky Drop
u8 otherSkyDropper = gBattleStruct->skyDropTargets[battler];
// Clear Sky Drop data
gBattleStruct->skyDropTargets[battler] = 0xFF;
gBattleStruct->skyDropTargets[otherSkyDropper] = 0xFF;
gBattleStruct->skyDropTargets[battler] = SKY_DROP_NO_TARGET;
gBattleStruct->skyDropTargets[otherSkyDropper] = SKY_DROP_NO_TARGET;
// If the other Pokemon involved in this Sky Drop was the target, not the attacker
if (gStatuses3[otherSkyDropper] & STATUS3_SKY_DROPPED)
@ -3915,7 +3915,7 @@ static void HandleEndTurn_ContinueBattle(void)
{
gBattleMons[i].status2 &= ~STATUS2_FLINCHED;
if ((gBattleMons[i].status1 & STATUS1_SLEEP) && (gBattleMons[i].status2 & STATUS2_MULTIPLETURNS))
CancelMultiTurnMoves(i);
CancelMultiTurnMoves(i, SKY_DROP_IGNORE);
}
gBattleStruct->eventBlockCounter = 0;
gBattleStruct->turnEffectsBattlerId = 0;

View File

@ -1319,7 +1319,7 @@ static void Cmd_attackcanceler(void)
gBattlescriptCurrInstr = BattleScript_FailedFromAtkString;
if (!gBattleMoveEffects[effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))
CancelMultiTurnMoves(gBattlerAttacker);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
return;
}
@ -1423,7 +1423,7 @@ static void Cmd_attackcanceler(void)
{
if (IsMoveMakingContact(gCurrentMove, gBattlerAttacker))
gProtectStructs[gBattlerAttacker].touchedProtectLike = TRUE;
CancelMultiTurnMoves(gBattlerAttacker);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED;
gLastLandedMoves[gBattlerTarget] = 0;
gLastHitByType[gBattlerTarget] = 0;
@ -1504,14 +1504,14 @@ static bool32 AccuracyCalcHelper(u32 move, u32 battler)
// If the attacker has the ability No Guard and they aren't targeting a Pokemon involved in a Sky Drop with the move Sky Drop, move hits.
else if (GetBattlerAbility(gBattlerAttacker) == ABILITY_NO_GUARD
&& !(gStatuses3[battler] & STATUS3_COMMANDER)
&& (moveEffect != EFFECT_SKY_DROP || gBattleStruct->skyDropTargets[battler] == 0xFF))
&& (moveEffect != EFFECT_SKY_DROP || gBattleStruct->skyDropTargets[battler] == SKY_DROP_NO_TARGET))
{
effect = TRUE;
ability = ABILITY_NO_GUARD;
}
// If the target has the ability No Guard and they aren't involved in a Sky Drop or the current move isn't Sky Drop, move hits.
else if (GetBattlerAbility(battler) == ABILITY_NO_GUARD
&& (moveEffect != EFFECT_SKY_DROP || gBattleStruct->skyDropTargets[battler] == 0xFF))
&& (moveEffect != EFFECT_SKY_DROP || gBattleStruct->skyDropTargets[battler] == SKY_DROP_NO_TARGET))
{
effect = TRUE;
ability = ABILITY_NO_GUARD;
@ -3303,7 +3303,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
if (!CanBeSlept(gEffectBattler, GetBattlerAbility(gEffectBattler), BLOCKED_BY_SLEEP_CLAUSE) && !gBattleStruct->battlerState[gEffectBattler].sleepClauseEffectExempt)
break;
cancelMultiTurnMovesResult = CancelMultiTurnMoves(gEffectBattler);
cancelMultiTurnMovesResult = CancelMultiTurnMoves(gEffectBattler, SKY_DROP_STATUS_FREEZE_SLEEP);
if (cancelMultiTurnMovesResult)
gBattlescriptCurrInstr = cancelMultiTurnMovesResult;
statusChanged = TRUE;
@ -3397,7 +3397,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
if (!CanBeFrozen(gEffectBattler))
break;
cancelMultiTurnMovesResult = CancelMultiTurnMoves(gEffectBattler);
cancelMultiTurnMovesResult = CancelMultiTurnMoves(gEffectBattler, SKY_DROP_STATUS_FREEZE_SLEEP);
if (cancelMultiTurnMovesResult)
gBattlescriptCurrInstr = cancelMultiTurnMovesResult;
statusChanged = TRUE;
@ -4320,8 +4320,8 @@ void SetMoveEffect(bool32 primary, bool32 certain)
MarkBattlerForControllerExec(gBattlerTarget);
}
if (gBattleMons[gBattlerTarget].pp[i] == 0 && gBattleStruct->skyDropTargets[gBattlerTarget] == 0xFF)
CancelMultiTurnMoves(gBattlerTarget);
if (gBattleMons[gBattlerTarget].pp[i] == 0 && gBattleStruct->skyDropTargets[gBattlerTarget] == SKY_DROP_NO_TARGET)
CancelMultiTurnMoves(gBattlerTarget, SKY_DROP_IGNORE);
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_MoveEffectEerieSpell;
@ -6986,7 +6986,7 @@ static void Cmd_moveend(void)
case MOVEEND_SKY_DROP_CONFUSE: // If a Pokemon was released from Sky Drop and was in LOCK_CONFUSE, go to "confused due to fatigue" scripts and clear Sky Drop data.
for (i = 0; i < gBattlersCount; i++)
{
if (gBattleStruct->skyDropTargets[i] == 0xFE)
if (gBattleStruct->skyDropTargets[i] == SKY_DROP_RELEASED_TARGET)
{
u8 targetId;
// Find the battler id of the Pokemon that was held by Sky Drop
@ -7003,8 +7003,8 @@ static void Cmd_moveend(void)
gBattlescriptCurrInstr = BattleScript_ThrashConfuses;
// Clear skyDropTargets data
gBattleStruct->skyDropTargets[i] = 0xFF;
gBattleStruct->skyDropTargets[targetId] = 0xFF;
gBattleStruct->skyDropTargets[i] = SKY_DROP_NO_TARGET;
gBattleStruct->skyDropTargets[targetId] = SKY_DROP_NO_TARGET;
return;
}
}
@ -7544,7 +7544,7 @@ static void Cmd_moveend(void)
&& MoveHasAdditionalEffectSelf(gCurrentMove, MOVE_EFFECT_THRASH) // If we're rampaging
&& gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT // And it is unusable
&& (gBattleMons[gBattlerAttacker].status2 & STATUS2_LOCK_CONFUSE) != STATUS2_LOCK_CONFUSE_TURN(1)) // And won't end this turn
CancelMultiTurnMoves(gBattlerAttacker); // Cancel it
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_IGNORE); // Cancel it
if (gBattleStruct->savedAttackerCount > 0)
{
@ -10442,7 +10442,7 @@ static void Cmd_various(void)
VARIOUS_ARGS();
// Cancel all multiturn moves of IN_AIR Pokemon except those being targeted by Sky Drop.
if (gStatuses3[battler] & STATUS3_ON_AIR && !(gStatuses3[battler] & STATUS3_SKY_DROPPED))
CancelMultiTurnMoves(battler);
CancelMultiTurnMoves(battler, SKY_DROP_GRAVITY_ON_AIRBORNE);
gStatuses3[battler] &= ~(STATUS3_MAGNET_RISE | STATUS3_TELEKINESIS | STATUS3_ON_AIR | STATUS3_SKY_DROPPED);
break;
@ -10483,7 +10483,7 @@ static void Cmd_various(void)
{
VARIOUS_ARGS();
const u8 *result;
result = CancelMultiTurnMoves(battler);
result = CancelMultiTurnMoves(battler, SKY_DROP_CANCEL_MULTI_TURN_MOVES);
if (result)
{
gBattlescriptCurrInstr = result;
@ -11505,12 +11505,12 @@ static void Cmd_various(void)
VARIOUS_ARGS(const u8 *failInstr);
// Check to see if the initial target of this Sky Drop fainted before the 2nd turn of Sky Drop.
// If so, make the move fail. If not, clear all of the statuses and continue the move.
if (gBattleStruct->skyDropTargets[gBattlerAttacker] == 0xFF)
if (gBattleStruct->skyDropTargets[gBattlerAttacker] == SKY_DROP_NO_TARGET)
gBattlescriptCurrInstr = cmd->failInstr;
else
{
gBattleStruct->skyDropTargets[gBattlerAttacker] = 0xFF;
gBattleStruct->skyDropTargets[gBattlerTarget] = 0xFF;
gBattleStruct->skyDropTargets[gBattlerAttacker] = SKY_DROP_NO_TARGET;
gBattleStruct->skyDropTargets[gBattlerTarget] = SKY_DROP_NO_TARGET;
gStatuses3[gBattlerTarget] &= ~(STATUS3_SKY_DROPPED | STATUS3_ON_AIR);
gBattlescriptCurrInstr = cmd->nextInstr;
}
@ -11523,14 +11523,14 @@ static void Cmd_various(void)
case VARIOUS_SKY_DROP_YAWN: // If the mon that's sleeping due to Yawn was holding a Pokemon in Sky Drop, release the target and clear Sky Drop data.
{
VARIOUS_ARGS();
if (gBattleStruct->skyDropTargets[gEffectBattler] != 0xFF && !(gStatuses3[gEffectBattler] & STATUS3_SKY_DROPPED))
if (gBattleStruct->skyDropTargets[gEffectBattler] != SKY_DROP_NO_TARGET && !(gStatuses3[gEffectBattler] & STATUS3_SKY_DROPPED))
{
// Set the target of Sky Drop as gEffectBattler
gEffectBattler = gBattleStruct->skyDropTargets[gEffectBattler];
// Clear skyDropTargets data
gBattleStruct->skyDropTargets[gBattleStruct->skyDropTargets[gEffectBattler]] = 0xFF;
gBattleStruct->skyDropTargets[gEffectBattler] = 0xFF;
gBattleStruct->skyDropTargets[gBattleStruct->skyDropTargets[gEffectBattler]] = SKY_DROP_NO_TARGET;
gBattleStruct->skyDropTargets[gEffectBattler] = SKY_DROP_NO_TARGET;
// If the target was in the middle of Outrage/Thrash/etc. when targeted by Sky Drop, confuse them on release and do proper animation
if (gBattleMons[gEffectBattler].status2 & STATUS2_LOCK_CONFUSE && CanBeConfused(gEffectBattler))
@ -14046,8 +14046,8 @@ static void Cmd_tryspiteppreduce(void)
gBattlescriptCurrInstr = cmd->nextInstr;
// Don't cut off Sky Drop if pp is brought to zero.
if (gBattleMons[gBattlerTarget].pp[i] == 0 && gBattleStruct->skyDropTargets[gBattlerTarget] == 0xFF)
CancelMultiTurnMoves(gBattlerTarget);
if (gBattleMons[gBattlerTarget].pp[i] == 0 && gBattleStruct->skyDropTargets[gBattlerTarget] == SKY_DROP_NO_TARGET)
CancelMultiTurnMoves(gBattlerTarget, SKY_DROP_IGNORE);
}
else
{
@ -14237,7 +14237,7 @@ static void Cmd_handlerollout(void)
if (gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)
{
CancelMultiTurnMoves(gBattlerAttacker);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_IGNORE);
gBattlescriptCurrInstr = BattleScript_MoveMissedPause;
}
else

View File

@ -1024,7 +1024,68 @@ void MarkBattlerReceivedLinkData(u32 battler)
gBattleControllerExecFlags &= ~(1u << (28 + battler));
}
const u8* CancelMultiTurnMoves(u32 battler)
const u8 *CheckSkyDropState(u32 battler, enum SkyDropState skyDropState)
{
const u8 *result = NULL;
u8 otherSkyDropper = gBattleStruct->skyDropTargets[battler];
gStatuses3[otherSkyDropper] &= ~(STATUS3_SKY_DROPPED | STATUS3_ON_AIR);
// Makes both attacker and target's sprites visible
gSprites[gBattlerSpriteIds[battler]].invisible = FALSE;
gSprites[gBattlerSpriteIds[otherSkyDropper]].invisible = FALSE;
// If target was sky dropped in the middle of Outrage/Thrash/Petal Dance,
// confuse them upon release and display "confused by fatigue" message & animation.
// Don't do this if this CancelMultiTurnMoves is caused by falling asleep via Yawn.
if (gBattleMons[otherSkyDropper].status2 & STATUS2_LOCK_CONFUSE && skyDropState != SKY_DROP_STATUS_YAWN)
{
gBattleMons[otherSkyDropper].status2 &= ~(STATUS2_LOCK_CONFUSE);
// If the target can be confused, confuse them.
// Don't use CanBeConfused, can cause issues in edge cases.
if (!(GetBattlerAbility(otherSkyDropper) == ABILITY_OWN_TEMPO
|| gBattleMons[otherSkyDropper].status2 & STATUS2_CONFUSION
|| IsBattlerTerrainAffected(otherSkyDropper, STATUS_FIELD_MISTY_TERRAIN)))
{
// Set confused status
gBattleMons[otherSkyDropper].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2);
if (skyDropState == SKY_DROP_ATTACKCANCELLER_CHECK)
{
gBattleStruct->skyDropTargets[battler] = SKY_DROP_RELEASED_TARGET;
}
else if (skyDropState == SKY_DROP_GRAVITY_ON_AIRBORNE)
{
// Reapplying STATUS3_SKY_DROPPED allows for avoiding unecessary messages when Gravity is applied to the target.
gBattleStruct->skyDropTargets[battler] = SKY_DROP_RELEASED_TARGET;
gStatuses3[otherSkyDropper] |= STATUS3_SKY_DROPPED;
}
else if (skyDropState == SKY_DROP_CANCEL_MULTI_TURN_MOVES)
{
gBattlerAttacker = otherSkyDropper;
result = BattleScript_ThrashConfuses;
}
else if (skyDropState == SKY_DROP_STATUS_FREEZE_SLEEP)
{
gBattlerAttacker = otherSkyDropper;
BattleScriptPush(gBattlescriptCurrInstr + 1);
result = BattleScript_ThrashConfuses;
}
}
}
// Clear skyDropTargets data, unless this CancelMultiTurnMoves is caused by Yawn, attackcanceler, or VARIOUS_GRAVITY_ON_AIRBORNE_MONS
if (!(gBattleMons[otherSkyDropper].status2 & STATUS2_LOCK_CONFUSE) && gBattleStruct->skyDropTargets[battler] < 4)
{
gBattleStruct->skyDropTargets[battler] = SKY_DROP_NO_TARGET;
gBattleStruct->skyDropTargets[otherSkyDropper] = SKY_DROP_NO_TARGET;
}
return result;
}
const u8 *CancelMultiTurnMoves(u32 battler, enum SkyDropState skyDropState)
{
const u8 *result = NULL;
gBattleMons[battler].status2 &= ~(STATUS2_UPROAR);
@ -1045,70 +1106,8 @@ const u8* CancelMultiTurnMoves(u32 battler)
if (!(gStatuses3[battler] & STATUS3_SKY_DROPPED))
gStatuses3[battler] &= ~(STATUS3_SEMI_INVULNERABLE);
// Check to see if this Pokemon was in the middle of using Sky Drop. If so, release the target.
if (gBattleStruct->skyDropTargets[battler] != 0xFF && !(gStatuses3[battler] & STATUS3_SKY_DROPPED))
{
// Get the target's battler id
u8 otherSkyDropper = gBattleStruct->skyDropTargets[battler];
// Clears sky_dropped and on_air statuses
gStatuses3[otherSkyDropper] &= ~(STATUS3_SKY_DROPPED | STATUS3_ON_AIR);
// Makes both attacker and target's sprites visible
gSprites[gBattlerSpriteIds[battler]].invisible = FALSE;
gSprites[gBattlerSpriteIds[otherSkyDropper]].invisible = FALSE;
// If target was sky dropped in the middle of Outrage/Thrash/Petal Dance,
// confuse them upon release and display "confused by fatigue" message & animation.
// Don't do this if this CancelMultiTurnMoves is caused by falling asleep via Yawn.
if (gBattleMons[otherSkyDropper].status2 & STATUS2_LOCK_CONFUSE && gBattleStruct->eventBlockCounter != 24)
{
gBattleMons[otherSkyDropper].status2 &= ~(STATUS2_LOCK_CONFUSE);
// If the target can be confused, confuse them.
// Don't use CanBeConfused, can cause issues in edge cases.
if (!(GetBattlerAbility(otherSkyDropper) == ABILITY_OWN_TEMPO
|| gBattleMons[otherSkyDropper].status2 & STATUS2_CONFUSION
|| IsBattlerTerrainAffected(otherSkyDropper, STATUS_FIELD_MISTY_TERRAIN)))
{
// Set confused status
gBattleMons[otherSkyDropper].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2);
// If this CancelMultiTurnMoves is occuring due to attackcanceller
if (gBattlescriptCurrInstr[0] == 0x0)
{
gBattleStruct->skyDropTargets[battler] = 0xFE;
}
// If this CancelMultiTurnMoves is occuring due to VARIOUS_GRAVITY_ON_AIRBORNE_MONS
// Reapplying STATUS3_SKY_DROPPED allows for avoiding unecessary messages when Gravity is applied to the target.
else if (gBattlescriptCurrInstr[0] == 0x76 && gBattlescriptCurrInstr[2] == 76)
{
gBattleStruct->skyDropTargets[battler] = 0xFE;
gStatuses3[otherSkyDropper] |= STATUS3_SKY_DROPPED;
}
// If this CancelMultiTurnMoves is occuring due to cancelmultiturnmoves script
else if (gBattlescriptCurrInstr[0] == 0x76 && gBattlescriptCurrInstr[2] == 0)
{
gBattlerAttacker = otherSkyDropper;
result = BattleScript_ThrashConfuses;
}
// If this CancelMultiTurnMoves is occuring due to receiving Sleep/Freeze status
else if (gBattleScripting.moveEffect <= PRIMARY_STATUS_MOVE_EFFECT)
{
gBattlerAttacker = otherSkyDropper;
BattleScriptPush(gBattlescriptCurrInstr + 1);
result = BattleScript_ThrashConfuses;
}
}
}
// Clear skyDropTargets data, unless this CancelMultiTurnMoves is caused by Yawn, attackcanceler, or VARIOUS_GRAVITY_ON_AIRBORNE_MONS
if (!(gBattleMons[otherSkyDropper].status2 & STATUS2_LOCK_CONFUSE) && gBattleStruct->skyDropTargets[battler] < 4)
{
gBattleStruct->skyDropTargets[battler] = 0xFF;
gBattleStruct->skyDropTargets[otherSkyDropper] = 0xFF;
}
}
if (gBattleStruct->skyDropTargets[battler] != SKY_DROP_NO_TARGET && !(gStatuses3[battler] & STATUS3_SKY_DROPPED))
result = CheckSkyDropState(battler, skyDropState);
gDisableStructs[battler].rolloutTimer = 0;
gDisableStructs[battler].furyCutterCounter = 0;
@ -1303,7 +1302,7 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler)
if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(battler) != GIMMICK_Z_MOVE && move == gLastMoves[battler] && move != MOVE_STRUGGLE && (gBattleMons[battler].status2 & STATUS2_TORMENT))
{
CancelMultiTurnMoves(battler);
CancelMultiTurnMoves(battler, SKY_DROP_IGNORE);
if (gBattleTypeFlags & BATTLE_TYPE_PALACE)
{
gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingTormentedMoveInPalace;
@ -1672,7 +1671,7 @@ bool32 BattleArenaTurnEnd(void)
&& IsBattlerAlive(B_POSITION_PLAYER_LEFT) && IsBattlerAlive(B_POSITION_OPPONENT_LEFT))
{
for (u32 battler = 0; battler < 2; battler++)
CancelMultiTurnMoves(battler);
CancelMultiTurnMoves(battler, SKY_DROP_IGNORE);
gBattlescriptCurrInstr = BattleScript_ArenaDoJudgment;
BattleScriptExecute(BattleScript_ArenaDoJudgment);
@ -1875,7 +1874,7 @@ static void CancellerRecharge(u32 *effect)
{
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_RECHARGE;
gDisableStructs[gBattlerAttacker].rechargeTimer = 0;
CancelMultiTurnMoves(gBattlerAttacker);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedMustRecharge;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
*effect = 1;
@ -2011,7 +2010,7 @@ static void CancellerTruant(u32 *effect)
{
if (GetBattlerAbility(gBattlerAttacker) == ABILITY_TRUANT && gDisableStructs[gBattlerAttacker].truantCounter)
{
CancelMultiTurnMoves(gBattlerAttacker);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LOAFING;
gBattlerAbility = gBattlerAttacker;
@ -2026,7 +2025,7 @@ static void CancellerFlinch(u32 *effect)
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_FLINCHED)
{
gProtectStructs[gBattlerAttacker].flinchImmobility = TRUE;
CancelMultiTurnMoves(gBattlerAttacker);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedFlinched;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
*effect = 1;
@ -2047,7 +2046,7 @@ static void CancellerInLove(u32 *effect)
BattleScriptPush(BattleScript_MoveUsedIsInLoveCantAttack);
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
gProtectStructs[gBattlerAttacker].loveImmobility = TRUE;
CancelMultiTurnMoves(gBattlerAttacker);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
}
gBattlescriptCurrInstr = BattleScript_MoveUsedIsInLove;
*effect = 1;
@ -2060,7 +2059,7 @@ static void CancellerDisabled(u32 *effect)
{
gProtectStructs[gBattlerAttacker].usedDisabledMove = TRUE;
gBattleScripting.battler = gBattlerAttacker;
CancelMultiTurnMoves(gBattlerAttacker);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsDisabled;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
*effect = 1;
@ -2073,7 +2072,7 @@ static void CancellerHealBlocked(u32 *effect)
{
gProtectStructs[gBattlerAttacker].usedHealBlockedMove = TRUE;
gBattleScripting.battler = gBattlerAttacker;
CancelMultiTurnMoves(gBattlerAttacker);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedHealBlockPrevents;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
*effect = 1;
@ -2086,7 +2085,7 @@ static void CancellerGravity(u32 *effect)
{
gProtectStructs[gBattlerAttacker].usedGravityPreventedMove = TRUE;
gBattleScripting.battler = gBattlerAttacker;
CancelMultiTurnMoves(gBattlerAttacker);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedGravityPrevents;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
*effect = 1;
@ -2098,7 +2097,7 @@ static void CancellerThroatChop(u32 *effect)
if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].throatChopTimer > gBattleTurnCounter && IsSoundMove(gCurrentMove))
{
gProtectStructs[gBattlerAttacker].usedThroatChopPreventedMove = TRUE;
CancelMultiTurnMoves(gBattlerAttacker);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsThroatChopPrevented;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
*effect = 1;
@ -2110,7 +2109,7 @@ static void CancellerTaunted(u32 *effect)
if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].tauntTimer && IsBattleMoveStatus(gCurrentMove))
{
gProtectStructs[gBattlerAttacker].usedTauntedMove = TRUE;
CancelMultiTurnMoves(gBattlerAttacker);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsTaunted;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
*effect = 1;
@ -2122,7 +2121,7 @@ static void CancellerImprisoned(u32 *effect)
if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && GetImprisonedMovesCount(gBattlerAttacker, gCurrentMove))
{
gProtectStructs[gBattlerAttacker].usedImprisonedMove = TRUE;
CancelMultiTurnMoves(gBattlerAttacker);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsImprisoned;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
*effect = 1;
@ -2181,7 +2180,7 @@ static void CancellerParalysed(u32 *effect)
{
gProtectStructs[gBattlerAttacker].prlzImmobility = TRUE;
// This is removed in FRLG and Emerald for some reason
//CancelMultiTurnMoves(gBattlerAttacker);
//CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsParalyzed;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
*effect = 1;
@ -2327,7 +2326,7 @@ static void CancellerPsychicTerrain(u32 *effect)
&& GetMoveTarget(gCurrentMove) != MOVE_TARGET_OPPONENTS_FIELD
&& GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget))
{
CancelMultiTurnMoves(gBattlerAttacker);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedPsychicTerrainPrevents;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
*effect = 1;
@ -2972,7 +2971,7 @@ bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 a
&& !(IsBattleMoveStatus(move) && (abilityDef == ABILITY_MAGIC_BOUNCE || gProtectStructs[battlerDef].bounceMove)))
{
if (option == ABILITY_RUN_SCRIPT && !IsSpreadMove(GetBattlerMoveTargetType(battlerAtk, move)))
CancelMultiTurnMoves(battlerAtk); // Don't cancel moves that can hit two targets bc one target might not be protected
CancelMultiTurnMoves(battlerAtk, SKY_DROP_ATTACKCANCELLER_CHECK); // Don't cancel moves that can hit two targets bc one target might not be protected
battleScriptBlocksMove = BattleScript_DarkTypePreventsPrankster;
}

View File

@ -0,0 +1,124 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_SKY_DROP) == EFFECT_SKY_DROP);
}
SINGLE_BATTLE_TEST("Sky Drop does no damage to Flying type Pokémon")
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_PIDGEY].weight < 2000);
ASSUME(gSpeciesInfo[SPECIES_PIDGEY].types[1] == TYPE_FLYING);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PIDGEY);
} WHEN {
TURN { MOVE(player, MOVE_SKY_DROP); }
TURN { SKIP_TURN(player); }
} SCENE {
MESSAGE("Wobbuffet took the opposing Pidgey into the sky!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_DROP, player);
NOT HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Sky Drop fails if target is behind a substitute")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_SKY_DROP); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, opponent);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_DROP, player);
}
}
SINGLE_BATTLE_TEST("Sky Drop fails if target is in a Semi-Invulnerable state")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_FLY); MOVE(player, MOVE_SKY_DROP); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLY, opponent);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_DROP, player);
}
}
DOUBLE_BATTLE_TEST("Sky Drop is cancelled if Gravity activated")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(playerLeft, MOVE_SKY_DROP, target: opponentLeft);
MOVE(playerRight, MOVE_GRAVITY);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_DROP, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_GRAVITY, playerRight);
MESSAGE("Wobbuffet fell from the sky due to the gravity!");
}
}
SINGLE_BATTLE_TEST("Sky Drop fails on heavy targets")
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_METAGROSS].weight >= 2000);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_METAGROSS);
} WHEN {
TURN { MOVE(player, MOVE_SKY_DROP); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_DROP, player);
MESSAGE("The opposing Metagross is too heavy to be lifted!");
}
}
SINGLE_BATTLE_TEST("Sky Drop cancels targets two turn moves")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(opponent, MOVE_SOLAR_BEAM);
MOVE(player, MOVE_SKY_DROP);
}
TURN { SKIP_TURN(player); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SOLAR_BEAM, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_DROP, player);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SOLAR_BEAM, opponent);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Sky Drop stops the confusion count until the target is dropped")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_THRASH); }
TURN { SKIP_TURN(player); }
TURN { SKIP_TURN(player); }
TURN { MOVE(opponent, MOVE_SKY_DROP); }
TURN { SKIP_TURN(opponent); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_THRASH, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_THRASH, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_THRASH, player);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_DROP, opponent);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, player);
}
}