Refactor Sky Drop and rampage confusion (#9249)
Some checks failed
CI / build (push) Has been cancelled
CI / docs_validate (push) Has been cancelled
CI / allcontributors (push) Has been cancelled

This commit is contained in:
Alex 2026-03-10 17:12:28 +01:00 committed by GitHub
parent a338550479
commit c114dfbc84
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 337 additions and 373 deletions

View File

@ -1204,6 +1204,11 @@
.byte B_SCR_OP_TRY_SYNCHRONIZE .byte B_SCR_OP_TRY_SYNCHRONIZE
.endm .endm
.macro tryconfusionafterskydrop battler:req
.byte B_SCR_OP_TRY_CONFUSION_AFTER_SKY_DROP
.byte \battler
.endm
.macro callnative func:req .macro callnative func:req
.byte B_SCR_OP_CALLNATIVE .byte B_SCR_OP_CALLNATIVE
.4byte \func .4byte \func
@ -2082,10 +2087,6 @@
.4byte \failInstr .4byte \failInstr
.endm .endm
.macro skydropyawn
callnative BS_SkyDropYawn
.endm
.macro jumpifpranksterblocked jumpInstr:req .macro jumpifpranksterblocked jumpInstr:req
callnative BS_JumpIfPranksterBlocked callnative BS_JumpIfPranksterBlocked
.4byte \jumpInstr .4byte \jumpInstr

View File

@ -2118,6 +2118,8 @@ BattleScript_EffectGravitySuccess::
selectfirstvalidtarget selectfirstvalidtarget
BattleScript_GravityLoop: BattleScript_GravityLoop:
jumpfifsemiinvulnerable BS_TARGET, STATE_ON_AIR, BattleScript_GravityLoopDrop jumpfifsemiinvulnerable BS_TARGET, STATE_ON_AIR, BattleScript_GravityLoopDrop
jumpfifsemiinvulnerable BS_TARGET, STATE_SKY_DROP_ATTACKER, BattleScript_GravityLoopDrop
jumpfifsemiinvulnerable BS_TARGET, STATE_SKY_DROP_TARGET, BattleScript_GravityLoopDrop
jumpifvolatile BS_TARGET, VOLATILE_MAGNET_RISE, BattleScript_GravityLoopDrop jumpifvolatile BS_TARGET, VOLATILE_MAGNET_RISE, BattleScript_GravityLoopDrop
jumpifvolatile BS_TARGET, VOLATILE_TELEKINESIS, BattleScript_GravityLoopDrop jumpifvolatile BS_TARGET, VOLATILE_TELEKINESIS, BattleScript_GravityLoopDrop
goto BattleScript_GravityLoopEnd goto BattleScript_GravityLoopEnd
@ -2832,18 +2834,6 @@ BattleScript_SkyDropFlyingType::
printstring STRINGID_ITDOESNTAFFECT printstring STRINGID_ITDOESNTAFFECT
waitmessage B_WAIT_TIME_LONG waitmessage B_WAIT_TIME_LONG
makevisible BS_ATTACKER makevisible BS_ATTACKER
jumpifvolatile BS_TARGET, VOLATILE_CONFUSION, BattleScript_SkyDropFlyingAlreadyConfused
jumpifvolatile BS_TARGET, VOLATILE_RAMPAGE_TURNS, BattleScript_SkyDropFlyingConfuseLock
return
BattleScript_SkyDropFlyingConfuseLock:
seteffectprimary BS_ATTACKER, BS_TARGET, MOVE_EFFECT_CONFUSION
BattleScript_SkyDropFlyingAlreadyConfused:
clearvolatile BS_TARGET, VOLATILE_RAMPAGE_TURNS
jumpifvolatile BS_TARGET, VOLATILE_CONFUSION, BattleScript_SkyDropFlyingAlreadyConfusedRet
setbyte BS_ATTACKER, BS_TARGET
call BattleScript_ThrashConfusesRet
BattleScript_SkyDropFlyingAlreadyConfusedRet:
return return
BattleScript_SkyDropNoTarget:: BattleScript_SkyDropNoTarget::
@ -3978,6 +3968,7 @@ BattleScript_FaintBattler::
dofaintanimation BS_FAINTED dofaintanimation BS_FAINTED
copybyte sBATTLER, gBattlerFainted @ for message copybyte sBATTLER, gBattlerFainted @ for message
printstring STRINGID_BATTLERFAINTED printstring STRINGID_BATTLERFAINTED
tryconfusionafterskydrop BS_FAINTED
cleareffectsonfaint BS_FAINTED cleareffectsonfaint BS_FAINTED
trytoclearprimalweather trytoclearprimalweather
tryrevertweatherform tryrevertweatherform
@ -5705,7 +5696,7 @@ BattleScript_ThrashConfuses::
waitmessage B_WAIT_TIME_LONG waitmessage B_WAIT_TIME_LONG
end2 end2
BattleScript_ThrashConfusesRet:: BattleScript_ConfusionAfterRampage::
volatileanimation BS_SCRIPTING, VOLATILE_CONFUSION volatileanimation BS_SCRIPTING, VOLATILE_CONFUSION
printstring STRINGID_PKMNFATIGUECONFUSION printstring STRINGID_PKMNFATIGUECONFUSION
waitmessage B_WAIT_TIME_LONG waitmessage B_WAIT_TIME_LONG
@ -5837,9 +5828,8 @@ BattleScript_YawnMakesAsleepEnd2::
updatestatusicon BS_EFFECT_BATTLER updatestatusicon BS_EFFECT_BATTLER
waitstate waitstate
tryactivateitem BS_EFFECT_BATTLER, ACTIVATION_ON_STATUS_CHANGE tryactivateitem BS_EFFECT_BATTLER, ACTIVATION_ON_STATUS_CHANGE
jumpfifsemiinvulnerable BS_EFFECT_BATTLER, STATE_SKY_DROP, BattleScript_YawnEnd jumpfifsemiinvulnerable BS_EFFECT_BATTLER, STATE_SKY_DROP_TARGET, BattleScript_YawnEnd
makevisible BS_EFFECT_BATTLER makevisible BS_EFFECT_BATTLER
skydropyawn
BattleScript_YawnEnd: BattleScript_YawnEnd:
end2 end2

View File

@ -653,7 +653,6 @@ struct BattleStruct
u8 throwingPokeBall:1; u8 throwingPokeBall:1;
u8 ballSpriteIds[2]; // item gfx, window gfx u8 ballSpriteIds[2]; // item gfx, window gfx
u8 moveInfoSpriteId; // move info, window gfx u8 moveInfoSpriteId; // move info, window gfx
u8 skyDropTargets[MAX_BATTLERS_COUNT]; // For Sky Drop, to account for if multiple Pokemon use Sky Drop in a double battle.
// When using a move which hits multiple opponents which is then bounced by a target, we need to make sure, the move hits both opponents, the one with bounce, and the one without. // When using a move which hits multiple opponents which is then bounced by a target, we need to make sure, the move hits both opponents, the one with bounce, and the one without.
u16 beatUpSpecies[PARTY_SIZE]; // Species for Gen5+ Beat Up, otherwise party indexes u16 beatUpSpecies[PARTY_SIZE]; // Species for Gen5+ Beat Up, otherwise party indexes
u8 attackerBeforeBounce:2; u8 attackerBeforeBounce:2;

View File

@ -90,7 +90,7 @@ void AnimSetCenterToCornerVecX(struct Sprite *sprite);
void BeginBattleIntroDummy(void); void BeginBattleIntroDummy(void);
void BeginBattleIntro(void); void BeginBattleIntro(void);
void SwitchInClearSetData(enum BattlerId battler, struct Volatiles *volatilesCopy); void SwitchInClearSetData(enum BattlerId battler, struct Volatiles *volatilesCopy);
const u8 *FaintClearSetData(enum BattlerId battler); void FaintClearSetData(enum BattlerId battler);
void BattleTurnPassed(void); void BattleTurnPassed(void);
u8 IsRunningFromBattleImpossible(enum BattlerId battler); u8 IsRunningFromBattleImpossible(enum BattlerId battler);
void SwitchTwoBattlersInParty(enum BattlerId battler, enum BattlerId battler2); void SwitchTwoBattlersInParty(enum BattlerId battler, enum BattlerId battler2);

View File

@ -133,7 +133,7 @@ extern const u8 BattleScript_MoveUsedIsParalyzed[];
extern const u8 BattleScript_MoveUsedFlinched[]; extern const u8 BattleScript_MoveUsedFlinched[];
extern const u8 BattleScript_PrintUproarOverTurns[]; extern const u8 BattleScript_PrintUproarOverTurns[];
extern const u8 BattleScript_ThrashConfuses[]; extern const u8 BattleScript_ThrashConfuses[];
extern const u8 BattleScript_ThrashConfusesRet[]; extern const u8 BattleScript_ConfusionAfterRampage[];
extern const u8 BattleScript_MoveUsedIsConfused[]; extern const u8 BattleScript_MoveUsedIsConfused[];
extern const u8 BattleScript_MoveUsedIsConfusedNoMore[]; extern const u8 BattleScript_MoveUsedIsConfusedNoMore[];
extern const u8 BattleScript_PrintPayDayMoneyString[]; extern const u8 BattleScript_PrintPayDayMoneyString[];

View File

@ -152,19 +152,6 @@ enum SleepClauseBlock
BLOCKED_BY_SLEEP_CLAUSE, BLOCKED_BY_SLEEP_CLAUSE,
}; };
enum SkyDropState
{
SKY_DROP_IGNORE,
SKY_DROP_ATTACKCANCELER_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
enum EjectPackTiming enum EjectPackTiming
{ {
START_OF_TURN, START_OF_TURN,
@ -197,7 +184,7 @@ enum BattlerId GetBattlerForBattleScript(u8 caseId);
bool32 IsBattlerMarkedForControllerExec(enum BattlerId battler); bool32 IsBattlerMarkedForControllerExec(enum BattlerId battler);
void MarkBattlerForControllerExec(enum BattlerId battler); void MarkBattlerForControllerExec(enum BattlerId battler);
void MarkBattlerReceivedLinkData(enum BattlerId battler); void MarkBattlerReceivedLinkData(enum BattlerId battler);
const u8 *CancelMultiTurnMoves(enum BattlerId battler, enum SkyDropState skyDropState); void CancelMultiTurnMoves(enum BattlerId battler);
bool32 IsLastMonToMove(enum BattlerId battler); bool32 IsLastMonToMove(enum BattlerId battler);
bool32 ShouldDefiantCompetitiveActivate(enum BattlerId battler, enum Ability ability); bool32 ShouldDefiantCompetitiveActivate(enum BattlerId battler, enum Ability ability);
void PrepareStringBattle(enum StringID stringId, enum BattlerId battler); void PrepareStringBattle(enum StringID stringId, enum BattlerId battler);
@ -349,7 +336,7 @@ bool32 CanBeParalyzed(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum
bool32 CanBeFrozen(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef); bool32 CanBeFrozen(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef);
bool32 CanGetFrostbite(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef); bool32 CanGetFrostbite(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef);
bool32 CanSetNonVolatileStatus(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum MoveEffect secondaryMoveEffect, enum ResultOption option); bool32 CanSetNonVolatileStatus(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum MoveEffect secondaryMoveEffect, enum ResultOption option);
bool32 CanBeConfused(enum BattlerId battler); bool32 CanBeConfused(enum BattlerId battlerAtk, enum BattlerId effectBattler);
bool32 IsSafeguardProtected(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk); bool32 IsSafeguardProtected(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk);
u32 GetBattlerAffectionHearts(enum BattlerId battler); u32 GetBattlerAffectionHearts(enum BattlerId battler);
void TryToRevertMimicryAndFlags(void); void TryToRevertMimicryAndFlags(void);
@ -405,6 +392,7 @@ bool32 DoesMoveMissTarget(struct BattleCalcValues *cv);
bool32 IsSemiInvulnerable(enum BattlerId battler, enum SemiInvulnerableExclusion excludeCommander); bool32 IsSemiInvulnerable(enum BattlerId battler, enum SemiInvulnerableExclusion excludeCommander);
bool32 CanBreakThroughSemiInvulnerablity(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum Move move); bool32 CanBreakThroughSemiInvulnerablity(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum Move move);
bool32 BreaksThroughSemiInvulnerableState(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum Move move, enum SemiInvulnerableState state); bool32 BreaksThroughSemiInvulnerableState(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum Move move, enum SemiInvulnerableState state);
bool32 IsBattlerOnAir(enum BattlerId battler);
bool32 HasPartnerTrainer(enum BattlerId battler); bool32 HasPartnerTrainer(enum BattlerId battler);
bool32 IsAffectedByPowderMove(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect); bool32 IsAffectedByPowderMove(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect);
enum Move GetNaturePowerMove(void); enum Move GetNaturePowerMove(void);

View File

@ -106,7 +106,7 @@
#define B_SKILL_SWAP GEN_LATEST // In Gen4+, Skill Swap triggers switch-in abilities after use. #define B_SKILL_SWAP GEN_LATEST // In Gen4+, Skill Swap triggers switch-in abilities after use.
#define B_BRICK_BREAK GEN_LATEST // In Gen4+, you can destroy your own side's screens. In Gen 5+, screens are not removed if the target is immune. #define B_BRICK_BREAK GEN_LATEST // In Gen4+, you can destroy your own side's screens. In Gen 5+, screens are not removed if the target is immune.
#define B_WISH_HP_SOURCE GEN_LATEST // In Gen5+, Wish heals half of the user's max HP instead of the target's. #define B_WISH_HP_SOURCE GEN_LATEST // In Gen5+, Wish heals half of the user's max HP instead of the target's.
#define B_RAMPAGE_CANCELLING GEN_LATEST // In Gen5+, a failed Thrash, etc, will cancel except on its last turn. #define B_RAMPAGE_CONFUSION GEN_LATEST // In Gen5+, Rampage is canceled after the move (as opposed to End Turn) and a failed rampage move will cancel the counter unless it is the last turn
#define B_HEAL_BLOCKING GEN_LATEST // In Gen5+, Heal Block prevents healing by Black Sludge, Leftovers, Shell Bell. Affected Pokémon will not consume held HP-restoring Berries or Berry Juice. #define B_HEAL_BLOCKING GEN_LATEST // In Gen5+, Heal Block prevents healing by Black Sludge, Leftovers, Shell Bell. Affected Pokémon will not consume held HP-restoring Berries or Berry Juice.
// Draining abilities will not heal but will prevent damage. In Gen6+, Heal Block prevents the use of most HP-draining moves. // Draining abilities will not heal but will prevent damage. In Gen6+, Heal Block prevents the use of most HP-draining moves.
#define B_ROOTED_GROUNDING GEN_LATEST // In Gen4+, Ingrain causes the affected Pokémon to become grounded. #define B_ROOTED_GROUNDING GEN_LATEST // In Gen4+, Ingrain causes the affected Pokémon to become grounded.

View File

@ -183,6 +183,8 @@ enum VolatileFlags
F(VOLATILE_BIDE, bideTurns, (u32, 3)) \ F(VOLATILE_BIDE, bideTurns, (u32, 3)) \
F(VOLATILE_RAMPAGE_TURNS, rampageTurns, (u32, B_RAMPAGE_TURNS + 1)) \ F(VOLATILE_RAMPAGE_TURNS, rampageTurns, (u32, B_RAMPAGE_TURNS + 1)) \
F(VOLATILE_MULTIPLETURNS, multipleTurns, (u32, 1)) \ F(VOLATILE_MULTIPLETURNS, multipleTurns, (u32, 1)) \
F(VOLATILE_SKY_DROP_TARGET, skyDropTarget, (enum BattlerId, MAX_BATTLERS_COUNT)) \
F(VOLATILE_CONFUSE_AFTER_DROP, confuseAfterDrop, (u32, 1)) \
F(VOLATILE_WRAPPED, wrapped, (u32, 1)) \ F(VOLATILE_WRAPPED, wrapped, (u32, 1)) \
F(VOLATILE_WRAPPED_BY, wrappedBy, (enum BattlerId, MAX_BITS(MAX_BATTLERS_COUNT))) \ F(VOLATILE_WRAPPED_BY, wrappedBy, (enum BattlerId, MAX_BITS(MAX_BATTLERS_COUNT))) \
F(VOLATILE_WRAPPED_MOVE, wrappedMove, (u32, MOVES_COUNT_ALL - 1)) \ F(VOLATILE_WRAPPED_MOVE, wrappedMove, (u32, MOVES_COUNT_ALL - 1)) \
@ -318,7 +320,8 @@ enum SemiInvulnerableState
STATE_UNDERWATER, STATE_UNDERWATER,
STATE_ON_AIR, STATE_ON_AIR,
STATE_PHANTOM_FORCE, STATE_PHANTOM_FORCE,
STATE_SKY_DROP, STATE_SKY_DROP_ATTACKER,
STATE_SKY_DROP_TARGET,
STATE_COMMANDER, STATE_COMMANDER,
SEMI_INVULNERABLE_COUNT SEMI_INVULNERABLE_COUNT
}; };

View File

@ -96,7 +96,6 @@ enum MoveEndState
MOVEEND_SYMBIOSIS, MOVEEND_SYMBIOSIS,
MOVEEND_SUBSTITUTE, MOVEEND_SUBSTITUTE,
MOVEEND_FAINT_BLOCK, MOVEEND_FAINT_BLOCK,
MOVEEND_SKY_DROP_CONFUSE,
MOVEEND_UPDATE_LAST_MOVES, MOVEEND_UPDATE_LAST_MOVES,
MOVEEND_MIRROR_MOVE, MOVEEND_MIRROR_MOVE,
MOVEEND_NEXT_TARGET, // Everything up until here is handled for each strike of a spread move MOVEEND_NEXT_TARGET, // Everything up until here is handled for each strike of a spread move
@ -121,6 +120,8 @@ enum MoveEndState
MOVEEND_OPPORTUNIST, MOVEEND_OPPORTUNIST,
MOVEEND_MIRROR_HERB, MOVEEND_MIRROR_HERB,
MOVEEND_THIRD_MOVE_BLOCK, MOVEEND_THIRD_MOVE_BLOCK,
MOVEEND_RAMPAGE,
MOVEEND_CONFUSION_AFTER_SKY_DROP, // If target was previously rampaging, it should be confused when dropped
MOVEEND_EJECT_PACK, MOVEEND_EJECT_PACK,
MOVEEND_CLEAR_BITS, MOVEEND_CLEAR_BITS,
MOVEEND_DANCER, MOVEEND_DANCER,

View File

@ -226,6 +226,7 @@ enum BattleScriptOpcode
B_SCR_OP_SETNONVOLATILESTATUS, B_SCR_OP_SETNONVOLATILESTATUS,
B_SCR_OP_TRYOVERWRITEABILITY, B_SCR_OP_TRYOVERWRITEABILITY,
B_SCR_OP_TRY_SYNCHRONIZE, B_SCR_OP_TRY_SYNCHRONIZE,
B_SCR_OP_TRY_CONFUSION_AFTER_SKY_DROP,
// Expansion users, please don't use any of the unused commands. // Expansion users, please don't use any of the unused commands.
// They are reserved for expansion usage. // They are reserved for expansion usage.
@ -261,7 +262,6 @@ enum BattleScriptOpcode
B_SCR_OP_UNUSED_29, B_SCR_OP_UNUSED_29,
B_SCR_OP_UNUSED_30, B_SCR_OP_UNUSED_30,
B_SCR_OP_UNUSED_31, B_SCR_OP_UNUSED_31,
B_SCR_OP_UNUSED_32,
B_SCR_OP_CALLNATIVE, B_SCR_OP_CALLNATIVE,
}; };

