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
.endm
.macro tryconfusionafterskydrop battler:req
.byte B_SCR_OP_TRY_CONFUSION_AFTER_SKY_DROP
.byte \battler
.endm
.macro callnative func:req
.byte B_SCR_OP_CALLNATIVE
.4byte \func
@ -2082,10 +2087,6 @@
.4byte \failInstr
.endm
.macro skydropyawn
callnative BS_SkyDropYawn
.endm
.macro jumpifpranksterblocked jumpInstr:req
callnative BS_JumpIfPranksterBlocked
.4byte \jumpInstr

View File

@ -2118,6 +2118,8 @@ BattleScript_EffectGravitySuccess::
selectfirstvalidtarget
BattleScript_GravityLoop:
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_TELEKINESIS, BattleScript_GravityLoopDrop
goto BattleScript_GravityLoopEnd
@ -2832,18 +2834,6 @@ BattleScript_SkyDropFlyingType::
printstring STRINGID_ITDOESNTAFFECT
waitmessage B_WAIT_TIME_LONG
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
BattleScript_SkyDropNoTarget::
@ -3978,6 +3968,7 @@ BattleScript_FaintBattler::
dofaintanimation BS_FAINTED
copybyte sBATTLER, gBattlerFainted @ for message
printstring STRINGID_BATTLERFAINTED
tryconfusionafterskydrop BS_FAINTED
cleareffectsonfaint BS_FAINTED
trytoclearprimalweather
tryrevertweatherform
@ -5705,7 +5696,7 @@ BattleScript_ThrashConfuses::
waitmessage B_WAIT_TIME_LONG
end2
BattleScript_ThrashConfusesRet::
BattleScript_ConfusionAfterRampage::
volatileanimation BS_SCRIPTING, VOLATILE_CONFUSION
printstring STRINGID_PKMNFATIGUECONFUSION
waitmessage B_WAIT_TIME_LONG
@ -5837,9 +5828,8 @@ BattleScript_YawnMakesAsleepEnd2::
updatestatusicon BS_EFFECT_BATTLER
waitstate
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
skydropyawn
BattleScript_YawnEnd:
end2

View File

@ -653,7 +653,6 @@ struct BattleStruct
u8 throwingPokeBall:1;
u8 ballSpriteIds[2]; // item gfx, 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.
u16 beatUpSpecies[PARTY_SIZE]; // Species for Gen5+ Beat Up, otherwise party indexes
u8 attackerBeforeBounce:2;

View File

@ -90,7 +90,7 @@ void AnimSetCenterToCornerVecX(struct Sprite *sprite);
void BeginBattleIntroDummy(void);
void BeginBattleIntro(void);
void SwitchInClearSetData(enum BattlerId battler, struct Volatiles *volatilesCopy);
const u8 *FaintClearSetData(enum BattlerId battler);
void FaintClearSetData(enum BattlerId battler);
void BattleTurnPassed(void);
u8 IsRunningFromBattleImpossible(enum BattlerId battler);
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_PrintUproarOverTurns[];
extern const u8 BattleScript_ThrashConfuses[];
extern const u8 BattleScript_ThrashConfusesRet[];
extern const u8 BattleScript_ConfusionAfterRampage[];
extern const u8 BattleScript_MoveUsedIsConfused[];
extern const u8 BattleScript_MoveUsedIsConfusedNoMore[];
extern const u8 BattleScript_PrintPayDayMoneyString[];

View File

