Fixes Magic Bounce in double battles (#4464)

* Fixes Magic Bounce in double battles

* Add Double Battle check
This commit is contained in:
Alex 2024-05-02 14:23:46 +02:00 committed by GitHub
parent 2aed78ebbb
commit 6d397f9867
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 86 additions and 23 deletions

View file

@ -157,7 +157,6 @@ struct ProtectStruct
u32 flinchImmobility:1;
u32 notFirstStrike:1;
u32 palaceUnableToUseMove:1;
u32 usesBouncedMove:1;
u32 usedHealBlockedMove:1;
u32 usedGravityPreventedMove:1;
u32 powderSelfDmg:1;
@ -736,9 +735,10 @@ struct BattleStruct
u8 quickClawBattlerId;
struct LostItem itemLost[PARTY_SIZE]; // Player's team that had items consumed or stolen (two bytes per party member)
u8 forcedSwitch:4; // For each battler
u8 additionalEffectsCounter:4; // A counter for the additionalEffects applied by the current move in Cmd_setadditionaleffects
u8 blunderPolicy:1; // should blunder policy activate
u8 swapDamageCategory:1; // Photon Geyser, Shell Side Arm, Light That Burns the Sky
u8 additionalEffectsCounter:4; // A counter for the additionalEffects applied by the current move in Cmd_setadditionaleffects
u8 bouncedMoveIsUsed:1;
u8 ballSpriteIds[2]; // item gfx, window gfx
u8 appearedInBattle; // Bitfield to track which Pokemon appeared in battle. Used for Burmy's form change
u8 skyDropTargets[MAX_BATTLERS_COUNT]; // For Sky Drop, to account for if multiple Pokemon use Sky Drop in a double battle.

View file

@ -3581,7 +3581,6 @@ const u8* FaintClearSetData(u32 battler)
gProtectStructs[battler].flinchImmobility = FALSE;
gProtectStructs[battler].notFirstStrike = FALSE;
gProtectStructs[battler].usedHealBlockedMove = FALSE;
gProtectStructs[battler].usesBouncedMove = FALSE;
gProtectStructs[battler].usedGravityPreventedMove = FALSE;
gProtectStructs[battler].usedThroatChopPreventedMove = FALSE;
gProtectStructs[battler].statRaised = FALSE;

View file

@ -1394,9 +1394,9 @@ static void Cmd_attackcanceler(void)
if (gProtectStructs[gBattlerTarget].bounceMove
&& gMovesInfo[gCurrentMove].magicCoatAffected
&& !gProtectStructs[gBattlerAttacker].usesBouncedMove)
&& !gBattleStruct->bouncedMoveIsUsed)
{
gProtectStructs[gBattlerTarget].usesBouncedMove = TRUE;
gBattleStruct->bouncedMoveIsUsed = TRUE;
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
// Edge case for bouncing a powder move against a grass type pokemon.
SetAtkCancellerForCalledMove();
@ -1413,18 +1413,33 @@ static void Cmd_attackcanceler(void)
}
return;
}
else if (GetBattlerAbility(gBattlerTarget) == ABILITY_MAGIC_BOUNCE
&& gMovesInfo[gCurrentMove].magicCoatAffected
&& !gProtectStructs[gBattlerAttacker].usesBouncedMove)
else if (gMovesInfo[gCurrentMove].magicCoatAffected && !gBattleStruct->bouncedMoveIsUsed)
{
gProtectStructs[gBattlerTarget].usesBouncedMove = TRUE;
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
// Edge case for bouncing a powder move against a grass type pokemon.
SetAtkCancellerForCalledMove();
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_MagicCoatBounce;
gBattlerAbility = gBattlerTarget;
return;
u32 battler = gBattlerTarget;
if (GetBattlerAbility(gBattlerTarget) == ABILITY_MAGIC_BOUNCE)
{
battler = gBattlerTarget;
gBattleStruct->bouncedMoveIsUsed = TRUE;
}
else if (IsDoubleBattle()
&& gMovesInfo[gCurrentMove].target == MOVE_TARGET_OPPONENTS_FIELD
&& GetBattlerAbility(BATTLE_PARTNER(gBattlerTarget)) == ABILITY_MAGIC_BOUNCE)
{
gBattlerTarget = battler = BATTLE_PARTNER(gBattlerTarget);
gBattleStruct->bouncedMoveIsUsed = TRUE;
}
if (gBattleStruct->bouncedMoveIsUsed)
{
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
// Edge case for bouncing a powder move against a grass type pokemon.
SetAtkCancellerForCalledMove();
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_MagicCoatBounce;
gBattlerAbility = battler;
return;
}
}
// Z-moves and Max Moves bypass protection, but deal reduced damage (factored in AccumulateOtherModifiers)
@ -5920,9 +5935,10 @@ static void Cmd_moveend(void)
return;
}
// Check if the move used was actually a bounced move. If so, we need to go back to the original attacker and make sure, its move hits all 2 or 3 pokemon.
else if (gProtectStructs[gBattlerAttacker].usesBouncedMove)
else if (gBattleStruct->bouncedMoveIsUsed)
{
u8 originalBounceTarget = gBattlerAttacker;
gBattleStruct->bouncedMoveIsUsed = FALSE;
gBattlerAttacker = gBattleStruct->attackerBeforeBounce;
gBattleStruct->targetsDone[gBattlerAttacker] |= gBitTable[originalBounceTarget];
gBattleStruct->targetsDone[originalBounceTarget] = 0;
@ -6221,7 +6237,7 @@ static void Cmd_moveend(void)
if (!(gBattleStruct->lastMoveFailed & gBitTable[gBattlerAttacker]
|| (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove
&& gProtectStructs[gBattlerAttacker].usesBouncedMove)))
&& gBattleStruct->bouncedMoveIsUsed)))
{ // Dance move succeeds
// Set target for other Dancer mons; set bit so that mon cannot activate Dancer off of its own move
if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove)
@ -6316,7 +6332,6 @@ static void Cmd_moveend(void)
CancelMultiTurnMoves(gBattlerAttacker); // Cancel it
gBattleStruct->targetsDone[gBattlerAttacker] = 0;
gProtectStructs[gBattlerAttacker].usesBouncedMove = FALSE;
gProtectStructs[gBattlerAttacker].targetAffected = FALSE;
gProtectStructs[gBattlerAttacker].shellTrap = FALSE;
gBattleStruct->ateBoost[gBattlerAttacker] = 0;
@ -6333,6 +6348,7 @@ static void Cmd_moveend(void)
gBattleStruct->hitSwitchTargetFailed = FALSE;
gBattleStruct->isAtkCancelerForCalledMove = FALSE;
gBattleStruct->swapDamageCategory = FALSE;
gBattleStruct->bouncedMoveIsUsed = FALSE;
gBattleStruct->enduredDamage = 0;
gBattleStruct->additionalEffectsCounter = 0;
gBattleScripting.moveendState++;