View File

@ -98,7 +98,7 @@
F(B_SKILL_SWAP, skillSwap, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(B_SKILL_SWAP, skillSwap, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(B_BRICK_BREAK, brickBreak, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(B_BRICK_BREAK, brickBreak, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(B_WISH_HP_SOURCE, wishHpSource, (u32, GEN_COUNT - 1)) \ F(B_WISH_HP_SOURCE, wishHpSource, (u32, GEN_COUNT - 1)) \
F(B_RAMPAGE_CANCELLING, rampageCancelling, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(B_RAMPAGE_CONFUSION, rampageConfusion, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(B_HEAL_BLOCKING, healBlocking, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(B_HEAL_BLOCKING, healBlocking, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(B_ROOTED_GROUNDING, rootedGrounding, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(B_ROOTED_GROUNDING, rootedGrounding, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(B_METRONOME_MOVES, metronomeMoves, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(B_METRONOME_MOVES, metronomeMoves, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \

View File

@ -37,7 +37,7 @@ bool32 ShouldUseItem(enum BattlerId battler)
// If teaming up with player and Pokemon is on the right, or Pokemon is currently held by Sky Drop // If teaming up with player and Pokemon is on the right, or Pokemon is currently held by Sky Drop
if ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT) if ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT)
|| gBattleMons[battler].volatiles.semiInvulnerable == STATE_SKY_DROP) || gBattleMons[battler].volatiles.semiInvulnerable == STATE_SKY_DROP_TARGET)
return FALSE; return FALSE;
if (gBattleMons[battler].volatiles.embargo) if (gBattleMons[battler].volatiles.embargo)

View File

@ -427,7 +427,7 @@ bool32 IsBattlerTrapped(enum BattlerId battlerAtk, enum BattlerId battlerDef)
return TRUE; return TRUE;
if (gBattleMons[battlerDef].volatiles.escapePrevention) if (gBattleMons[battlerDef].volatiles.escapePrevention)
return TRUE; return TRUE;
if (gBattleMons[battlerDef].volatiles.semiInvulnerable == STATE_SKY_DROP) if (gBattleMons[battlerDef].volatiles.semiInvulnerable == STATE_SKY_DROP_TARGET)
return TRUE; return TRUE;
if (gBattleMons[battlerDef].volatiles.root) if (gBattleMons[battlerDef].volatiles.root)
return TRUE; return TRUE;
@ -3696,7 +3696,7 @@ bool32 AI_CanBeConfused(enum BattlerId battlerAtk, enum BattlerId battlerDef, en
if (gBattleMons[battlerDef].volatiles.confusionTurns > 0 if (gBattleMons[battlerDef].volatiles.confusionTurns > 0
|| (abilityDef == ABILITY_OWN_TEMPO && !DoesBattlerIgnoreAbilityChecks(battlerAtk, gAiLogicData->abilities[battlerAtk], move)) || (abilityDef == ABILITY_OWN_TEMPO && !DoesBattlerIgnoreAbilityChecks(battlerAtk, gAiLogicData->abilities[battlerAtk], move))
|| IsMistyTerrainAffected(battlerDef, abilityDef, gAiLogicData->holdEffects[battlerDef], gFieldStatuses) || IsMistyTerrainAffected(battlerDef, abilityDef, gAiLogicData->holdEffects[battlerDef], gFieldStatuses)
|| gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD || IsSafeguardProtected(battlerAtk, battlerDef, gAiLogicData->abilities[battlerAtk])
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)) || DoesSubstituteBlockMove(battlerAtk, battlerDef, move))
return FALSE; return FALSE;
return TRUE; return TRUE;

View File

@ -6583,24 +6583,6 @@ static void ReloadBattlerSprites(enum BattlerId battler, struct Pokemon *party)
} }
} }
static void TrySwapSkyDropTargets(enum BattlerId battlerAtk, enum BattlerId battlerPartner)
{
u32 temp;
// battlerAtk is using Ally Switch
// check if our partner is the target of sky drop
// If so, change that index to battlerAtk
for (enum BattlerId i = 0; i < gBattlersCount; i++) {
if (gBattleStruct->skyDropTargets[i] == battlerPartner) {
gBattleStruct->skyDropTargets[i] = battlerAtk;
break;
}
}
// Then swap our own sky drop targets with the partner in case our partner is mid-skydrop
SWAP(gBattleStruct->skyDropTargets[battlerAtk], gBattleStruct->skyDropTargets[battlerPartner], temp);
}
#define TRY_SIDE_TIMER_BATTLER_ID_SWAP(battlerAtk, battlerPartner, side, field) \ #define TRY_SIDE_TIMER_BATTLER_ID_SWAP(battlerAtk, battlerPartner, side, field) \
if (gSideTimers[side].field == battlerAtk) \ if (gSideTimers[side].field == battlerAtk) \
gSideTimers[side].field = battlerPartner; \ gSideTimers[side].field = battlerPartner; \
@ -6748,7 +6730,6 @@ static void AnimTask_AllySwitchDataSwap(u8 taskId)
SwitchTwoBattlersInParty(battlerAtk, battlerPartner); SwitchTwoBattlersInParty(battlerAtk, battlerPartner);
SWAP(gBattlerPartyIndexes[battlerAtk], gBattlerPartyIndexes[battlerPartner], temp); SWAP(gBattlerPartyIndexes[battlerAtk], gBattlerPartyIndexes[battlerPartner], temp);
TrySwapSkyDropTargets(battlerAtk, battlerPartner);
TrySwapStickyWebBattlerId(battlerAtk, battlerPartner); TrySwapStickyWebBattlerId(battlerAtk, battlerPartner);
TrySwapWishBattlerIds(battlerAtk, battlerPartner); TrySwapWishBattlerIds(battlerAtk, battlerPartner);
TrySwapAttractBattlerIds(battlerAtk, battlerPartner); TrySwapAttractBattlerIds(battlerAtk, battlerPartner);

View File

@ -329,12 +329,14 @@ static bool32 HandleEndTurnFirstEventBlock(enum BattlerId battler)
gBattleStruct->eventState.endTurnBlock++; gBattleStruct->eventState.endTurnBlock++;
break; break;
case FIRST_EVENT_BLOCK_THRASH: case FIRST_EVENT_BLOCK_THRASH:
if (gBattleMons[battler].volatiles.rampageTurns && gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP) if (B_RAMPAGE_CONFUSION < GEN_5
&& gBattleMons[battler].volatiles.rampageTurns
&& gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP_TARGET)
{ {
gBattleMons[battler].volatiles.rampageTurns--; gBattleMons[battler].volatiles.rampageTurns--;
if (gBattleMons[battler].volatiles.unableToUseMove) if (gBattleMons[battler].volatiles.unableToUseMove)
{ {
CancelMultiTurnMoves(battler, SKY_DROP_IGNORE); CancelMultiTurnMoves(battler);
} }
else if (!gBattleMons[battler].volatiles.rampageTurns && gBattleMons[battler].volatiles.multipleTurns) else if (!gBattleMons[battler].volatiles.rampageTurns && gBattleMons[battler].volatiles.multipleTurns)
{ {
@ -905,7 +907,7 @@ static bool32 HandleEndTurnYawn(enum BattlerId battler)
else else
gBattleMons[battler].status1 |= (RandomUniform(RNG_SLEEP_TURNS, 2, 8)); gBattleMons[battler].status1 |= (RandomUniform(RNG_SLEEP_TURNS, 2, 8));
CancelMultiTurnMoves(battler, SKY_DROP_STATUS_YAWN); CancelMultiTurnMoves(battler);
TryActivateSleepClause(battler, gBattlerPartyIndexes[battler]); TryActivateSleepClause(battler, gBattlerPartyIndexes[battler]);
BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1); BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1);
MarkBattlerForControllerExec(battler); MarkBattlerForControllerExec(battler);
@ -1241,7 +1243,7 @@ static bool32 HandleEndTurnThirdEventBlock(enum BattlerId battler)
gBattleMons[battler].volatiles.uproarTurns--; // uproar timer goes down gBattleMons[battler].volatiles.uproarTurns--; // uproar timer goes down
if (gBattleMons[battler].volatiles.unableToUseMove) if (gBattleMons[battler].volatiles.unableToUseMove)
{ {
CancelMultiTurnMoves(battler, SKY_DROP_IGNORE); CancelMultiTurnMoves(battler);
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_ENDS; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_ENDS;
} }
else if (gBattleMons[battler].volatiles.uproarTurns) else if (gBattleMons[battler].volatiles.uproarTurns)
@ -1252,7 +1254,7 @@ static bool32 HandleEndTurnThirdEventBlock(enum BattlerId battler)
else else
{ {
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_ENDS; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_ENDS;
CancelMultiTurnMoves(battler, SKY_DROP_IGNORE); CancelMultiTurnMoves(battler);
} }
BattleScriptExecute(BattleScript_PrintUproarOverTurns); BattleScriptExecute(BattleScript_PrintUproarOverTurns);
effect = TRUE; effect = TRUE;

View File

@ -3084,7 +3084,6 @@ static void BattleStartClearSetData(void)
gBattleStruct->lastTakenMoveFrom[i][2] = MOVE_NONE; gBattleStruct->lastTakenMoveFrom[i][2] = MOVE_NONE;
gBattleStruct->lastTakenMoveFrom[i][3] = MOVE_NONE; gBattleStruct->lastTakenMoveFrom[i][3] = MOVE_NONE;
gBattleStruct->AI_monToSwitchIntoId[i] = PARTY_SIZE; gBattleStruct->AI_monToSwitchIntoId[i] = PARTY_SIZE;
gBattleStruct->skyDropTargets[i] = SKY_DROP_NO_TARGET;
} }
gLastUsedMove = 0; gLastUsedMove = 0;
@ -3328,10 +3327,8 @@ void SwitchInClearSetData(enum BattlerId battler, struct Volatiles *volatilesCop
Ai_UpdateSwitchInData(battler); Ai_UpdateSwitchInData(battler);
} }
const u8* FaintClearSetData(enum BattlerId battler) void FaintClearSetData(enum BattlerId battler)
{ {
const u8 *result = NULL;
for (enum Stat i = 0; i < NUM_BATTLE_STATS; i++) for (enum Stat i = 0; i < NUM_BATTLE_STATS; i++)
gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE; gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE;
@ -3427,48 +3424,6 @@ const u8* FaintClearSetData(enum BattlerId battler)
Ai_UpdateFaintData(battler); Ai_UpdateFaintData(battler);
TryBattleFormChange(battler, FORM_CHANGE_FAINT, GetBattlerAbility(battler)); TryBattleFormChange(battler, FORM_CHANGE_FAINT, GetBattlerAbility(battler));
// If the fainted mon was involved in a Sky Drop
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] = 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 (gBattleMons[otherSkyDropper].volatiles.semiInvulnerable == STATE_SKY_DROP)
{
// Release the target and take them out of the semi-invulnerable state
gBattleMons[otherSkyDropper].volatiles.semiInvulnerable = STATE_NONE;
// Make the target's sprite visible
gSprites[gBattlerSpriteIds[otherSkyDropper]].invisible = FALSE;
// If the target was sky dropped in the middle of using Outrage/Petal Dance/Thrash,
// confuse them upon release and print "confused via fatigue" message and animation.
if (gBattleMons[otherSkyDropper].volatiles.rampageTurns)
{
gBattleMons[otherSkyDropper].volatiles.rampageTurns = 0;
// If the released mon can be confused, do so.
// Don't use CanBeConfused here, since it can cause issues in edge cases.
enum Ability ability = GetBattlerAbility(otherSkyDropper);
if (!(ability == ABILITY_OWN_TEMPO
|| gBattleMons[otherSkyDropper].volatiles.confusionTurns
|| IsMistyTerrainAffected(otherSkyDropper, ability, GetBattlerHoldEffect(otherSkyDropper), gFieldStatuses)))
{
gBattleMons[otherSkyDropper].volatiles.confusionTurns = ((Random()) % 4) + 2;
gBattlerAttacker = otherSkyDropper;
result = BattleScript_ThrashConfuses;
}
}
}
}
return result;
} }
static void DoBattleIntro(void) static void DoBattleIntro(void)
@ -3964,7 +3919,7 @@ static void HandleEndTurn_ContinueBattle(void)
{ {
gBattleMons[battler].volatiles.flinched = FALSE; gBattleMons[battler].volatiles.flinched = FALSE;
if ((gBattleMons[battler].status1 & STATUS1_SLEEP) && (gBattleMons[battler].volatiles.multipleTurns)) if ((gBattleMons[battler].status1 & STATUS1_SLEEP) && (gBattleMons[battler].volatiles.multipleTurns))
CancelMultiTurnMoves(battler, SKY_DROP_IGNORE); CancelMultiTurnMoves(battler);
} }
gBattleStruct->eventState.endTurnBlock = 0; gBattleStruct->eventState.endTurnBlock = 0;
gBattleStruct->eventState.endTurnBattler = 0; gBattleStruct->eventState.endTurnBattler = 0;
@ -4298,7 +4253,7 @@ static void HandleTurnActionSelectionState(void)
| BATTLE_TYPE_RECORDED_LINK)) | BATTLE_TYPE_RECORDED_LINK))
&& !gTestRunnerEnabled) && !gTestRunnerEnabled)
// Or if currently held by Sky Drop // Or if currently held by Sky Drop
|| gBattleMons[battler].volatiles.semiInvulnerable == STATE_SKY_DROP) || gBattleMons[battler].volatiles.semiInvulnerable == STATE_SKY_DROP_TARGET)
{ {
RecordedBattle_ClearBattlerAction(battler, 1); RecordedBattle_ClearBattlerAction(battler, 1);
gSelectionBattleScripts[battler] = BattleScript_ActionSelectionItemsCantBeUsed; gSelectionBattleScripts[battler] = BattleScript_ActionSelectionItemsCantBeUsed;

View File

@ -68,7 +68,7 @@ static enum CancelerResult CancelerStanceChangeOne(struct BattleContext *ctx)
static enum CancelerResult CancelerSkyDrop(struct BattleContext *ctx) static enum CancelerResult CancelerSkyDrop(struct BattleContext *ctx)
{ {
// If Pokemon is being held in Sky Drop // If Pokemon is being held in Sky Drop
if (gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable == STATE_SKY_DROP) if (gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable == STATE_SKY_DROP_TARGET)
{ {
gBattlescriptCurrInstr = BattleScript_MoveEnd; gBattlescriptCurrInstr = BattleScript_MoveEnd;
return CANCELER_RESULT_FAILURE; return CANCELER_RESULT_FAILURE;
@ -80,7 +80,7 @@ static enum CancelerResult CancelerRecharge(struct BattleContext *ctx)
{ {
if (gBattleMons[ctx->battlerAtk].volatiles.rechargeTimer > 0) if (gBattleMons[ctx->battlerAtk].volatiles.rechargeTimer > 0)
{ {
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_MoveUsedMustRecharge; gBattlescriptCurrInstr = BattleScript_MoveUsedMustRecharge;
return CANCELER_RESULT_FAILURE; return CANCELER_RESULT_FAILURE;
} }
@ -251,7 +251,7 @@ static enum CancelerResult CancelerTruant(struct BattleContext *ctx)
{ {
if (GetBattlerAbility(ctx->battlerAtk) == ABILITY_TRUANT && gBattleMons[ctx->battlerAtk].volatiles.truantCounter) if (GetBattlerAbility(ctx->battlerAtk) == ABILITY_TRUANT && gBattleMons[ctx->battlerAtk].volatiles.truantCounter)
{ {
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); CancelMultiTurnMoves(ctx->battlerAtk);
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LOAFING; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LOAFING;
gBattlerAbility = ctx->battlerAtk; gBattlerAbility = ctx->battlerAtk;
gBattlescriptCurrInstr = BattleScript_TruantLoafingAround; gBattlescriptCurrInstr = BattleScript_TruantLoafingAround;
@ -273,7 +273,7 @@ static enum CancelerResult CancelerFocus(struct BattleContext *ctx)
&& (focusPunchFailureConfig == GEN_5 || focusPunchFailureConfig == GEN_6 || GetMoveEffect(ctx->move) == EFFECT_FOCUS_PUNCH) && (focusPunchFailureConfig == GEN_5 || focusPunchFailureConfig == GEN_6 || GetMoveEffect(ctx->move) == EFFECT_FOCUS_PUNCH)
&& !gProtectStructs[ctx->battlerAtk].survivedOHKO) && !gProtectStructs[ctx->battlerAtk].survivedOHKO)
{ {
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_FocusPunchLostFocus; gBattlescriptCurrInstr = BattleScript_FocusPunchLostFocus;
return CANCELER_RESULT_FAILURE; return CANCELER_RESULT_FAILURE;
} }
@ -298,7 +298,7 @@ static enum CancelerResult CancelerFlinch(struct BattleContext *ctx)
{ {
if (gBattleMons[ctx->battlerAtk].volatiles.flinched) if (gBattleMons[ctx->battlerAtk].volatiles.flinched)
{ {
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_MoveUsedFlinched; gBattlescriptCurrInstr = BattleScript_MoveUsedFlinched;
return CANCELER_RESULT_FAILURE; return CANCELER_RESULT_FAILURE;
} }
@ -312,7 +312,7 @@ static enum CancelerResult CancelerDisabled(struct BattleContext *ctx)
&& gBattleMons[ctx->battlerAtk].volatiles.disabledMove != MOVE_NONE) && gBattleMons[ctx->battlerAtk].volatiles.disabledMove != MOVE_NONE)
{ {
gBattleScripting.battler = ctx->battlerAtk; gBattleScripting.battler = ctx->battlerAtk;
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsDisabled; gBattlescriptCurrInstr = BattleScript_MoveUsedIsDisabled;
return CANCELER_RESULT_FAILURE; return CANCELER_RESULT_FAILURE;
} }
@ -326,20 +326,20 @@ static enum CancelerResult CancelerVolatileBlocked(struct BattleContext *ctx)
&& IsHealBlockPreventingMove(ctx->battlerAtk, ctx->move)) && IsHealBlockPreventingMove(ctx->battlerAtk, ctx->move))
{ {
gBattleScripting.battler = ctx->battlerAtk; gBattleScripting.battler = ctx->battlerAtk;
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_MoveUsedHealBlockPrevents; gBattlescriptCurrInstr = BattleScript_MoveUsedHealBlockPrevents;
return CANCELER_RESULT_FAILURE; return CANCELER_RESULT_FAILURE;
} }
else if (gFieldStatuses & STATUS_FIELD_GRAVITY && IsGravityPreventingMove(ctx->move)) else if (gFieldStatuses & STATUS_FIELD_GRAVITY && IsGravityPreventingMove(ctx->move))
{ {
gBattleScripting.battler = ctx->battlerAtk; gBattleScripting.battler = ctx->battlerAtk;
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_MoveUsedGravityPrevents; gBattlescriptCurrInstr = BattleScript_MoveUsedGravityPrevents;
return CANCELER_RESULT_FAILURE; return CANCELER_RESULT_FAILURE;
} }
else if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && gBattleMons[ctx->battlerAtk].volatiles.throatChopTimer > 0 && IsSoundMove(ctx->move)) else if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && gBattleMons[ctx->battlerAtk].volatiles.throatChopTimer > 0 && IsSoundMove(ctx->move))
{ {
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsThroatChopPrevented; gBattlescriptCurrInstr = BattleScript_MoveUsedIsThroatChopPrevented;
return CANCELER_RESULT_FAILURE; return CANCELER_RESULT_FAILURE;
} }
@ -353,7 +353,7 @@ static enum CancelerResult CancelerTaunted(struct BattleContext *ctx)
&& IsBattleMoveStatus(ctx->move) && IsBattleMoveStatus(ctx->move)
&& (GetConfig(B_TAUNT_ME_FIRST) < GEN_5 || GetMoveEffect(ctx->move) != EFFECT_ME_FIRST)) && (GetConfig(B_TAUNT_ME_FIRST) < GEN_5 || GetMoveEffect(ctx->move) != EFFECT_ME_FIRST))
{ {
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsTaunted; gBattlescriptCurrInstr = BattleScript_MoveUsedIsTaunted;
return CANCELER_RESULT_FAILURE; return CANCELER_RESULT_FAILURE;
} }
@ -364,7 +364,7 @@ static enum CancelerResult CancelerImprisoned(struct BattleContext *ctx)
{ {
if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && GetImprisonedMovesCount(ctx->battlerAtk, ctx->move)) if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && GetImprisonedMovesCount(ctx->battlerAtk, ctx->move))
{ {
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsImprisoned; gBattlescriptCurrInstr = BattleScript_MoveUsedIsImprisoned;
return CANCELER_RESULT_FAILURE; return CANCELER_RESULT_FAILURE;
} }
@ -433,7 +433,7 @@ static enum CancelerResult CancelerParalyzed(struct BattleContext *ctx)
&& !(B_MAGIC_GUARD == GEN_4 && IsAbilityAndRecord(ctx->battlerAtk, ctx->abilityAtk, ABILITY_MAGIC_GUARD)) && !(B_MAGIC_GUARD == GEN_4 && IsAbilityAndRecord(ctx->battlerAtk, ctx->abilityAtk, ABILITY_MAGIC_GUARD))
&& !RandomPercentage(RNG_PARALYSIS, 75)) && !RandomPercentage(RNG_PARALYSIS, 75))
{ {
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK); CancelMultiTurnMoves(gBattlerAttacker);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsParalyzed; gBattlescriptCurrInstr = BattleScript_MoveUsedIsParalyzed;
return CANCELER_RESULT_FAILURE; return CANCELER_RESULT_FAILURE;
} }
@ -453,7 +453,7 @@ static enum CancelerResult CancelerInfatuation(struct BattleContext *ctx)
else else
{ {
BattleScriptPush(BattleScript_MoveUsedIsInLoveCantAttack); BattleScriptPush(BattleScript_MoveUsedIsInLoveCantAttack);
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsInLove; gBattlescriptCurrInstr = BattleScript_MoveUsedIsInLove;
return CANCELER_RESULT_FAILURE; return CANCELER_RESULT_FAILURE;
} }
@ -1015,7 +1015,7 @@ static enum CancelerResult CancelerSkyBattle(struct BattleContext *ctx)
{ {
if (gBattleStruct->isSkyBattle && IsMoveSkyBattleBanned(gCurrentMove)) if (gBattleStruct->isSkyBattle && IsMoveSkyBattleBanned(gCurrentMove))
{ {
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_ButItFailed; gBattlescriptCurrInstr = BattleScript_ButItFailed;
return CANCELER_RESULT_FAILURE; return CANCELER_RESULT_FAILURE;
} }
@ -1042,7 +1042,7 @@ static enum CancelerResult CancelerWeatherPrimal(struct BattleContext *ctx)
if (result == CANCELER_RESULT_FAILURE) if (result == CANCELER_RESULT_FAILURE)
{ {
gProtectStructs[ctx->battlerAtk].chargingTurn = FALSE; gProtectStructs[ctx->battlerAtk].chargingTurn = FALSE;
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove; gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove;
} }
} }
@ -1492,17 +1492,16 @@ static enum CancelerResult HandleSkyDropResult(struct BattleContext *ctx)
gBattleScripting.animTargetsHit = 0; gBattleScripting.animTargetsHit = 0;
gBattleMons[ctx->battlerAtk].volatiles.multipleTurns = FALSE; gBattleMons[ctx->battlerAtk].volatiles.multipleTurns = FALSE;
gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable = STATE_NONE; gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable = STATE_NONE;
gBattleMons[ctx->battlerDef].volatiles.semiInvulnerable = STATE_NONE; gBattleMons[ctx->battlerAtk].volatiles.skyDropTarget = 0;
gBattleStruct->skyDropTargets[gBattlerTarget] = SKY_DROP_NO_TARGET;
// Sky Drop fails if target already fainted // Sky Drop fails if target already left the field
if (gBattleStruct->skyDropTargets[ctx->battlerAtk] == SKY_DROP_NO_TARGET) if (gBattleMons[ctx->battlerDef].volatiles.semiInvulnerable == STATE_NONE)
{ {
gBattlescriptCurrInstr = BattleScript_SkyDropNoTarget; gBattlescriptCurrInstr = BattleScript_SkyDropNoTarget;
return CANCELER_RESULT_FAILURE; return CANCELER_RESULT_FAILURE;
} }
gBattleStruct->skyDropTargets[ctx->battlerAtk] = SKY_DROP_NO_TARGET; gBattleMons[ctx->battlerDef].volatiles.semiInvulnerable = STATE_NONE;
return CANCELER_RESULT_SUCCESS; return CANCELER_RESULT_SUCCESS;
} }
@ -1528,22 +1527,19 @@ static enum CancelerResult HandleSkyDropResult(struct BattleContext *ctx)
} }
// First turn // First turn
gBattleMons[ctx->battlerDef].volatiles.multipleTurns = 0;
gBattleMons[ctx->battlerDef].volatiles.uproarTurns = 0;
gBattleMons[ctx->battlerDef].volatiles.bideTurns = 0;
gBattleMons[ctx->battlerDef].volatiles.rolloutTimer = 0;
gBattleMons[ctx->battlerDef].volatiles.furyCutterCounter = 0;
if (gSideTimers[GetBattlerSide(ctx->battlerDef)].followmeTimer != 0 && gSideTimers[GetBattlerSide(ctx->battlerDef)].followmeTarget == ctx->battlerDef) if (gBattleMons[ctx->battlerDef].volatiles.rampageTurns > 0)
gSideTimers[GetBattlerSide(ctx->battlerDef)].followmeTimer = 0; gBattleMons[ctx->battlerDef].volatiles.confuseAfterDrop = TRUE;
gBattleStruct->skyDropTargets[ctx->battlerAtk] = ctx->battlerDef; CancelMultiTurnMoves(ctx->battlerDef);
gBattleStruct->skyDropTargets[ctx->battlerDef] = ctx->battlerAtk;
gLockedMoves[ctx->battlerAtk] = ctx->move; gLockedMoves[ctx->battlerAtk] = ctx->move;
gProtectStructs[ctx->battlerAtk].chargingTurn = TRUE; gProtectStructs[ctx->battlerAtk].chargingTurn = TRUE;
gBattleMons[ctx->battlerAtk].volatiles.multipleTurns = TRUE; gBattleMons[ctx->battlerAtk].volatiles.multipleTurns = TRUE;
gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable = STATE_ON_AIR; gBattleMons[ctx->battlerAtk].volatiles.skyDropTarget = ctx->battlerDef + 1;
gBattleMons[ctx->battlerDef].volatiles.semiInvulnerable = STATE_SKY_DROP; gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable = STATE_SKY_DROP_ATTACKER;
gBattleMons[ctx->battlerDef].volatiles.semiInvulnerable = STATE_SKY_DROP_TARGET;
if (gSideTimers[GetBattlerSide(ctx->battlerDef)].followmeTimer != 0 && gSideTimers[GetBattlerSide(ctx->battlerDef)].followmeTarget == ctx->battlerDef)
gSideTimers[GetBattlerSide(ctx->battlerDef)].followmeTimer = 0;
gBattlescriptCurrInstr = BattleScript_SkyDropCharging; gBattlescriptCurrInstr = BattleScript_SkyDropCharging;
return CANCELER_RESULT_BREAK; return CANCELER_RESULT_BREAK;
@ -1803,7 +1799,7 @@ static enum CancelerResult CancelerTargetFailure(struct BattleContext *ctx)
gLastHitByType[gBattlerTarget] = 0; gLastHitByType[gBattlerTarget] = 0;
gBattleScripting.battler = ctx->battlerDef; gBattleScripting.battler = ctx->battlerDef;
gBattleStruct->pledgeMove = FALSE; gBattleStruct->pledgeMove = FALSE;
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); CancelMultiTurnMoves(ctx->battlerAtk);
return CANCELER_RESULT_PAUSE; return CANCELER_RESULT_PAUSE;
} }
} }
@ -2343,7 +2339,7 @@ static enum MoveEndResult MoveEndAttackerVisible(void)
if (IsBattlerUnaffectedByMove(gBattlerTarget) if (IsBattlerUnaffectedByMove(gBattlerTarget)
|| !IsSemiInvulnerable(gBattlerAttacker, CHECK_ALL) || !IsSemiInvulnerable(gBattlerAttacker, CHECK_ALL)
|| (gBattleStruct->unableToUseMove && gBattleMons[gBattlerAttacker].volatiles.semiInvulnerable != STATE_SKY_DROP)) || (gBattleStruct->unableToUseMove && gBattleMons[gBattlerAttacker].volatiles.semiInvulnerable != STATE_SKY_DROP_TARGET))
{ {
BtlController_EmitSpriteInvisibility(gBattlerAttacker, B_COMM_TO_CONTROLLER, FALSE); BtlController_EmitSpriteInvisibility(gBattlerAttacker, B_COMM_TO_CONTROLLER, FALSE);
MarkBattlerForControllerExec(gBattlerAttacker); MarkBattlerForControllerExec(gBattlerAttacker);
@ -2574,37 +2570,6 @@ static enum MoveEndResult MoveEndFaintBlock(void)
return result; return result;
} }
static enum MoveEndResult MoveEndSkyDropConfuse(void)
{
enum MoveEndResult result = MOVEEND_RESULT_CONTINUE;
for (enum BattlerId battler = 0; battler < gBattlersCount; battler++)
{
if (gBattleStruct->skyDropTargets[battler] == SKY_DROP_RELEASED_TARGET)
{
// Find the battler id of the Pokemon that was held by Sky Drop
enum BattlerId targetId;
for (targetId = 0; targetId < gBattlersCount; targetId++)
{
if (gBattleStruct->skyDropTargets[targetId] == battler)
break;
}
gBattleScripting.battler = targetId;
BattleScriptCall(BattleScript_ThrashConfusesRet); // Jump to "confused due to fatigue" script
// Clear skyDropTargets data
gBattleStruct->skyDropTargets[battler] = SKY_DROP_NO_TARGET;
gBattleStruct->skyDropTargets[targetId] = SKY_DROP_NO_TARGET;
result = MOVEEND_RESULT_RUN_SCRIPT;
break;
}
}
gBattleScripting.moveendState++;
return result;
}
static enum MoveEndResult MoveEndUpdateLastMoves(void) static enum MoveEndResult MoveEndUpdateLastMoves(void)
{ {
if (!IsOnPlayerSide(gBattlerAttacker)) if (!IsOnPlayerSide(gBattlerAttacker))
@ -3654,6 +3619,58 @@ static bool32 ShouldSetStompingTantrumTimer(void)
return numNotAffectedTargets == gBattleStruct->numSpreadTargets; return numNotAffectedTargets == gBattleStruct->numSpreadTargets;
} }
static enum MoveEndResult MoveEndRampage(void)
{
enum MoveEndResult result = MOVEEND_RESULT_CONTINUE;
if (gBattleMons[gBattlerAttacker].volatiles.rampageTurns == 0
|| gSpecialStatuses[gBattlerAttacker].dancerUsedMove
|| B_RAMPAGE_CONFUSION < GEN_5)
{
result = MOVEEND_RESULT_CONTINUE;
}
else if (--gBattleMons[gBattlerAttacker].volatiles.rampageTurns == 0)
{
CancelMultiTurnMoves(gBattlerAttacker);
if (CanBeConfused(gBattlerAttacker, gBattlerAttacker))
{
gBattleScripting.battler = gBattlerAttacker;
gBattleMons[gBattlerAttacker].volatiles.confusionTurns = RandomUniform(RNG_CONFUSION_TURNS, 2, B_CONFUSION_TURNS); // 2-5 turns
BattleScriptCall(BattleScript_ConfusionAfterRampage);
result = MOVEEND_RESULT_BREAK;
}
}
else if (IsBattlerUnaffectedByMove(gBattlerTarget))
{
CancelMultiTurnMoves(gBattlerAttacker);
result = MOVEEND_RESULT_CONTINUE;
}
gBattleScripting.moveendState++;
return result;
}
static enum MoveEndResult MoveEndConfusionAfterSkyDrop(void)
{
enum MoveEndResult result = MOVEEND_RESULT_CONTINUE;
if (gBattleMons[gBattlerTarget].volatiles.confuseAfterDrop
&& gBattleMons[gBattlerTarget].volatiles.semiInvulnerable != STATE_SKY_DROP_TARGET)
{
gBattleMons[gBattlerTarget].volatiles.confuseAfterDrop = FALSE;
if (CanBeConfused(gBattlerTarget, gBattlerTarget))
{
gBattleScripting.battler = gBattlerTarget;
gBattleMons[gBattlerTarget].volatiles.confusionTurns = RandomUniform(RNG_CONFUSION_TURNS, 2, B_CONFUSION_TURNS); // 2-5 turns
BattleScriptCall(BattleScript_ConfusionAfterRampage);
result = MOVEEND_RESULT_BREAK;
}
}
gBattleScripting.moveendState++;
return result;
}
static enum MoveEndResult MoveEndClearBits(void) static enum MoveEndResult MoveEndClearBits(void)
{ {
ValidateBattlers(); ValidateBattlers();
@ -3672,12 +3689,6 @@ static enum MoveEndResult MoveEndClearBits(void)
if (originallyUsedMove != MOVE_NONE) if (originallyUsedMove != MOVE_NONE)
TryUpdateEvolutionTracker(IF_USED_MOVE_X_TIMES, 1, originallyUsedMove); TryUpdateEvolutionTracker(IF_USED_MOVE_X_TIMES, 1, originallyUsedMove);
if (B_RAMPAGE_CANCELLING >= GEN_5
&& MoveHasAdditionalEffectSelf(gCurrentMove, MOVE_EFFECT_THRASH) // If we're rampaging
&& IsBattlerUnaffectedByMove(gBattlerTarget) // And it is unusable
&& gBattleMons[gBattlerAttacker].volatiles.rampageTurns != 1) // And won't end this turn
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_IGNORE); // Cancel it
SetSameMoveTurnValues(moveEffect); SetSameMoveTurnValues(moveEffect);
TryClearChargeVolatile(moveType); TryClearChargeVolatile(moveType);
gProtectStructs[gBattlerAttacker].shellTrap = FALSE; gProtectStructs[gBattlerAttacker].shellTrap = FALSE;
@ -3699,6 +3710,7 @@ static enum MoveEndResult MoveEndClearBits(void)
SetActiveGimmick(gBattlerAttacker, GIMMICK_NONE); SetActiveGimmick(gBattlerAttacker, GIMMICK_NONE);
if (gBattleMons[gBattlerAttacker].volatiles.destinyBond > 0) if (gBattleMons[gBattlerAttacker].volatiles.destinyBond > 0)
gBattleMons[gBattlerAttacker].volatiles.destinyBond--; gBattleMons[gBattlerAttacker].volatiles.destinyBond--;
// check if Stellar type boost should be used up // check if Stellar type boost should be used up
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA
&& GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR && GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR
@ -3789,7 +3801,6 @@ static enum MoveEndResult (*const sMoveEndHandlers[])(void) =
[MOVEEND_SYMBIOSIS] = MoveEndSymbiosis, [MOVEEND_SYMBIOSIS] = MoveEndSymbiosis,
[MOVEEND_SUBSTITUTE] = MoveEndSubstitute, [MOVEEND_SUBSTITUTE] = MoveEndSubstitute,
[MOVEEND_FAINT_BLOCK] = MoveEndFaintBlock, [MOVEEND_FAINT_BLOCK] = MoveEndFaintBlock,
[MOVEEND_SKY_DROP_CONFUSE] = MoveEndSkyDropConfuse,
[MOVEEND_UPDATE_LAST_MOVES] = MoveEndUpdateLastMoves, [MOVEEND_UPDATE_LAST_MOVES] = MoveEndUpdateLastMoves,
[MOVEEND_MIRROR_MOVE] = MoveEndMirrorMove, [MOVEEND_MIRROR_MOVE] = MoveEndMirrorMove,
[MOVEEND_NEXT_TARGET] = MoveEndNextTarget, [MOVEEND_NEXT_TARGET] = MoveEndNextTarget,
@ -3807,14 +3818,16 @@ static enum MoveEndResult (*const sMoveEndHandlers[])(void) =
[MOVEEND_LIFE_ORB_SHELL_BELL] = MoveEndLifeOrbShellBell, [MOVEEND_LIFE_ORB_SHELL_BELL] = MoveEndLifeOrbShellBell,
[MOVEEND_FORM_CHANGE] = MoveEndFormChange, [MOVEEND_FORM_CHANGE] = MoveEndFormChange,
[MOVEEND_EMERGENCY_EXIT] = MoveEndEmergencyExit, [MOVEEND_EMERGENCY_EXIT] = MoveEndEmergencyExit,
[MOVEEND_EJECT_PACK] = MoveEndEjectPack,
[MOVEEND_HIT_ESCAPE] = MoveEndHitEscape, [MOVEEND_HIT_ESCAPE] = MoveEndHitEscape,
[MOVEEND_PICKPOCKET] = MoveEndPickpocket,
[MOVEEND_ITEMS_EFFECTS_ALL] = MoveEndItemsEffectsAll, [MOVEEND_ITEMS_EFFECTS_ALL] = MoveEndItemsEffectsAll,
[MOVEEND_WHITE_HERB] = MoveEndWhiteHerb, [MOVEEND_WHITE_HERB] = MoveEndWhiteHerb,
[MOVEEND_OPPORTUNIST] = MoveEndOpportunist, [MOVEEND_OPPORTUNIST] = MoveEndOpportunist,
[MOVEEND_MIRROR_HERB] = MoveEndMirrorHerb, [MOVEEND_MIRROR_HERB] = MoveEndMirrorHerb,
[MOVEEND_THIRD_MOVE_BLOCK] = MoveEndThirdMoveBlock, [MOVEEND_THIRD_MOVE_BLOCK] = MoveEndThirdMoveBlock,
[MOVEEND_PICKPOCKET] = MoveEndPickpocket, [MOVEEND_RAMPAGE] = MoveEndRampage,
[MOVEEND_CONFUSION_AFTER_SKY_DROP] = MoveEndConfusionAfterSkyDrop,
[MOVEEND_EJECT_PACK] = MoveEndEjectPack,
[MOVEEND_CLEAR_BITS] = MoveEndClearBits, [MOVEEND_CLEAR_BITS] = MoveEndClearBits,
[MOVEEND_DANCER] = MoveEndDancer, [MOVEEND_DANCER] = MoveEndDancer,
[MOVEEND_PURSUIT_NEXT_ACTION] = MoveEndPursuitNextAction, [MOVEEND_PURSUIT_NEXT_ACTION] = MoveEndPursuitNextAction,

View File

@ -576,6 +576,7 @@ static void Cmd_jumpifcaptivateaffected(void);
static void Cmd_setnonvolatilestatus(void); static void Cmd_setnonvolatilestatus(void);
static void Cmd_tryoverwriteability(void); static void Cmd_tryoverwriteability(void);
static void Cmd_trysynchronize(void); static void Cmd_trysynchronize(void);
static void Cmd_tryconfusionafterskydrop(void);
static void Cmd_dummy(void); static void Cmd_dummy(void);
static void Cmd_callnative(void); static void Cmd_callnative(void);
@ -804,6 +805,7 @@ void (*const gBattleScriptingCommandsTable[])(void) =
[B_SCR_OP_SETNONVOLATILESTATUS] = Cmd_setnonvolatilestatus, [B_SCR_OP_SETNONVOLATILESTATUS] = Cmd_setnonvolatilestatus,
[B_SCR_OP_TRYOVERWRITEABILITY] = Cmd_tryoverwriteability, [B_SCR_OP_TRYOVERWRITEABILITY] = Cmd_tryoverwriteability,
[B_SCR_OP_TRY_SYNCHRONIZE] = Cmd_trysynchronize, [B_SCR_OP_TRY_SYNCHRONIZE] = Cmd_trysynchronize,
[B_SCR_OP_TRY_CONFUSION_AFTER_SKY_DROP] = Cmd_tryconfusionafterskydrop,
[B_SCR_OP_UNUSED_1] = Cmd_dummy, [B_SCR_OP_UNUSED_1] = Cmd_dummy,
[B_SCR_OP_UNUSED_2] = Cmd_dummy, [B_SCR_OP_UNUSED_2] = Cmd_dummy,
[B_SCR_OP_UNUSED_3] = Cmd_dummy, [B_SCR_OP_UNUSED_3] = Cmd_dummy,
@ -835,7 +837,6 @@ void (*const gBattleScriptingCommandsTable[])(void) =
[B_SCR_OP_UNUSED_29] = Cmd_dummy, [B_SCR_OP_UNUSED_29] = Cmd_dummy,
[B_SCR_OP_UNUSED_30] = Cmd_dummy, [B_SCR_OP_UNUSED_30] = Cmd_dummy,
[B_SCR_OP_UNUSED_31] = Cmd_dummy, [B_SCR_OP_UNUSED_31] = Cmd_dummy,
[B_SCR_OP_UNUSED_32] = Cmd_dummy,
[B_SCR_OP_CALLNATIVE] = Cmd_callnative, [B_SCR_OP_CALLNATIVE] = Cmd_callnative,
}; };
@ -2321,14 +2322,8 @@ static void SetNonVolatileStatus(enum BattlerId battlerAtk, enum BattlerId effec
{ {
gEffectBattler = effectBattler; gEffectBattler = effectBattler;
if (effect == MOVE_EFFECT_SLEEP if (effect == MOVE_EFFECT_SLEEP || effect == MOVE_EFFECT_FREEZE)
|| effect == MOVE_EFFECT_FREEZE) CancelMultiTurnMoves(effectBattler);
{
const u8 *cancelMultiTurnMovesResult = NULL;
cancelMultiTurnMovesResult = CancelMultiTurnMoves(effectBattler, SKY_DROP_STATUS_FREEZE_SLEEP);
if (cancelMultiTurnMovesResult)
gBattlescriptCurrInstr = cancelMultiTurnMovesResult;
}
BattleScriptPush(battleScript); BattleScriptPush(battleScript);
@ -2518,29 +2513,15 @@ void SetMoveEffect(enum BattlerId battlerAtk, enum BattlerId effectBattler, enum
} }
break; break;
case MOVE_EFFECT_CONFUSION: case MOVE_EFFECT_CONFUSION:
if (!CanBeConfused(gEffectBattler) if (!CanBeConfused(gBattlerAttacker, gEffectBattler))
|| gBattleMons[gEffectBattler].volatiles.confusionTurns
|| (IsSafeguardProtected(gBattlerAttacker, gEffectBattler, GetBattlerAbility(gBattlerAttacker)) && !primary))
{ {
gBattlescriptCurrInstr = battleScript; gBattlescriptCurrInstr = battleScript;
} }
else else
{ {
gBattleMons[gEffectBattler].volatiles.confusionTurns = RandomUniform(RNG_CONFUSION_TURNS, 2, B_CONFUSION_TURNS); // 2-5 turns gBattleMons[gEffectBattler].volatiles.confusionTurns = RandomUniform(RNG_CONFUSION_TURNS, 2, B_CONFUSION_TURNS); // 2-5 turns
BattleScriptPush(battleScript);
// If the confusion is activating due to being released from Sky Drop, go to "confused due to fatigue" script. gBattlescriptCurrInstr = BattleScript_MoveEffectConfusion;
// Otherwise, do normal confusion script.
if (GetMoveEffect(gCurrentMove) == EFFECT_SKY_DROP)
{
gBattleMons[gEffectBattler].volatiles.rampageTurns = 0;
gBattlerAttacker = gEffectBattler;
gBattlescriptCurrInstr = BattleScript_ThrashConfuses;
}
else
{
BattleScriptPush(battleScript);
gBattlescriptCurrInstr = BattleScript_MoveEffectConfusion;
}
} }
break; break;
case MOVE_EFFECT_FLINCH: case MOVE_EFFECT_FLINCH:
@ -3184,8 +3165,8 @@ void SetMoveEffect(enum BattlerId battlerAtk, enum BattlerId effectBattler, enum
MarkBattlerForControllerExec(gBattlerTarget); MarkBattlerForControllerExec(gBattlerTarget);
} }
if (gBattleMons[gBattlerTarget].pp[i] == 0 && gBattleStruct->skyDropTargets[gBattlerTarget] == SKY_DROP_NO_TARGET) if (gBattleMons[gBattlerTarget].pp[i] == 0)
CancelMultiTurnMoves(gBattlerTarget, SKY_DROP_IGNORE); CancelMultiTurnMoves(gBattlerTarget);
BattleScriptPush(battleScript); BattleScriptPush(battleScript);
gBattlescriptCurrInstr = BattleScript_MoveEffectEerieSpell; gBattlescriptCurrInstr = BattleScript_MoveEffectEerieSpell;
@ -3977,7 +3958,6 @@ static void Cmd_cleareffectsonfaint(void)
if (gBattleControllerExecFlags == 0) if (gBattleControllerExecFlags == 0)
{ {
enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); enum BattlerId battler = GetBattlerForBattleScript(cmd->battler);
const u8 *clearDataResult = NULL;
if (!(gBattleTypeFlags & BATTLE_TYPE_ARENA) || !IsBattlerAlive(battler)) if (!(gBattleTypeFlags & BATTLE_TYPE_ARENA) || !IsBattlerAlive(battler))
{ {
gBattleMons[battler].status1 = 0; gBattleMons[battler].status1 = 0;
@ -3985,11 +3965,8 @@ static void Cmd_cleareffectsonfaint(void)
MarkBattlerForControllerExec(battler); MarkBattlerForControllerExec(battler);
} }
clearDataResult = FaintClearSetData(battler); // Effects like attractions, trapping, etc. FaintClearSetData(battler); // Effects like attractions, trapping, etc.
if (clearDataResult) gBattlescriptCurrInstr = cmd->nextInstr;
gBattlescriptCurrInstr = clearDataResult;
else
gBattlescriptCurrInstr = cmd->nextInstr;
} }
} }
@ -9100,9 +9077,8 @@ static void Cmd_tryspiteppreduce(void)
gBattlescriptCurrInstr = cmd->nextInstr; gBattlescriptCurrInstr = cmd->nextInstr;
// Don't cut off Sky Drop if pp is brought to zero. if (gBattleMons[gBattlerTarget].pp[i] == 0)
if (gBattleMons[gBattlerTarget].pp[i] == 0 && gBattleStruct->skyDropTargets[gBattlerTarget] == SKY_DROP_NO_TARGET) CancelMultiTurnMoves(gBattlerTarget);
CancelMultiTurnMoves(gBattlerTarget, SKY_DROP_IGNORE);
} }
else else
{ {
@ -11632,6 +11608,43 @@ static void Cmd_trysynchronize(void)
} }
} }
// If target was dropped due to attacker fainting and was previously rampaging, try to confuse
static void Cmd_tryconfusionafterskydrop(void)
{
CMD_ARGS(u8 battler);
enum BattlerId faintBattler = GetBattlerForBattleScript(cmd->battler);
enum BattlerId skyDropTarget = gBattleMons[faintBattler].volatiles.skyDropTarget - 1;
bool32 shouldConfuse = FALSE;
if (gBattleMons[faintBattler].volatiles.semiInvulnerable != STATE_SKY_DROP_ATTACKER)
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
else if (gBattleMons[skyDropTarget].volatiles.semiInvulnerable == STATE_SKY_DROP_TARGET)
{
BtlController_EmitSpriteInvisibility(skyDropTarget, B_COMM_TO_CONTROLLER, FALSE);
MarkBattlerForControllerExec(skyDropTarget);
gBattleMons[skyDropTarget].volatiles.semiInvulnerable = STATE_NONE;
gSpecialStatuses[skyDropTarget].restoredBattlerSprite = TRUE;
if (gBattleMons[skyDropTarget].volatiles.confuseAfterDrop)
{
gBattleMons[skyDropTarget].volatiles.confuseAfterDrop = FALSE;
if (CanBeConfused(skyDropTarget, skyDropTarget))
{
shouldConfuse = TRUE;
gBattleScripting.battler = skyDropTarget;
BattleScriptPush(cmd->nextInstr);
gBattlescriptCurrInstr = BattleScript_ConfusionAfterRampage;
}
}
}
if (!shouldConfuse)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_dummy(void) static void Cmd_dummy(void)
{ {
} }
@ -13172,7 +13185,7 @@ void BS_TrySetConfusion(void)
{ {
NATIVE_ARGS(const u8 *failInstr); NATIVE_ARGS(const u8 *failInstr);
if (CanBeConfused(gBattlerTarget)) if (CanBeConfused(gBattlerAttacker, gBattlerTarget))
{ {
gBattleMons[gBattlerTarget].volatiles.confusionTurns = RandomUniform(RNG_CONFUSION_TURNS, 2, B_CONFUSION_TURNS); // 2-5 turns gBattleMons[gBattlerTarget].volatiles.confusionTurns = RandomUniform(RNG_CONFUSION_TURNS, 2, B_CONFUSION_TURNS); // 2-5 turns
gBattleCommunication[MULTIUSE_STATE] = 1; gBattleCommunication[MULTIUSE_STATE] = 1;
@ -13612,10 +13625,6 @@ void BS_JumpIfNotBerry(void)
void BS_GravityOnAirborneMons(void) void BS_GravityOnAirborneMons(void)
{ {
NATIVE_ARGS(); NATIVE_ARGS();
// Cancel all multiturn moves of IN_AIR Pokemon except those being targeted by Sky Drop.
if (gBattleMons[gBattlerTarget].volatiles.semiInvulnerable == STATE_ON_AIR)
CancelMultiTurnMoves(gBattlerTarget, SKY_DROP_GRAVITY_ON_AIRBORNE);
gBattleMons[gBattlerTarget].volatiles.semiInvulnerable = STATE_NONE; gBattleMons[gBattlerTarget].volatiles.semiInvulnerable = STATE_NONE;
gBattleMons[gBattlerTarget].volatiles.magnetRise = FALSE; gBattleMons[gBattlerTarget].volatiles.magnetRise = FALSE;
gBattleMons[gBattlerTarget].volatiles.telekinesis = FALSE; gBattleMons[gBattlerTarget].volatiles.telekinesis = FALSE;
@ -13651,11 +13660,8 @@ void BS_TryAcupressure(void)
void BS_CancelMultiTurnMoves(void) void BS_CancelMultiTurnMoves(void)
{ {
NATIVE_ARGS(); NATIVE_ARGS();
const u8 *result = CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_CANCEL_MULTI_TURN_MOVES); CancelMultiTurnMoves(gBattlerAttacker);
if (result) gBattlescriptCurrInstr = cmd->nextInstr;
gBattlescriptCurrInstr = result;
else
gBattlescriptCurrInstr = cmd->nextInstr;
} }
void BS_IsRunningImpossible(void) void BS_IsRunningImpossible(void)
@ -14219,7 +14225,7 @@ void BS_TryInstruct(void)
|| IsMoveInstructBanned(move) || IsMoveInstructBanned(move)
|| IsInstructBannedChargingMove(gBattlerTarget) || IsInstructBannedChargingMove(gBattlerTarget)
|| gBattleMons[gBattlerTarget].volatiles.bideTurns != 0 || gBattleMons[gBattlerTarget].volatiles.bideTurns != 0
|| gBattleMons[gBattlerTarget].volatiles.semiInvulnerable == STATE_SKY_DROP || gBattleMons[gBattlerTarget].volatiles.semiInvulnerable == STATE_SKY_DROP_TARGET
|| gBattleMoveEffects[GetMoveEffect(move)].twoTurnEffect || gBattleMoveEffects[GetMoveEffect(move)].twoTurnEffect
|| (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX) || (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX)
|| IsZMove(move) || IsZMove(move)
@ -14643,37 +14649,6 @@ void BS_TryHealQuarterHealth(void)
gBattlescriptCurrInstr = cmd->nextInstr; // can heal gBattlescriptCurrInstr = cmd->nextInstr; // can heal
} }
void BS_SkyDropYawn(void)
{
NATIVE_ARGS();
if (gBattleStruct->skyDropTargets[gEffectBattler] != SKY_DROP_NO_TARGET && gBattleMons[gEffectBattler].volatiles.semiInvulnerable != STATE_SKY_DROP)
{
// Set the target of Sky Drop as gEffectBattler
gEffectBattler = gBattleStruct->skyDropTargets[gEffectBattler];
// Clear skyDropTargets data
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].volatiles.rampageTurns && CanBeConfused(gEffectBattler))
{
gBattleMons[gEffectBattler].volatiles.rampageTurns = 0;
gBattlerAttacker = gEffectBattler;
gBattleMons[gBattlerTarget].volatiles.confusionTurns = RandomUniform(RNG_CONFUSION_TURNS, 2, B_CONFUSION_TURNS); // 2-5 turns
gBattlescriptCurrInstr = BattleScript_ThrashConfuses;
}
else
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
else
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
void BS_JumpIfPranksterBlocked(void) void BS_JumpIfPranksterBlocked(void)
{ {
NATIVE_ARGS(const u8 *jumpInstr); NATIVE_ARGS(const u8 *jumpInstr);

View File

@ -1072,96 +1072,26 @@ void MarkBattlerReceivedLinkData(enum BattlerId battler)
MarkBattleControllerMessageSynchronizedOverLink(battler); MarkBattleControllerMessageSynchronizedOverLink(battler);
} }
const u8 *CheckSkyDropState(enum BattlerId battler, enum SkyDropState skyDropState) void CancelMultiTurnMoves(enum BattlerId battler)
{ {
const u8 *result = NULL;
u8 otherSkyDropper = gBattleStruct->skyDropTargets[battler];
gBattleMons[battler].volatiles.semiInvulnerable = STATE_NONE;
// 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].volatiles.rampageTurns && skyDropState != SKY_DROP_STATUS_YAWN)
{
gBattleMons[otherSkyDropper].volatiles.rampageTurns = 0;
// If the target can be confused, confuse them.
// Don't use CanBeConfused, can cause issues in edge cases.
enum Ability ability = GetBattlerAbility(otherSkyDropper);
if (!(gBattleMons[otherSkyDropper].volatiles.confusionTurns > 0
|| IsAbilityAndRecord(otherSkyDropper, ability, ABILITY_OWN_TEMPO)
|| IsMistyTerrainAffected(otherSkyDropper, ability, GetBattlerHoldEffect(otherSkyDropper), gFieldStatuses)))
{
gBattleMons[otherSkyDropper].volatiles.confusionTurns = RandomUniform(RNG_CONFUSION_TURNS, 2, B_CONFUSION_TURNS); // 2-5 turns
if (skyDropState == SKY_DROP_ATTACKCANCELER_CHECK)
{
gBattleStruct->skyDropTargets[battler] = SKY_DROP_RELEASED_TARGET;
}
else if (skyDropState == SKY_DROP_GRAVITY_ON_AIRBORNE)
{
// Reapplying STATE_SKY_DROPPED allows for avoiding unecessary messages when Gravity is applied to the target.
gBattleStruct->skyDropTargets[battler] = SKY_DROP_RELEASED_TARGET;
gBattleMons[otherSkyDropper].volatiles.semiInvulnerable = STATE_SKY_DROP;
}
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].volatiles.rampageTurns) && gBattleStruct->skyDropTargets[battler] < 4)
{
gBattleStruct->skyDropTargets[battler] = SKY_DROP_NO_TARGET;
gBattleStruct->skyDropTargets[otherSkyDropper] = SKY_DROP_NO_TARGET;
}
return result;
}
const u8 *CancelMultiTurnMoves(enum BattlerId battler, enum SkyDropState skyDropState)
{
const u8 *result = NULL;
gBattleMons[battler].volatiles.uproarTurns = 0; gBattleMons[battler].volatiles.uproarTurns = 0;
gBattleMons[battler].volatiles.bideTurns = 0; gBattleMons[battler].volatiles.bideTurns = 0;
if (B_RAMPAGE_CANCELLING < GEN_5)
{
gBattleMons[battler].volatiles.multipleTurns = 0;
gBattleMons[battler].volatiles.rampageTurns = 0;
}
else if (!gBattleMons[battler].volatiles.rampageTurns
|| gBattleMons[battler].volatiles.rampageTurns > 1)
{
gBattleMons[battler].volatiles.multipleTurns = 0;
}
// Clear battler's semi-invulnerable bits if they are not held by Sky Drop.
if (gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP)
gBattleMons[battler].volatiles.semiInvulnerable = STATE_NONE;
if (gBattleStruct->skyDropTargets[battler] != SKY_DROP_NO_TARGET && gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP)
result = CheckSkyDropState(battler, skyDropState);
gBattleMons[battler].volatiles.rolloutTimer = 0; gBattleMons[battler].volatiles.rolloutTimer = 0;
gBattleMons[battler].volatiles.furyCutterCounter = 0; gBattleMons[battler].volatiles.furyCutterCounter = 0;
return result; if (B_RAMPAGE_CONFUSION < GEN_5
} || gBattleMons[battler].volatiles.rampageTurns != 1) // Will be confused at the end of the turn
{
gLockedMoves[battler] = MOVE_NONE;
gBattleMons[battler].volatiles.multipleTurns = 0;
gBattleMons[battler].volatiles.rampageTurns = 0;
}
// Clear battler's semi-invulnerable bits if they are not held by Sky Drop.
if (gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP_TARGET)
gBattleMons[battler].volatiles.semiInvulnerable = STATE_NONE;
}
// Returns TRUE if no other battler after this one in turn order will use a move // Returns TRUE if no other battler after this one in turn order will use a move
bool32 IsLastMonToMove(enum BattlerId battler) bool32 IsLastMonToMove(enum BattlerId battler)
@ -1425,7 +1355,7 @@ u32 TrySetCantSelectMoveBattleScript(enum BattlerId battler)
if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(battler) != GIMMICK_Z_MOVE && move == gLastMoves[battler] && move != MOVE_STRUGGLE && (gBattleMons[battler].volatiles.torment == TRUE)) if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(battler) != GIMMICK_Z_MOVE && move == gLastMoves[battler] && move != MOVE_STRUGGLE && (gBattleMons[battler].volatiles.torment == TRUE))
{ {
CancelMultiTurnMoves(battler, SKY_DROP_IGNORE); CancelMultiTurnMoves(battler);
if (gBattleTypeFlags & BATTLE_TYPE_PALACE) if (gBattleTypeFlags & BATTLE_TYPE_PALACE)
{ {
gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingTormentedMoveInPalace; gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingTormentedMoveInPalace;
@ -1808,7 +1738,7 @@ bool32 BattleArenaTurnEnd(void)
&& IsBattlerAlive(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)) && IsBattlerAlive(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT))) && IsBattlerAlive(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)) && IsBattlerAlive(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)))
{ {
for (enum BattlerId battler = 0; battler < 2; battler++) for (enum BattlerId battler = 0; battler < 2; battler++)
CancelMultiTurnMoves(battler, SKY_DROP_IGNORE); CancelMultiTurnMoves(battler);
gBattlescriptCurrInstr = BattleScript_ArenaDoJudgment; gBattlescriptCurrInstr = BattleScript_ArenaDoJudgment;
BattleScriptExecute(BattleScript_ArenaDoJudgment); BattleScriptExecute(BattleScript_ArenaDoJudgment);
@ -4418,7 +4348,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
case ABILITY_POISON_PUPPETEER: case ABILITY_POISON_PUPPETEER:
if (IsRestrictedAbility(gBattlerAttacker, ABILITY_POISON_PUPPETEER) if (IsRestrictedAbility(gBattlerAttacker, ABILITY_POISON_PUPPETEER)
&& gBattleStruct->poisonPuppeteerConfusion == TRUE && gBattleStruct->poisonPuppeteerConfusion == TRUE
&& CanBeConfused(gBattlerTarget)) && CanBeConfused(gBattlerAttacker, gBattlerTarget))
{ {
gBattleStruct->poisonPuppeteerConfusion = FALSE; gBattleStruct->poisonPuppeteerConfusion = FALSE;
gBattleScripting.moveEffect = MOVE_EFFECT_CONFUSION; gBattleScripting.moveEffect = MOVE_EFFECT_CONFUSION;
@ -5023,7 +4953,7 @@ bool32 CanBattlerEscape(enum BattlerId battler) // no ability check
return FALSE; return FALSE;
else if (gFieldStatuses & STATUS_FIELD_FAIRY_LOCK) else if (gFieldStatuses & STATUS_FIELD_FAIRY_LOCK)
return FALSE; return FALSE;
else if (gBattleMons[battler].volatiles.semiInvulnerable == STATE_SKY_DROP) else if (gBattleMons[battler].volatiles.semiInvulnerable == STATE_SKY_DROP_TARGET)
return FALSE; return FALSE;
else else
return TRUE; return TRUE;
@ -5512,13 +5442,16 @@ static bool32 CanSleepDueToSleepClause(enum BattlerId battlerAtk, enum BattlerId
return FALSE; return FALSE;
} }
bool32 CanBeConfused(enum BattlerId battler) bool32 CanBeConfused(enum BattlerId battlerAtk, enum BattlerId effectBattler)
{ {
enum Ability ability = GetBattlerAbility(battler); enum Ability effectAbility = GetBattlerAbility(effectBattler);
if (gBattleMons[battler].volatiles.confusionTurns > 0
|| IsMistyTerrainAffected(battler, ability, GetBattlerHoldEffect(battler), gFieldStatuses) if (gBattleMons[effectBattler].volatiles.confusionTurns > 0
|| IsAbilityAndRecord(battler, ability, ABILITY_OWN_TEMPO)) || IsSafeguardProtected(battlerAtk, effectBattler, GetBattlerAbility(battlerAtk))
|| IsMistyTerrainAffected(effectBattler, effectAbility, GetBattlerHoldEffect(effectBattler), gFieldStatuses)
|| IsAbilityAndRecord(effectBattler, effectAbility, ABILITY_OWN_TEMPO))
return FALSE; return FALSE;
return TRUE; return TRUE;
} }
@ -7424,7 +7357,7 @@ static inline uq4_12_t GetDiveModifier(enum Move move, enum BattlerId battlerDef
static inline uq4_12_t GetAirborneModifier(enum Move move, enum BattlerId battlerDef) static inline uq4_12_t GetAirborneModifier(enum Move move, enum BattlerId battlerDef)
{ {
if (MoveDamagesAirborneDoubleDamage(move) && gBattleMons[battlerDef].volatiles.semiInvulnerable == STATE_ON_AIR) if (MoveDamagesAirborneDoubleDamage(move) && IsBattlerOnAir(battlerDef))
return UQ_4_12(2.0); return UQ_4_12(2.0);
return UQ_4_12(1.0); return UQ_4_12(1.0);
} }
@ -8543,7 +8476,7 @@ bool32 CanMegaEvolve(enum BattlerId battler)
return FALSE; return FALSE;
// Check if battler is currently held by Sky Drop. // Check if battler is currently held by Sky Drop.
if (gBattleMons[battler].volatiles.semiInvulnerable == STATE_SKY_DROP) if (gBattleMons[battler].volatiles.semiInvulnerable == STATE_SKY_DROP_TARGET)
return FALSE; return FALSE;
// Check if battler is holding a Z-Crystal. // Check if battler is holding a Z-Crystal.
@ -8584,7 +8517,7 @@ bool32 CanUltraBurst(enum BattlerId battler)
return FALSE; return FALSE;
// Check if mon is currently held by Sky Drop // Check if mon is currently held by Sky Drop
if (gBattleMons[battler].volatiles.semiInvulnerable == STATE_SKY_DROP) if (gBattleMons[battler].volatiles.semiInvulnerable == STATE_SKY_DROP_TARGET)
return FALSE; return FALSE;
enum Ability ability = GetBattlerAbility(battler); enum Ability ability = GetBattlerAbility(battler);
@ -10057,7 +9990,7 @@ bool32 EmergencyExitCanBeTriggered(enum BattlerId battler)
&& HadMoreThanHalfHpNowDoesnt(battler) && HadMoreThanHalfHpNowDoesnt(battler)
&& (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER)) && (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER))
&& !(gBattleTypeFlags & BATTLE_TYPE_ARENA) && !(gBattleTypeFlags & BATTLE_TYPE_ARENA)
&& gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP) && gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP_TARGET)
return TRUE; return TRUE;
return FALSE; return FALSE;
@ -10259,6 +10192,14 @@ static bool32 CanMoveSkipAccuracyCheck(enum BattlerId battlerAtk, u32 move)
return MoveAlwaysHitsOnSameType(move) && IS_BATTLER_OF_TYPE(battlerAtk, GetMoveType(move)); return MoveAlwaysHitsOnSameType(move) && IS_BATTLER_OF_TYPE(battlerAtk, GetMoveType(move));
} }
static bool32 IsSkyDropInvolved(enum BattlerId battlerDef, enum BattleMoveEffects moveEffect)
{
if (moveEffect != EFFECT_SKY_DROP)
return FALSE;
return gBattleMons[battlerDef].volatiles.semiInvulnerable == STATE_SKY_DROP_ATTACKER
|| gBattleMons[battlerDef].volatiles.semiInvulnerable == STATE_SKY_DROP_TARGET;
}
bool32 CanMoveSkipAccuracyCalc(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum Move move, enum ResultOption option) bool32 CanMoveSkipAccuracyCalc(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum Move move, enum ResultOption option)
{ {
bool32 effect = FALSE; bool32 effect = FALSE;
@ -10271,17 +10212,14 @@ bool32 CanMoveSkipAccuracyCalc(enum BattlerId battlerAtk, enum BattlerId battler
{ {
effect = TRUE; effect = TRUE;
} }
// 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 (abilityAtk == ABILITY_NO_GUARD else if (abilityAtk == ABILITY_NO_GUARD
&& gBattleMons[battlerDef].volatiles.semiInvulnerable != STATE_COMMANDER && gBattleMons[battlerDef].volatiles.semiInvulnerable != STATE_COMMANDER
&& (moveEffect != EFFECT_SKY_DROP || gBattleStruct->skyDropTargets[battlerDef] == SKY_DROP_NO_TARGET)) && !IsSkyDropInvolved(battlerDef, moveEffect))
{ {
effect = TRUE; effect = TRUE;
ability = ABILITY_NO_GUARD; 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 (abilityDef == ABILITY_NO_GUARD && !IsSkyDropInvolved(battlerDef, moveEffect))
else if (abilityDef == ABILITY_NO_GUARD
&& (moveEffect != EFFECT_SKY_DROP || gBattleStruct->skyDropTargets[battlerDef] == SKY_DROP_NO_TARGET))
{ {
effect = TRUE; effect = TRUE;
ability = ABILITY_NO_GUARD; ability = ABILITY_NO_GUARD;
@ -10569,7 +10507,8 @@ static bool32 CanBreakThroughSemiInvulnerablityInternal(enum BattlerId battlerAt
case STATE_UNDERWATER: case STATE_UNDERWATER:
return MoveDamagesUnderWater(move); return MoveDamagesUnderWater(move);
case STATE_ON_AIR: case STATE_ON_AIR:
case STATE_SKY_DROP: case STATE_SKY_DROP_ATTACKER:
case STATE_SKY_DROP_TARGET:
return MoveDamagesAirborne(move) || MoveDamagesAirborneDoubleDamage(move); return MoveDamagesAirborne(move) || MoveDamagesAirborneDoubleDamage(move);
case STATE_PHANTOM_FORCE: case STATE_PHANTOM_FORCE:
return FALSE; return FALSE;
@ -10593,6 +10532,19 @@ bool32 BreaksThroughSemiInvulnerableState(enum BattlerId battlerAtk, enum Battle
return CanBreakThroughSemiInvulnerablityInternal(battlerAtk, battlerDef, abilityAtk, abilityDef, move, state); return CanBreakThroughSemiInvulnerablityInternal(battlerAtk, battlerDef, abilityAtk, abilityDef, move, state);
} }
bool32 IsBattlerOnAir(enum BattlerId battler)
{
switch (gBattleMons[battler].volatiles.semiInvulnerable)
{
case STATE_ON_AIR:
case STATE_SKY_DROP_ATTACKER:
case STATE_SKY_DROP_TARGET:
return TRUE;
default:
return FALSE;
}
}
bool32 HasPartnerTrainer(enum BattlerId battler) bool32 HasPartnerTrainer(enum BattlerId battler)
{ {
if ((GetBattlerSide(battler) == B_SIDE_PLAYER && gBattleTypeFlags & BATTLE_TYPE_PLAYER_HAS_PARTNER) if ((GetBattlerSide(battler) == B_SIDE_PLAYER && gBattleTypeFlags & BATTLE_TYPE_PLAYER_HAS_PARTNER)

View File

@ -208,3 +208,86 @@ DOUBLE_BATTLE_TEST("Sky Drop does not trigger Volt Absorb on it's charge turn")
} }
} }
SINGLE_BATTLE_TEST("Sky Drop: If target was locked into a move that would confuse, the target will be freed and confusion occurs immediately")
{
GIVEN {
PLAYER(SPECIES_TORKOAL) { Ability(ABILITY_DROUGHT); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_THRASH); MOVE(opponent, MOVE_SKY_DROP);}
TURN { SKIP_TURN(opponent); }
} SCENE {
ABILITY_POPUP(player, ABILITY_DROUGHT);
ANIMATION(ANIM_TYPE_MOVE, MOVE_THRASH, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_DROP, opponent);
HP_BAR(player);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, player);
MESSAGE("The sunlight is strong.");
}
}
DOUBLE_BATTLE_TEST("Sky Drop: If target was locked into a move that would confuse, the target will be freed and confusion occurs immediately (attacker faints due to status)")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { HP(1); MaxHP(2); Status1(STATUS1_BURN); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(playerLeft, MOVE_THRASH, target: opponentRight);
MOVE(opponentLeft, MOVE_SKY_DROP, target: playerLeft);
SEND_OUT(opponentLeft, 2);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_THRASH, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_DROP, opponentLeft);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, playerLeft);
}
}
DOUBLE_BATTLE_TEST("Sky Drop: If target was locked into a move that would confuse, the target will be freed and confusion occurs immediately (attacker faints due to target ability)")
{
GIVEN {
PLAYER(SPECIES_SHARPEDO) { Ability(ABILITY_ROUGH_SKIN); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { HP(1); MaxHP(2); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(playerLeft, MOVE_THRASH, target: opponentRight);
MOVE(opponentLeft, MOVE_SKY_DROP, target: playerLeft);
}
TURN {
SKIP_TURN(opponentLeft);
SEND_OUT(opponentLeft, 2);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_THRASH, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_DROP, opponentLeft); // 1st turn
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_DROP, opponentLeft); // 2nd turn
ABILITY_POPUP(playerLeft, ABILITY_ROUGH_SKIN);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, playerLeft);
}
}
SINGLE_BATTLE_TEST("Sky Drop: Flying types will still get confused if they rampaged before being dropped")
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_PIDGEY].weight < 2000);
ASSUME(GetSpeciesType(SPECIES_PIDGEY, 1) == TYPE_FLYING);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PIDGEY);
} WHEN {
TURN { MOVE(opponent, MOVE_THRASH); MOVE(player, MOVE_SKY_DROP); }
TURN { SKIP_TURN(player); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_THRASH, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_DROP, player);
NOT HP_BAR(opponent);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, opponent);
}
}