@ -152,19 +152,6 @@ enum SleepClauseBlock
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
{
START_OF_TURN,
@ -197,7 +184,7 @@ enum BattlerId GetBattlerForBattleScript(u8 caseId);
bool32 IsBattlerMarkedForControllerExec(enum BattlerId battler);
void MarkBattlerForControllerExec(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 ShouldDefiantCompetitiveActivate(enum BattlerId battler, enum Ability ability);
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 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 CanBeConfused(enum BattlerId battler);
bool32 CanBeConfused(enum BattlerId battlerAtk, enum BattlerId effectBattler);
bool32 IsSafeguardProtected(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk);
u32 GetBattlerAffectionHearts(enum BattlerId battler);
void TryToRevertMimicryAndFlags(void);
@ -405,6 +392,7 @@ bool32 DoesMoveMissTarget(struct BattleCalcValues *cv);
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 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 IsAffectedByPowderMove(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect);
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_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_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.
// 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.

View File

@ -183,6 +183,8 @@ enum VolatileFlags
F(VOLATILE_BIDE, bideTurns, (u32, 3)) \
F(VOLATILE_RAMPAGE_TURNS, rampageTurns, (u32, B_RAMPAGE_TURNS + 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_BY, wrappedBy, (enum BattlerId, MAX_BITS(MAX_BATTLERS_COUNT))) \
F(VOLATILE_WRAPPED_MOVE, wrappedMove, (u32, MOVES_COUNT_ALL - 1)) \
@ -318,7 +320,8 @@ enum SemiInvulnerableState
STATE_UNDERWATER,
STATE_ON_AIR,
STATE_PHANTOM_FORCE,
STATE_SKY_DROP,
STATE_SKY_DROP_ATTACKER,
STATE_SKY_DROP_TARGET,
STATE_COMMANDER,
SEMI_INVULNERABLE_COUNT
};

View File

@ -96,7 +96,6 @@ enum MoveEndState
MOVEEND_SYMBIOSIS,
MOVEEND_SUBSTITUTE,
MOVEEND_FAINT_BLOCK,
MOVEEND_SKY_DROP_CONFUSE,
MOVEEND_UPDATE_LAST_MOVES,
MOVEEND_MIRROR_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_MIRROR_HERB,
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_CLEAR_BITS,
MOVEEND_DANCER,

View File

@ -226,6 +226,7 @@ enum BattleScriptOpcode
B_SCR_OP_SETNONVOLATILESTATUS,
B_SCR_OP_TRYOVERWRITEABILITY,
B_SCR_OP_TRY_SYNCHRONIZE,
B_SCR_OP_TRY_CONFUSION_AFTER_SKY_DROP,
// Expansion users, please don't use any of the unused commands.
// They are reserved for expansion usage.
@ -261,7 +262,6 @@ enum BattleScriptOpcode
B_SCR_OP_UNUSED_29,
B_SCR_OP_UNUSED_30,
B_SCR_OP_UNUSED_31,
B_SCR_OP_UNUSED_32,
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_BRICK_BREAK, brickBreak, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
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_ROOTED_GROUNDING, rootedGrounding, (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 ((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;
if (gBattleMons[battler].volatiles.embargo)

View File

@ -427,7 +427,7 @@ bool32 IsBattlerTrapped(enum BattlerId battlerAtk, enum BattlerId battlerDef)
return TRUE;
if (gBattleMons[battlerDef].volatiles.escapePrevention)
return TRUE;
if (gBattleMons[battlerDef].volatiles.semiInvulnerable == STATE_SKY_DROP)
if (gBattleMons[battlerDef].volatiles.semiInvulnerable == STATE_SKY_DROP_TARGET)
return TRUE;
if (gBattleMons[battlerDef].volatiles.root)
return TRUE;
@ -3696,7 +3696,7 @@ bool32 AI_CanBeConfused(enum BattlerId battlerAtk, enum BattlerId battlerDef, en
if (gBattleMons[battlerDef].volatiles.confusionTurns > 0
|| (abilityDef == ABILITY_OWN_TEMPO && !DoesBattlerIgnoreAbilityChecks(battlerAtk, gAiLogicData->abilities[battlerAtk], move))
|| IsMistyTerrainAffected(battlerDef, abilityDef, gAiLogicData->holdEffects[battlerDef], gFieldStatuses)
|| gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD
|| IsSafeguardProtected(battlerAtk, battlerDef, gAiLogicData->abilities[battlerAtk])
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move))
return FALSE;
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) \
if (gSideTimers[side].field == battlerAtk) \
gSideTimers[side].field = battlerPartner; \
@ -6748,7 +6730,6 @@ static void AnimTask_AllySwitchDataSwap(u8 taskId)
SwitchTwoBattlersInParty(battlerAtk, battlerPartner);
SWAP(gBattlerPartyIndexes[battlerAtk], gBattlerPartyIndexes[battlerPartner], temp);
TrySwapSkyDropTargets(battlerAtk, battlerPartner);
TrySwapStickyWebBattlerId(battlerAtk, battlerPartner);
TrySwapWishBattlerIds(battlerAtk, battlerPartner);
TrySwapAttractBattlerIds(battlerAtk, battlerPartner);

View File

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

View File

@ -3084,7 +3084,6 @@ 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] = SKY_DROP_NO_TARGET;
}
gLastUsedMove = 0;
@ -3328,10 +3327,8 @@ void SwitchInClearSetData(enum BattlerId battler, struct Volatiles *volatilesCop
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++)
gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE;
@ -3427,48 +3424,6 @@ const u8* FaintClearSetData(enum BattlerId battler)
Ai_UpdateFaintData(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)
@ -3964,7 +3919,7 @@ static void HandleEndTurn_ContinueBattle(void)
{
gBattleMons[battler].volatiles.flinched = FALSE;
if ((gBattleMons[battler].status1 & STATUS1_SLEEP) && (gBattleMons[battler].volatiles.multipleTurns))
CancelMultiTurnMoves(battler, SKY_DROP_IGNORE);
CancelMultiTurnMoves(battler);
}
gBattleStruct->eventState.endTurnBlock = 0;
gBattleStruct->eventState.endTurnBattler = 0;
@ -4298,7 +4253,7 @@ static void HandleTurnActionSelectionState(void)
| BATTLE_TYPE_RECORDED_LINK))
&& !gTestRunnerEnabled)
// 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);
gSelectionBattleScripts[battler] = BattleScript_ActionSelectionItemsCantBeUsed;