View file

@ -3489,10 +3489,7 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
if (effect != 0)
gBattlescriptCurrInstr = BattleScript_PowderMoveNoEffect;
}
if (gProtectStructs[gBattlerAttacker].usesBouncedMove) // Edge case for bouncing a powder move against a grass type pokemon.
gBattleStruct->atkCancellerTracker = CANCELLER_END;
else
gBattleStruct->atkCancellerTracker++;
gBattleStruct->atkCancellerTracker++;
break;
case CANCELLER_POWDER_STATUS:
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_POWDER)

View file

@ -81,3 +81,54 @@ DOUBLE_BATTLE_TEST("Magic Bounce bounces back moves hitting both foes at two foe
MESSAGE("Foe Wynaut's Defense fell!");
}
}
DOUBLE_BATTLE_TEST("Magic Bounce bounces back moves hitting foes field")
{
u32 battlerOne, battlerTwo, abilityBattlerOne, abilityBattlerTwo;
PARAMETRIZE { battlerOne = SPECIES_NATU; abilityBattlerOne = ABILITY_MAGIC_BOUNCE;
battlerTwo = SPECIES_ESPEON; abilityBattlerTwo = ABILITY_SYNCHRONIZE; }
PARAMETRIZE { battlerOne = SPECIES_NATU; abilityBattlerOne = ABILITY_KEEN_EYE;
battlerTwo = SPECIES_ESPEON; abilityBattlerTwo = ABILITY_MAGIC_BOUNCE; }
GIVEN {
ASSUME(gMovesInfo[MOVE_STEALTH_ROCK].target == MOVE_TARGET_OPPONENTS_FIELD);
PLAYER(SPECIES_ABRA);
PLAYER(SPECIES_KADABRA);
OPPONENT(battlerOne) { Ability(abilityBattlerOne); }
OPPONENT(battlerTwo) { Ability(abilityBattlerTwo); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_STEALTH_ROCK); }
} SCENE {
if (abilityBattlerOne == ABILITY_MAGIC_BOUNCE)
ABILITY_POPUP(opponentLeft, ABILITY_MAGIC_BOUNCE);
else
ABILITY_POPUP(opponentRight, ABILITY_MAGIC_BOUNCE);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, playerLeft);
if (abilityBattlerOne == ABILITY_MAGIC_BOUNCE) {
MESSAGE("Abra's Stealth Rock was bounced back by Foe Natu's Magic Bounce!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponentLeft);
} else {
MESSAGE("Abra's Stealth Rock was bounced back by Foe Espeon's Magic Bounce!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponentRight);
}
}
}
SINGLE_BATTLE_TEST("Magic Bounce bounced back status moves can not be bounced back by Magic Bounce")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC);
PLAYER(SPECIES_ESPEON) { Ability(ABILITY_MAGIC_BOUNCE); }
OPPONENT(SPECIES_ESPEON) { Ability(ABILITY_MAGIC_BOUNCE); }
} WHEN {
TURN { MOVE(player, MOVE_TOXIC); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_MAGIC_BOUNCE);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player);
MESSAGE("Espeon's Toxic was bounced back by Foe Espeon's Magic Bounce!");
NOT ABILITY_POPUP(player, ABILITY_MAGIC_BOUNCE);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, opponent);
STATUS_ICON(player, badPoison: TRUE);
}
}