diff --git a/include/battle_util.h b/include/battle_util.h index 0d48af95e5..2034ed3efb 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -407,7 +407,7 @@ void SetStartingStatus(enum StartingStatus status); void ResetStartingStatuses(void); bool32 IsUsableWhileAsleepEffect(enum BattleMoveEffects effect); void SetWrapTurns(enum BattlerId battler, enum HoldEffect holdEffect); -bool32 ChangeOrderTargetAfterAttacker(void); +bool32 ChangeOrderTargetAfterAttacker(enum BattlerId battlerDef); void TryUpdateEvolutionTracker(enum EvolutionConditions evolutionCondition, u32 upAmount, enum Move usedMove); bool32 CanUseMoveConsecutively(enum BattlerId battler); void TryResetConsecutiveUseCounter(enum BattlerId battler); diff --git a/src/battle_move_resolution.c b/src/battle_move_resolution.c index 27d2a85394..8cc5b67b03 100644 --- a/src/battle_move_resolution.c +++ b/src/battle_move_resolution.c @@ -3697,6 +3697,8 @@ static enum MoveEndResult MoveEndAbilityEffectFoesFainted(struct BattleCalcValue static enum MoveEndResult MoveEndShellTrap(struct BattleCalcValues *cv) { + u32 shellTrapBattlerMask = 0; + for (enum BattlerId battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { if (battlerDef == cv->battlerAtk || IsBattlerAlly(battlerDef, cv->battlerAtk)) @@ -3709,11 +3711,28 @@ static enum MoveEndResult MoveEndShellTrap(struct BattleCalcValues *cv) && gProtectStructs[battlerDef].physicalBattlerId == cv->battlerAtk) { gProtectStructs[battlerDef].shellTrap = TRUE; - if (IsDoubleBattle()) // Change move order in double battles, so the hit mon with shell trap moves immediately after being hit. - ChangeOrderTargetAfterAttacker(); // In what order should 2 targets move that will activate a trap? + shellTrapBattlerMask |= 1u << battlerDef; } } + if (IsDoubleBattle() && shellTrapBattlerMask != 0) + { + enum BattlerId shellTrapBattlers[MAX_BATTLERS_COUNT]; + u32 numShellTrapBattlers = 0; + + for (u32 i = 0; i < gBattlersCount; i++) + { + enum BattlerId battler = gBattlersBySpeed[i]; + + if (shellTrapBattlerMask & (1u << battler)) + shellTrapBattlers[numShellTrapBattlers++] = battler; + } + + // Each reorder inserts the battler immediately after the attacker, so use reverse turn order to keep faster battlers first. + while (numShellTrapBattlers != 0) + ChangeOrderTargetAfterAttacker(shellTrapBattlers[--numShellTrapBattlers]); + } + gBattleScripting.moveendState++; return MOVEEND_RESULT_CONTINUE; } @@ -4381,7 +4400,7 @@ static enum MoveEndResult MoveEndPursuitNextAction(struct BattleCalcValues *cv) u32 storedTarget = gBattlerTarget; if (SetTargetToNextPursuiter(gBattlerTarget)) { - ChangeOrderTargetAfterAttacker(); + ChangeOrderTargetAfterAttacker(gBattlerTarget); gBattleStruct->moveTarget[gBattlerTarget] = storedTarget; gBattlerTarget = storedTarget; } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 6cf6075e5d..fcb2209668 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -8468,7 +8468,7 @@ static void Cmd_jumpifnopursuitswitchdmg(void) if (SetTargetToNextPursuiter(gBattlerAttacker)) { - ChangeOrderTargetAfterAttacker(); + ChangeOrderTargetAfterAttacker(gBattlerTarget); gBattleStruct->battlerState[gBattlerAttacker].pursuitTarget = TRUE; gBattleStruct->pursuitStoredSwitch = gBattleStruct->monToSwitchIntoId[gBattlerAttacker]; gSpecialStatuses[gBattlerAttacker].queuedSwitch = NO_QUEUED_SWITCH; // Don't send out replacement before Pursuits @@ -13251,7 +13251,7 @@ void BS_PowerTrick(void) void BS_TryAfterYou(void) { NATIVE_ARGS(const u8 *failInstr); - if (ChangeOrderTargetAfterAttacker()) + if (ChangeOrderTargetAfterAttacker(gBattlerTarget)) { gSpecialStatuses[gBattlerTarget].afterYou = 1; gBattlescriptCurrInstr = cmd->nextInstr; diff --git a/src/battle_util.c b/src/battle_util.c index 28a786f9df..dfcb9330e1 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -10819,13 +10819,13 @@ void SetWrapTurns(enum BattlerId battler, enum HoldEffect holdEffect) } // Return True if the order was changed, and false if the order was not changed(for example because the target would move after the attacker anyway). -bool32 ChangeOrderTargetAfterAttacker(void) +bool32 ChangeOrderTargetAfterAttacker(enum BattlerId battlerDef) { u32 i; u8 data[MAX_BATTLERS_COUNT]; u8 actionsData[MAX_BATTLERS_COUNT]; u32 attackerTurnOrderNum = GetBattlerTurnOrderNum(gBattlerAttacker); - u32 targetTurnOrderNum = GetBattlerTurnOrderNum(gBattlerTarget); + u32 targetTurnOrderNum = GetBattlerTurnOrderNum(battlerDef); if (attackerTurnOrderNum > targetTurnOrderNum) return FALSE; @@ -10839,14 +10839,14 @@ bool32 ChangeOrderTargetAfterAttacker(void) } if (attackerTurnOrderNum == 0 && targetTurnOrderNum == 2) { - gBattlerByTurnOrder[1] = gBattlerTarget; + gBattlerByTurnOrder[1] = battlerDef; gActionsByTurnOrder[1] = actionsData[2]; gBattlerByTurnOrder[2] = data[1]; gActionsByTurnOrder[2] = actionsData[1]; } else if (attackerTurnOrderNum == 0 && targetTurnOrderNum == 3) { - gBattlerByTurnOrder[1] = gBattlerTarget; + gBattlerByTurnOrder[1] = battlerDef; gActionsByTurnOrder[1] = actionsData[3]; gBattlerByTurnOrder[2] = data[1]; gActionsByTurnOrder[2] = actionsData[1]; @@ -10855,7 +10855,7 @@ bool32 ChangeOrderTargetAfterAttacker(void) } else // attackerTurnOrderNum == 1, targetTurnOrderNum == 3 { - gBattlerByTurnOrder[2] = gBattlerTarget; + gBattlerByTurnOrder[2] = battlerDef; gActionsByTurnOrder[2] = actionsData[3]; gBattlerByTurnOrder[3] = data[2]; gActionsByTurnOrder[3] = actionsData[2]; diff --git a/test/battle/move_effect/shell_trap.c b/test/battle/move_effect/shell_trap.c index 3d8c457ab8..5a16e67533 100644 --- a/test/battle/move_effect/shell_trap.c +++ b/test/battle/move_effect/shell_trap.c @@ -280,14 +280,15 @@ DOUBLE_BATTLE_TEST("Shell Trap does not trigger when hit into Substitute") } } -DOUBLE_BATTLE_TEST("Shell Trap activates on both opposing Targets") +DOUBLE_BATTLE_TEST("Shell Trap activates for both opposing targets in speed order") { GIVEN { ASSUME(GetMoveTarget(MOVE_EARTHQUAKE) == TARGET_FOES_AND_ALLY); - PLAYER(SPECIES_WYNAUT); - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WYNAUT); - OPPONENT(SPECIES_WOBBUFFET); + ASSUME(GetMoveCategory(MOVE_EARTHQUAKE) == DAMAGE_CATEGORY_PHYSICAL); + PLAYER(SPECIES_WYNAUT) { Speed(1); } + PLAYER(SPECIES_WOBBUFFET) { Speed(2); } + OPPONENT(SPECIES_WYNAUT) { Speed(4); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(3); } } WHEN { TURN { MOVE(playerLeft, MOVE_SHELL_TRAP); @@ -296,9 +297,30 @@ DOUBLE_BATTLE_TEST("Shell Trap activates on both opposing Targets") } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, opponentLeft); - // Order might be incorrect compared to vanilla - ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_TRAP, playerLeft); ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_TRAP, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_TRAP, playerLeft); } } +DOUBLE_BATTLE_TEST("Shell Trap activates immediately after its user is hit by a spread move") +{ + GIVEN { + ASSUME(GetMoveTarget(MOVE_EARTHQUAKE) == TARGET_FOES_AND_ALLY); + ASSUME(GetMoveCategory(MOVE_EARTHQUAKE) == DAMAGE_CATEGORY_PHYSICAL); + PLAYER(SPECIES_WOBBUFFET) { Speed(3); } + PLAYER(SPECIES_WOBBUFFET) { Speed(1); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(4); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(2); } + } WHEN { + TURN { + MOVE(opponentLeft, MOVE_EARTHQUAKE); + MOVE(playerRight, MOVE_SHELL_TRAP); + } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SHELL_TRAP_SETUP, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_TRAP, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight); + } +}