View File

@ -68,7 +68,7 @@ static enum CancelerResult CancelerStanceChangeOne(struct BattleContext *ctx)
static enum CancelerResult CancelerSkyDrop(struct BattleContext *ctx)
{
// 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;
return CANCELER_RESULT_FAILURE;
@ -80,7 +80,7 @@ static enum CancelerResult CancelerRecharge(struct BattleContext *ctx)
{
if (gBattleMons[ctx->battlerAtk].volatiles.rechargeTimer > 0)
{
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK);
CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_MoveUsedMustRecharge;
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)
{
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK);
CancelMultiTurnMoves(ctx->battlerAtk);
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LOAFING;
gBattlerAbility = ctx->battlerAtk;
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)
&& !gProtectStructs[ctx->battlerAtk].survivedOHKO)
{
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK);
CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_FocusPunchLostFocus;
return CANCELER_RESULT_FAILURE;
}
@ -298,7 +298,7 @@ static enum CancelerResult CancelerFlinch(struct BattleContext *ctx)
{
if (gBattleMons[ctx->battlerAtk].volatiles.flinched)
{
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK);
CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_MoveUsedFlinched;
return CANCELER_RESULT_FAILURE;
}
@ -312,7 +312,7 @@ static enum CancelerResult CancelerDisabled(struct BattleContext *ctx)
&& gBattleMons[ctx->battlerAtk].volatiles.disabledMove != MOVE_NONE)
{
gBattleScripting.battler = ctx->battlerAtk;
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK);
CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsDisabled;
return CANCELER_RESULT_FAILURE;
}
@ -326,20 +326,20 @@ static enum CancelerResult CancelerVolatileBlocked(struct BattleContext *ctx)
&& IsHealBlockPreventingMove(ctx->battlerAtk, ctx->move))
{
gBattleScripting.battler = ctx->battlerAtk;
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK);
CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_MoveUsedHealBlockPrevents;
return CANCELER_RESULT_FAILURE;
}
else if (gFieldStatuses & STATUS_FIELD_GRAVITY && IsGravityPreventingMove(ctx->move))
{
gBattleScripting.battler = ctx->battlerAtk;
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK);
CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_MoveUsedGravityPrevents;
return CANCELER_RESULT_FAILURE;
}
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;
return CANCELER_RESULT_FAILURE;
}
@ -353,7 +353,7 @@ static enum CancelerResult CancelerTaunted(struct BattleContext *ctx)
&& IsBattleMoveStatus(ctx->move)
&& (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;
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))
{
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK);
CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsImprisoned;
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))
&& !RandomPercentage(RNG_PARALYSIS, 75))
{
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK);
CancelMultiTurnMoves(gBattlerAttacker);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsParalyzed;
return CANCELER_RESULT_FAILURE;
}
@ -453,7 +453,7 @@ static enum CancelerResult CancelerInfatuation(struct BattleContext *ctx)
else
{
BattleScriptPush(BattleScript_MoveUsedIsInLoveCantAttack);
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK);
CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsInLove;
return CANCELER_RESULT_FAILURE;
}
@ -1015,7 +1015,7 @@ static enum CancelerResult CancelerSkyBattle(struct BattleContext *ctx)
{
if (gBattleStruct->isSkyBattle && IsMoveSkyBattleBanned(gCurrentMove))
{
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK);
CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_ButItFailed;
return CANCELER_RESULT_FAILURE;
}
@ -1042,7 +1042,7 @@ static enum CancelerResult CancelerWeatherPrimal(struct BattleContext *ctx)
if (result == CANCELER_RESULT_FAILURE)
{
gProtectStructs[ctx->battlerAtk].chargingTurn = FALSE;
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK);
CancelMultiTurnMoves(ctx->battlerAtk);
gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove;
}
}
@ -1492,17 +1492,16 @@ static enum CancelerResult HandleSkyDropResult(struct BattleContext *ctx)
gBattleScripting.animTargetsHit = 0;
gBattleMons[ctx->battlerAtk].volatiles.multipleTurns = FALSE;
gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable = STATE_NONE;
gBattleMons[ctx->battlerDef].volatiles.semiInvulnerable = STATE_NONE;
gBattleStruct->skyDropTargets[gBattlerTarget] = SKY_DROP_NO_TARGET;
gBattleMons[ctx->battlerAtk].volatiles.skyDropTarget = 0;
// Sky Drop fails if target already fainted
if (gBattleStruct->skyDropTargets[ctx->battlerAtk] == SKY_DROP_NO_TARGET)
// Sky Drop fails if target already left the field
if (gBattleMons[ctx->battlerDef].volatiles.semiInvulnerable == STATE_NONE)
{
gBattlescriptCurrInstr = BattleScript_SkyDropNoTarget;
return CANCELER_RESULT_FAILURE;
}
gBattleStruct->skyDropTargets[ctx->battlerAtk] = SKY_DROP_NO_TARGET;
gBattleMons[ctx->battlerDef].volatiles.semiInvulnerable = STATE_NONE;
return CANCELER_RESULT_SUCCESS;
}
@ -1528,22 +1527,19 @@ static enum CancelerResult HandleSkyDropResult(struct BattleContext *ctx)
}
// 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)
gSideTimers[GetBattlerSide(ctx->battlerDef)].followmeTimer = 0;
if (gBattleMons[ctx->battlerDef].volatiles.rampageTurns > 0)
gBattleMons[ctx->battlerDef].volatiles.confuseAfterDrop = TRUE;
gBattleStruct->skyDropTargets[ctx->battlerAtk] = ctx->battlerDef;
gBattleStruct->skyDropTargets[ctx->battlerDef] = ctx->battlerAtk;
CancelMultiTurnMoves(ctx->battlerDef);
gLockedMoves[ctx->battlerAtk] = ctx->move;
gProtectStructs[ctx->battlerAtk].chargingTurn = TRUE;
gBattleMons[ctx->battlerAtk].volatiles.multipleTurns = TRUE;
gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable = STATE_ON_AIR;
gBattleMons[ctx->battlerDef].volatiles.semiInvulnerable = STATE_SKY_DROP;
gBattleMons[ctx->battlerAtk].volatiles.skyDropTarget = ctx->battlerDef + 1;
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;
return CANCELER_RESULT_BREAK;
@ -1803,7 +1799,7 @@ static enum CancelerResult CancelerTargetFailure(struct BattleContext *ctx)
gLastHitByType[gBattlerTarget] = 0;
gBattleScripting.battler = ctx->battlerDef;
gBattleStruct->pledgeMove = FALSE;
CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK);
CancelMultiTurnMoves(ctx->battlerAtk);
return CANCELER_RESULT_PAUSE;
}
}
@ -2343,7 +2339,7 @@ static enum MoveEndResult MoveEndAttackerVisible(void)
if (IsBattlerUnaffectedByMove(gBattlerTarget)
|| !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);
MarkBattlerForControllerExec(gBattlerAttacker);
@ -2574,37 +2570,6 @@ static enum MoveEndResult MoveEndFaintBlock(void)
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)
{
if (!IsOnPlayerSide(gBattlerAttacker))
@ -3654,6 +3619,58 @@ static bool32 ShouldSetStompingTantrumTimer(void)
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)
{
ValidateBattlers();
@ -3672,12 +3689,6 @@ static enum MoveEndResult MoveEndClearBits(void)
if (originallyUsedMove != MOVE_NONE)
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);
TryClearChargeVolatile(moveType);
gProtectStructs[gBattlerAttacker].shellTrap = FALSE;
@ -3699,6 +3710,7 @@ static enum MoveEndResult MoveEndClearBits(void)
SetActiveGimmick(gBattlerAttacker, GIMMICK_NONE);
if (gBattleMons[gBattlerAttacker].volatiles.destinyBond > 0)
gBattleMons[gBattlerAttacker].volatiles.destinyBond--;
// check if Stellar type boost should be used up
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA
&& GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR
@ -3789,7 +3801,6 @@ static enum MoveEndResult (*const sMoveEndHandlers[])(void) =
[MOVEEND_SYMBIOSIS] = MoveEndSymbiosis,
[MOVEEND_SUBSTITUTE] = MoveEndSubstitute,
[MOVEEND_FAINT_BLOCK] = MoveEndFaintBlock,
[MOVEEND_SKY_DROP_CONFUSE] = MoveEndSkyDropConfuse,
[MOVEEND_UPDATE_LAST_MOVES] = MoveEndUpdateLastMoves,
[MOVEEND_MIRROR_MOVE] = MoveEndMirrorMove,
[MOVEEND_NEXT_TARGET] = MoveEndNextTarget,
@ -3807,14 +3818,16 @@ static enum MoveEndResult (*const sMoveEndHandlers[])(void) =
[MOVEEND_LIFE_ORB_SHELL_BELL] = MoveEndLifeOrbShellBell,
[MOVEEND_FORM_CHANGE] = MoveEndFormChange,
[MOVEEND_EMERGENCY_EXIT] = MoveEndEmergencyExit,
[MOVEEND_EJECT_PACK] = MoveEndEjectPack,
[MOVEEND_HIT_ESCAPE] = MoveEndHitEscape,
[MOVEEND_PICKPOCKET] = MoveEndPickpocket,
[MOVEEND_ITEMS_EFFECTS_ALL] = MoveEndItemsEffectsAll,
[MOVEEND_WHITE_HERB] = MoveEndWhiteHerb,
[MOVEEND_OPPORTUNIST] = MoveEndOpportunist,
[MOVEEND_MIRROR_HERB] = MoveEndMirrorHerb,
[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_DANCER] = MoveEndDancer,
[MOVEEND_PURSUIT_NEXT_ACTION] = MoveEndPursuitNextAction,

