diff --git a/src/battle_anim_effects_1.c b/src/battle_anim_effects_1.c index d5efe499eb..1d50cee3d1 100644 --- a/src/battle_anim_effects_1.c +++ b/src/battle_anim_effects_1.c @@ -6642,6 +6642,79 @@ static void ReloadBattlerSprites(u32 battler, struct Pokemon *party) } } +static void TrySwapSkyDropTargets(u32 battlerAtk, u32 battlerPartner) +{ + u32 i, temp; + + // battlerAtk is using Ally Switch + // check if our partner is the target of sky drop + // If so, change that index to battlerAtk + for (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; \ + else if (gSideTimers[side].field == battlerPartner) \ + gSideTimers[side].field = battlerAtk; + +static void TrySwapStickyWebBattlerId(u32 battlerAtk, u32 battlerPartner) +{ + u32 atkSide = GetBattlerSide(battlerAtk); + u32 oppSide = GetBattlerSide(BATTLE_OPPOSITE(battlerAtk)); + + // not all of these are needed to be swapped, but are done so to be robust to anything in the future that might care about them + TRY_SIDE_TIMER_BATTLER_ID_SWAP(battlerAtk, battlerPartner, atkSide, reflectBattlerId); + TRY_SIDE_TIMER_BATTLER_ID_SWAP(battlerAtk, battlerPartner, atkSide, lightscreenBattlerId); + TRY_SIDE_TIMER_BATTLER_ID_SWAP(battlerAtk, battlerPartner, atkSide, mistBattlerId); + TRY_SIDE_TIMER_BATTLER_ID_SWAP(battlerAtk, battlerPartner, atkSide, safeguardBattlerId); + TRY_SIDE_TIMER_BATTLER_ID_SWAP(battlerAtk, battlerPartner, atkSide, auroraVeilBattlerId); + TRY_SIDE_TIMER_BATTLER_ID_SWAP(battlerAtk, battlerPartner, atkSide, tailwindBattlerId); + TRY_SIDE_TIMER_BATTLER_ID_SWAP(battlerAtk, battlerPartner, atkSide, luckyChantBattlerId); + + // if we've set sticky web on the opposing side, need to swap stickyWebBattlerId for mirror armor + TRY_SIDE_TIMER_BATTLER_ID_SWAP(battlerAtk, battlerPartner, oppSide, stickyWebBattlerId); +} +#undef TRY_SIDE_TIMER_BATTLER_ID_SWAP + +static void TrySwapWishBattlerIds(u32 battlerAtk, u32 battlerPartner) +{ + u32 i, temp; + u32 oppSide = GetBattlerSide(BATTLE_OPPOSITE(battlerAtk)); + + // if used future sight on opposing side, properly track who used it + if (gSideStatuses[oppSide] & SIDE_STATUS_FUTUREATTACK) { + for (i = 0; i < gBattlersCount; i++) { + if (IsAlly(i,battlerAtk)) + continue; // only on opposing side + if (gWishFutureKnock.futureSightBattlerIndex[i] == battlerAtk) { + // if target was attacked with future sight from us, now they'll be the partner slot + gWishFutureKnock.futureSightBattlerIndex[i] = battlerPartner; + gWishFutureKnock.futureSightPartyIndex[i] = gBattlerPartyIndexes[battlerPartner]; + break; + } else if (gWishFutureKnock.futureSightBattlerIndex[i] == battlerPartner) { + gWishFutureKnock.futureSightBattlerIndex[i] = battlerAtk; + gWishFutureKnock.futureSightPartyIndex[i] = gBattlerPartyIndexes[battlerAtk]; + break; + } + } + } + + // swap wish party indices + if (gWishFutureKnock.wishCounter[battlerAtk] > 0 + || gWishFutureKnock.wishCounter[battlerPartner] > 0) { + SWAP(gWishFutureKnock.wishPartyId[battlerAtk], gWishFutureKnock.wishPartyId[battlerPartner], temp); + } +} + static void AnimTask_AllySwitchDataSwap(u8 taskId) { s32 i, j; @@ -6692,6 +6765,10 @@ static void AnimTask_AllySwitchDataSwap(u8 taskId) SwitchTwoBattlersInParty(battlerAtk, battlerPartner); SWAP(gBattlerPartyIndexes[battlerAtk], gBattlerPartyIndexes[battlerPartner], temp); + TrySwapSkyDropTargets(battlerAtk, battlerPartner); + TrySwapStickyWebBattlerId(battlerAtk, battlerPartner); + TrySwapWishBattlerIds(battlerAtk, battlerPartner); + // For Snipe Shot and abilities Stalwart/Propeller Tail - keep the original target. for (i = 0; i < MAX_BATTLERS_COUNT; i++) { diff --git a/test/battle/move_effect/ally_switch.c b/test/battle/move_effect/ally_switch.c index c7aa52d7c5..974730f120 100644 --- a/test/battle/move_effect/ally_switch.c +++ b/test/battle/move_effect/ally_switch.c @@ -203,5 +203,79 @@ DOUBLE_BATTLE_TEST("Ally Switch works if ally used two-turn move like Dig") } } +DOUBLE_BATTLE_TEST("Ally switch swaps sky drop targets if being used by partner") +{ + u8 visibility; + GIVEN { + ASSUME(gMovesInfo[MOVE_SKY_DROP].effect == EFFECT_SKY_DROP); + PLAYER(SPECIES_FEAROW) { Speed(100); } + PLAYER(SPECIES_XATU) { Speed(150); } + OPPONENT(SPECIES_ARON) { Speed(25); Ability(ABILITY_STURDY); } + OPPONENT(SPECIES_WYNAUT) { Speed(30); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_SKY_DROP, target: opponentLeft); } + TURN { MOVE(playerRight, MOVE_ALLY_SWITCH); SKIP_TURN(playerLeft); MOVE(opponentRight, MOVE_MUD_SPORT); MOVE(opponentLeft, MOVE_IRON_DEFENSE); } + } SCENE { + MESSAGE("Fearow used Sky Drop!"); + MESSAGE("Fearow took the opposing Aron into the sky!"); + // turn 2 + MESSAGE("Xatu used Ally Switch!"); + MESSAGE("Xatu and Fearow switched places!"); + MESSAGE("Fearow used Sky Drop!"); + HP_BAR(opponentLeft); + MESSAGE("The opposing Wynaut used Mud Sport!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MUD_SPORT, opponentRight); + MESSAGE("The opposing Aron used Iron Defense!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_IRON_DEFENSE, opponentLeft); + } THEN { + // all battlers should be visible + visibility = gBattleSpritesDataPtr->battlerData[0].invisible; + EXPECT_EQ(visibility, 0); + visibility = gBattleSpritesDataPtr->battlerData[1].invisible; + EXPECT_EQ(visibility, 0); + visibility = gBattleSpritesDataPtr->battlerData[2].invisible; + EXPECT_EQ(visibility, 0); + visibility = gBattleSpritesDataPtr->battlerData[3].invisible; + EXPECT_EQ(visibility, 0); + } +} + +DOUBLE_BATTLE_TEST("Ally switch swaps opposing sky drop targets if partner is being held in the air") +{ + u8 visibility; + GIVEN { + ASSUME(gMovesInfo[MOVE_SKY_DROP].effect == EFFECT_SKY_DROP); + PLAYER(SPECIES_ARON) { Speed(25); Ability(ABILITY_STURDY); } + PLAYER(SPECIES_WYNAUT) { Speed(30); } + OPPONENT(SPECIES_FEAROW) { Speed(100); } + OPPONENT(SPECIES_XATU) { Speed(150); } + } WHEN { + TURN { MOVE(opponentLeft, MOVE_SKY_DROP, target: playerLeft); } + TURN { MOVE(opponentRight, MOVE_ALLY_SWITCH); SKIP_TURN(opponentLeft); MOVE(playerRight, MOVE_MUD_SPORT); MOVE(playerLeft, MOVE_IRON_DEFENSE); } + } SCENE { + MESSAGE("The opposing Fearow used Sky Drop!"); + MESSAGE("The opposing Fearow took Aron into the sky!"); + // turn 2 + MESSAGE("The opposing Xatu used Ally Switch!"); + MESSAGE("The opposing Xatu and the opposing Fearow switched places!"); + MESSAGE("The opposing Fearow used Sky Drop!"); + HP_BAR(playerLeft); + MESSAGE("Wynaut used Mud Sport!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MUD_SPORT, playerRight); + MESSAGE("Aron used Iron Defense!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_IRON_DEFENSE, playerLeft); + } THEN { + // all battlers should be visible + visibility = gBattleSpritesDataPtr->battlerData[0].invisible; + EXPECT_EQ(visibility, 0); + visibility = gBattleSpritesDataPtr->battlerData[1].invisible; + EXPECT_EQ(visibility, 0); + visibility = gBattleSpritesDataPtr->battlerData[2].invisible; + EXPECT_EQ(visibility, 0); + visibility = gBattleSpritesDataPtr->battlerData[3].invisible; + EXPECT_EQ(visibility, 0); + } +} + // Triple Battles required to test //TO_DO_BATTLE_TEST("Ally Switch fails if the user is in the middle of the field in a Triple Battle"); diff --git a/test/battle/move_effect/sticky_web.c b/test/battle/move_effect/sticky_web.c index f1fff0fd1e..e99368aa7d 100644 --- a/test/battle/move_effect/sticky_web.c +++ b/test/battle/move_effect/sticky_web.c @@ -271,3 +271,31 @@ SINGLE_BATTLE_TEST("Sticky Web is placed on the correct side after Memento") MESSAGE("A sticky web has been laid out on the ground around your team!"); } } + +DOUBLE_BATTLE_TEST("Sticky Web setter has their speed lowered with Mirror Armor even after Ally Switch") +{ + GIVEN { + PLAYER(SPECIES_SQUIRTLE); + PLAYER(SPECIES_CHARMANDER); + PLAYER(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); Item(ITEM_IRON_BALL); } // Iron Ball, so that flying type Corviknight is affected by Sticky Web. + OPPONENT(SPECIES_CATERPIE); + OPPONENT(SPECIES_NATU); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_STICKY_WEB); } + TURN { MOVE(opponentRight, MOVE_ALLY_SWITCH); } + TURN { SWITCH(playerRight, 2); } + } SCENE { + // Turn 1 - set up sticky web + ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, opponentLeft); + MESSAGE("A sticky web has been laid out on the ground around your team!"); + // Turn 2 - ally switch + MESSAGE("The opposing Natu used Ally Switch!"); + // turn 3 - send our corviknight + SEND_IN_MESSAGE("Corviknight"); + MESSAGE("Corviknight was caught in a sticky web!"); + ABILITY_POPUP(playerRight, ABILITY_MIRROR_ARMOR); + // sticky web setter - caterpie (now opponentRight) gets speed lowered + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + MESSAGE("The opposing Caterpie's Speed fell!"); + } +}