View File

@ -74,7 +74,7 @@ SINGLE_BATTLE_TEST("Thrash does not confuse the user if it is canceled on turn 2
SINGLE_BATTLE_TEST("Thrash confuses the user if it is canceled on turn 3 of 3, Protect") SINGLE_BATTLE_TEST("Thrash confuses the user if it is canceled on turn 3 of 3, Protect")
{ {
GIVEN { GIVEN {
ASSUME(B_RAMPAGE_CANCELLING >= GEN_5); WITH_CONFIG(B_RAMPAGE_CONFUSION, GEN_5);
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET);
} WHEN { } WHEN {
@ -89,7 +89,7 @@ SINGLE_BATTLE_TEST("Thrash confuses the user if it is canceled on turn 3 of 3, P
SINGLE_BATTLE_TEST("Thrash confuses the user if it is canceled on turn 3 of 3, Immunity") SINGLE_BATTLE_TEST("Thrash confuses the user if it is canceled on turn 3 of 3, Immunity")
{ {
GIVEN { GIVEN {
ASSUME(B_RAMPAGE_CANCELLING >= GEN_5); WITH_CONFIG(B_RAMPAGE_CONFUSION, GEN_5);
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GENGAR); OPPONENT(SPECIES_GENGAR);
@ -117,3 +117,24 @@ SINGLE_BATTLE_TEST("Petal Dance does not lock mons that copy the move with Dance
EXPECT(!(opponent->volatiles.multipleTurns)); EXPECT(!(opponent->volatiles.multipleTurns));
} }
} }
SINGLE_BATTLE_TEST("Thrash confuses the user after it finishes even if move failed")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { MovesWithPP({MOVE_THRASH, 10}); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GASTLY);
} WHEN {
TURN { MOVE(player, MOVE_THRASH); }
TURN { SKIP_TURN(player); }
TURN { SWITCH(opponent, 1); SKIP_TURN(player); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_THRASH, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_THRASH, player);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_THRASH, player);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, player);
} THEN {
// Check that PP has been consumed correctly
EXPECT_EQ(player->pp[0], 9);
}
}