View File

@ -576,6 +576,7 @@ static void Cmd_jumpifcaptivateaffected(void);
static void Cmd_setnonvolatilestatus(void);
static void Cmd_tryoverwriteability(void);
static void Cmd_trysynchronize(void);
static void Cmd_tryconfusionafterskydrop(void);
static void Cmd_dummy(void);
static void Cmd_callnative(void);
@ -804,6 +805,7 @@ void (*const gBattleScriptingCommandsTable[])(void) =
[B_SCR_OP_SETNONVOLATILESTATUS] = Cmd_setnonvolatilestatus,
[B_SCR_OP_TRYOVERWRITEABILITY] = Cmd_tryoverwriteability,
[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_2] = 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_30] = Cmd_dummy,
[B_SCR_OP_UNUSED_31] = Cmd_dummy,
[B_SCR_OP_UNUSED_32] = Cmd_dummy,
[B_SCR_OP_CALLNATIVE] = Cmd_callnative,
};
@ -2321,14 +2322,8 @@ static void SetNonVolatileStatus(enum BattlerId battlerAtk, enum BattlerId effec
{
gEffectBattler = effectBattler;
if (effect == MOVE_EFFECT_SLEEP
|| effect == MOVE_EFFECT_FREEZE)
{
const u8 *cancelMultiTurnMovesResult = NULL;
cancelMultiTurnMovesResult = CancelMultiTurnMoves(effectBattler, SKY_DROP_STATUS_FREEZE_SLEEP);
if (cancelMultiTurnMovesResult)
gBattlescriptCurrInstr = cancelMultiTurnMovesResult;
}
if (effect == MOVE_EFFECT_SLEEP || effect == MOVE_EFFECT_FREEZE)
CancelMultiTurnMoves(effectBattler);
BattleScriptPush(battleScript);
@ -2518,29 +2513,15 @@ void SetMoveEffect(enum BattlerId battlerAtk, enum BattlerId effectBattler, enum
}
break;
case MOVE_EFFECT_CONFUSION:
if (!CanBeConfused(gEffectBattler)
|| gBattleMons[gEffectBattler].volatiles.confusionTurns
|| (IsSafeguardProtected(gBattlerAttacker, gEffectBattler, GetBattlerAbility(gBattlerAttacker)) && !primary))
if (!CanBeConfused(gBattlerAttacker, gEffectBattler))
{
gBattlescriptCurrInstr = battleScript;
}
else
{
gBattleMons[gEffectBattler].volatiles.confusionTurns = RandomUniform(RNG_CONFUSION_TURNS, 2, B_CONFUSION_TURNS); // 2-5 turns
// If the confusion is activating due to being released from Sky Drop, go to "confused due to fatigue" script.
// 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;
}
BattleScriptPush(battleScript);
gBattlescriptCurrInstr = BattleScript_MoveEffectConfusion;
}
break;
case MOVE_EFFECT_FLINCH:
@ -3184,8 +3165,8 @@ void SetMoveEffect(enum BattlerId battlerAtk, enum BattlerId effectBattler, enum
MarkBattlerForControllerExec(gBattlerTarget);
}
if (gBattleMons[gBattlerTarget].pp[i] == 0 && gBattleStruct->skyDropTargets[gBattlerTarget] == SKY_DROP_NO_TARGET)
CancelMultiTurnMoves(gBattlerTarget, SKY_DROP_IGNORE);
if (gBattleMons[gBattlerTarget].pp[i] == 0)
CancelMultiTurnMoves(gBattlerTarget);
BattleScriptPush(battleScript);
gBattlescriptCurrInstr = BattleScript_MoveEffectEerieSpell;
@ -3977,7 +3958,6 @@ static void Cmd_cleareffectsonfaint(void)
if (gBattleControllerExecFlags == 0)
{
enum BattlerId battler = GetBattlerForBattleScript(cmd->battler);
const u8 *clearDataResult = NULL;
if (!(gBattleTypeFlags & BATTLE_TYPE_ARENA) || !IsBattlerAlive(battler))
{
gBattleMons[battler].status1 = 0;
@ -3985,11 +3965,8 @@ static void Cmd_cleareffectsonfaint(void)
MarkBattlerForControllerExec(battler);
}
clearDataResult = FaintClearSetData(battler); // Effects like attractions, trapping, etc.
if (clearDataResult)
gBattlescriptCurrInstr = clearDataResult;
else
gBattlescriptCurrInstr = cmd->nextInstr;
FaintClearSetData(battler); // Effects like attractions, trapping, etc.
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
@ -9100,9 +9077,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] == SKY_DROP_NO_TARGET)
CancelMultiTurnMoves(gBattlerTarget, SKY_DROP_IGNORE);
if (gBattleMons[gBattlerTarget].pp[i] == 0)
CancelMultiTurnMoves(gBattlerTarget);
}
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)
{
}
@ -13172,7 +13185,7 @@ void BS_TrySetConfusion(void)
{
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
gBattleCommunication[MULTIUSE_STATE] = 1;
@ -13612,10 +13625,6 @@ void BS_JumpIfNotBerry(void)
void BS_GravityOnAirborneMons(void)
{
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.magnetRise = FALSE;
gBattleMons[gBattlerTarget].volatiles.telekinesis = FALSE;
@ -13651,11 +13660,8 @@ void BS_TryAcupressure(void)
void BS_CancelMultiTurnMoves(void)
{
NATIVE_ARGS();
const u8 *result = CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_CANCEL_MULTI_TURN_MOVES);
if (result)
gBattlescriptCurrInstr = result;
else
gBattlescriptCurrInstr = cmd->nextInstr;
CancelMultiTurnMoves(gBattlerAttacker);
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_IsRunningImpossible(void)
@ -14219,7 +14225,7 @@ void BS_TryInstruct(void)
|| IsMoveInstructBanned(move)
|| IsInstructBannedChargingMove(gBattlerTarget)
|| gBattleMons[gBattlerTarget].volatiles.bideTurns != 0
|| gBattleMons[gBattlerTarget].volatiles.semiInvulnerable == STATE_SKY_DROP
|| gBattleMons[gBattlerTarget].volatiles.semiInvulnerable == STATE_SKY_DROP_TARGET
|| gBattleMoveEffects[GetMoveEffect(move)].twoTurnEffect
|| (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX)
|| IsZMove(move)
@ -14643,37 +14649,6 @@ void BS_TryHealQuarterHealth(void)
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)
{
NATIVE_ARGS(const u8 *jumpInstr);

View File

@ -1072,96 +1072,26 @@ void MarkBattlerReceivedLinkData(enum BattlerId 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.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.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
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))
{
CancelMultiTurnMoves(battler, SKY_DROP_IGNORE);
CancelMultiTurnMoves(battler);
if (gBattleTypeFlags & BATTLE_TYPE_PALACE)
{
gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingTormentedMoveInPalace;
@ -1808,7 +1738,7 @@ bool32 BattleArenaTurnEnd(void)
&& IsBattlerAlive(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)) && IsBattlerAlive(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)))
{
for (enum BattlerId battler = 0; battler < 2; battler++)
CancelMultiTurnMoves(battler, SKY_DROP_IGNORE);
CancelMultiTurnMoves(battler);
gBattlescriptCurrInstr = BattleScript_ArenaDoJudgment;
BattleScriptExecute(BattleScript_ArenaDoJudgment);
@ -4418,7 +4348,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
case ABILITY_POISON_PUPPETEER:
if (IsRestrictedAbility(gBattlerAttacker, ABILITY_POISON_PUPPETEER)
&& gBattleStruct->poisonPuppeteerConfusion == TRUE
&& CanBeConfused(gBattlerTarget))
&& CanBeConfused(gBattlerAttacker, gBattlerTarget))
{
gBattleStruct->poisonPuppeteerConfusion = FALSE;
gBattleScripting.moveEffect = MOVE_EFFECT_CONFUSION;
@ -5023,7 +4953,7 @@ bool32 CanBattlerEscape(enum BattlerId battler) // no ability check
return FALSE;
else if (gFieldStatuses & STATUS_FIELD_FAIRY_LOCK)
return FALSE;
else if (gBattleMons[battler].volatiles.semiInvulnerable == STATE_SKY_DROP)
else if (gBattleMons[battler].volatiles.semiInvulnerable == STATE_SKY_DROP_TARGET)
return FALSE;
else
return TRUE;
@ -5512,13 +5442,16 @@ static bool32 CanSleepDueToSleepClause(enum BattlerId battlerAtk, enum BattlerId
return FALSE;
}
bool32 CanBeConfused(enum BattlerId battler)
bool32 CanBeConfused(enum BattlerId battlerAtk, enum BattlerId effectBattler)
{
enum Ability ability = GetBattlerAbility(battler);
if (gBattleMons[battler].volatiles.confusionTurns > 0
|| IsMistyTerrainAffected(battler, ability, GetBattlerHoldEffect(battler), gFieldStatuses)
|| IsAbilityAndRecord(battler, ability, ABILITY_OWN_TEMPO))
enum Ability effectAbility = GetBattlerAbility(effectBattler);
if (gBattleMons[effectBattler].volatiles.confusionTurns > 0
|| IsSafeguardProtected(battlerAtk, effectBattler, GetBattlerAbility(battlerAtk))
|| IsMistyTerrainAffected(effectBattler, effectAbility, GetBattlerHoldEffect(effectBattler), gFieldStatuses)
|| IsAbilityAndRecord(effectBattler, effectAbility, ABILITY_OWN_TEMPO))
return FALSE;
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)
{
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(1.0);
}
@ -8543,7 +8476,7 @@ bool32 CanMegaEvolve(enum BattlerId battler)
return FALSE;
// 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;
// Check if battler is holding a Z-Crystal.
@ -8584,7 +8517,7 @@ bool32 CanUltraBurst(enum BattlerId battler)
return FALSE;
// 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;
enum Ability ability = GetBattlerAbility(battler);
@ -10057,7 +9990,7 @@ bool32 EmergencyExitCanBeTriggered(enum BattlerId battler)
&& HadMoreThanHalfHpNowDoesnt(battler)
&& (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER))
&& !(gBattleTypeFlags & BATTLE_TYPE_ARENA)
&& gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP)
&& gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP_TARGET)
return TRUE;
return FALSE;
@ -10259,6 +10192,14 @@ static bool32 CanMoveSkipAccuracyCheck(enum BattlerId battlerAtk, u32 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 effect = FALSE;
@ -10271,17 +10212,14 @@ bool32 CanMoveSkipAccuracyCalc(enum BattlerId battlerAtk, enum BattlerId battler
{
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
&& gBattleMons[battlerDef].volatiles.semiInvulnerable != STATE_COMMANDER
&& (moveEffect != EFFECT_SKY_DROP || gBattleStruct->skyDropTargets[battlerDef] == SKY_DROP_NO_TARGET))
&& !IsSkyDropInvolved(battlerDef, moveEffect))
{
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 (abilityDef == ABILITY_NO_GUARD
&& (moveEffect != EFFECT_SKY_DROP || gBattleStruct->skyDropTargets[battlerDef] == SKY_DROP_NO_TARGET))
else if (abilityDef == ABILITY_NO_GUARD && !IsSkyDropInvolved(battlerDef, moveEffect))
{
effect = TRUE;
ability = ABILITY_NO_GUARD;
@ -10569,7 +10507,8 @@ static bool32 CanBreakThroughSemiInvulnerablityInternal(enum BattlerId battlerAt
case STATE_UNDERWATER:
return MoveDamagesUnderWater(move);
case STATE_ON_AIR:
case STATE_SKY_DROP:
case STATE_SKY_DROP_ATTACKER:
case STATE_SKY_DROP_TARGET:
return MoveDamagesAirborne(move) || MoveDamagesAirborneDoubleDamage(move);
case STATE_PHANTOM_FORCE:
return FALSE;
@ -10593,6 +10532,19 @@ bool32 BreaksThroughSemiInvulnerableState(enum BattlerId battlerAtk, enum Battle
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)
{
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")
{
GIVEN {
ASSUME(B_RAMPAGE_CANCELLING >= GEN_5);
WITH_CONFIG(B_RAMPAGE_CONFUSION, GEN_5);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} 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")
{
GIVEN {
ASSUME(B_RAMPAGE_CANCELLING >= GEN_5);
WITH_CONFIG(B_RAMPAGE_CONFUSION, GEN_5);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
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));
}
}
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);
}
}