From 76656e85c2f682f35d2504ee4a46c8c2f37dad0b Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Wed, 18 Sep 2024 23:10:29 +0100 Subject: [PATCH] Fix Quash implementation, adds After You and Quash missing configs + tests (#5400) * Fix Quash + After You and Quash configs * Add tests --- include/config/battle.h | 2 + src/battle_script_commands.c | 41 ++++++++----- test/battle/move_effect/after_you.c | 49 ++++++++++++++- test/battle/move_effect/quash.c | 92 ++++++++++++++++++++++++++++- 4 files changed, 163 insertions(+), 21 deletions(-) diff --git a/include/config/battle.h b/include/config/battle.h index 402b5db6cf..b00eb199a9 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -123,6 +123,8 @@ #define B_HEAL_BELL_SOUNDPROOF GEN_LATEST // In Gen5, Heal Bell affects all mons with Soundproof. In Gen6-8 it affects inactive mons, but not battlers. In Gen9 it always affects the user. #define B_CHARGE GEN_LATEST // In Gen8-, Charge status is lost regardless of the typing of the next move. #define B_POWDER_RAIN GEN_LATEST // In Gen7+, Powder doesn't damage the user of a Fire type move in heavy rain. +#define B_AFTER_YOU_TURN_ORDER GEN_LATEST // In Gen8+, After You doesn't fail if the turn order wouldn't change after use. +#define B_QUASH_TURN_ORDER GEN_LATEST // In Gen8+, Quash-affected battlers move according to speed order. Before Gen8, Quash-affected battlers move in the order they were affected by Quash. // Ability settings #define B_EXPANDED_ABILITY_NAMES TRUE // If TRUE, ability names are increased from 12 characters to 16 characters. diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 1e4b10e928..ee2a148831 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -8929,9 +8929,10 @@ static bool32 ChangeOrderTargetAfterAttacker(void) u8 data[MAX_BATTLERS_COUNT]; u8 actionsData[MAX_BATTLERS_COUNT]; - if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget) - || GetBattlerTurnOrderNum(gBattlerAttacker) + 1 == GetBattlerTurnOrderNum(gBattlerTarget)) + if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)) return FALSE; + if (GetBattlerTurnOrderNum(gBattlerAttacker) + 1 == GetBattlerTurnOrderNum(gBattlerTarget)) + return B_AFTER_YOU_TURN_ORDER >= GEN_8; for (i = 0; i < MAX_BATTLERS_COUNT; i++) { @@ -8944,8 +8945,6 @@ static bool32 ChangeOrderTargetAfterAttacker(void) gActionsByTurnOrder[1] = actionsData[2]; gBattlerByTurnOrder[2] = data[1]; gActionsByTurnOrder[2] = actionsData[1]; - gBattlerByTurnOrder[3] = data[3]; - gActionsByTurnOrder[3] = actionsData[3]; } else if (GetBattlerTurnOrderNum(gBattlerAttacker) == 0 && GetBattlerTurnOrderNum(gBattlerTarget) == 3) { @@ -17110,7 +17109,7 @@ void BS_TryActivateGulpMissile(void) void BS_TryQuash(void) { NATIVE_ARGS(const u8 *failInstr); - u32 i; + u32 i, j; // It's true if foe is faster, has a bigger priority, or switches if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)) @@ -17121,19 +17120,29 @@ void BS_TryQuash(void) // If the above condition is not true, it means we are faster than the foe, so we can set the quash bit gProtectStructs[gBattlerTarget].quash = TRUE; - for (i = 0; i < gBattlersCount; i++) + + if (B_QUASH_TURN_ORDER < GEN_8) { - gBattlerByTurnOrder[i] = i; - } - for (i = 0; i < gBattlersCount - 1; i++) - { - s32 j; - for (j = i + 1; j < gBattlersCount; j++) + // Gen 7- config makes target go last so that the order of quash targets is kept for the correct turn order + j = GetBattlerTurnOrderNum(gBattlerTarget); + for (i = j + 1; i < gBattlersCount; i++) { - if (!gProtectStructs[i].quash - && !gProtectStructs[j].quash - && GetWhichBattlerFaster(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], FALSE) == -1) - SwapTurnOrder(i, j); + SwapTurnOrder(i, j); + j++; + } + } + else + { + // Gen 8+ config only alters Turn Order of battlers affected by Quash, dynamic speed should handle the rest + for (i = gCurrentTurnActionNumber + 1; i < gBattlersCount - 1; i++) + { + for (j = i + 1; j < gBattlersCount; j++) + { + u32 battler1 = gBattlerByTurnOrder[i], battler2 = gBattlerByTurnOrder[j]; + if ((gProtectStructs[battler1].quash || gProtectStructs[battler2].quash) + && GetWhichBattlerFaster(battler1, battler2, FALSE) == -1) + SwapTurnOrder(i, j); + } } } gBattlescriptCurrInstr = cmd->nextInstr; diff --git a/test/battle/move_effect/after_you.c b/test/battle/move_effect/after_you.c index b788fab725..32ea44efb1 100644 --- a/test/battle/move_effect/after_you.c +++ b/test/battle/move_effect/after_you.c @@ -52,7 +52,7 @@ DOUBLE_BATTLE_TEST("After You does nothing if the target has already moved") } } -DOUBLE_BATTLE_TEST("After You calculates correct targets if only one pokemon is left on the opposing side") +DOUBLE_BATTLE_TEST("After You calculates correct turn order if only one pokemon is left on the opposing side") { GIVEN { PLAYER(SPECIES_GRENINJA) { Speed(120); } @@ -86,5 +86,48 @@ DOUBLE_BATTLE_TEST("After You calculates correct targets if only one pokemon is } } -TO_DO_BATTLE_TEST("After You doesn't fail if the turner remains the same after After You (Gen8+)"); -TO_DO_BATTLE_TEST("After You ignores the effects of Quash"); +DOUBLE_BATTLE_TEST("After You doesn't fail if the turn order remains the same after After You (Gen8+)") +{ + GIVEN { + ASSUME(B_AFTER_YOU_TURN_ORDER >= GEN_8); + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + PLAYER(SPECIES_WYNAUT) { Speed(1); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(2); } + OPPONENT(SPECIES_WYNAUT) { Speed(3); } + } WHEN { + TURN { + MOVE(playerLeft, MOVE_CELEBRATE); + MOVE(playerRight, MOVE_CELEBRATE); + MOVE(opponentLeft, MOVE_CELEBRATE); + MOVE(opponentRight, MOVE_AFTER_YOU, target: opponentLeft); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_AFTER_YOU, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); + } +} + +DOUBLE_BATTLE_TEST("After You ignores the effects of Quash") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_QUASH].effect == EFFECT_QUASH); + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + PLAYER(SPECIES_WYNAUT) { Speed(1); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(2); } + OPPONENT(SPECIES_WYNAUT) { Speed(3); } + } WHEN { + TURN { + MOVE(playerLeft, MOVE_QUASH, target: opponentLeft); + MOVE(playerRight, MOVE_CELEBRATE); + MOVE(opponentLeft, MOVE_CELEBRATE); + MOVE(opponentRight, MOVE_AFTER_YOU, target: opponentLeft); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUASH, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_AFTER_YOU, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); + } +} diff --git a/test/battle/move_effect/quash.c b/test/battle/move_effect/quash.c index fd2bd9d877..5500fcb33e 100644 --- a/test/battle/move_effect/quash.c +++ b/test/battle/move_effect/quash.c @@ -32,14 +32,102 @@ DOUBLE_BATTLE_TEST("Quash is not affected by dynamic speed") PLAYER(SPECIES_WOBBUFFET) { Speed(30); } OPPONENT(SPECIES_TORCHIC) { Speed(50); } OPPONENT(SPECIES_TREECKO) { Speed(40); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_QUASH, target: opponentLeft); + MOVE(opponentRight, MOVE_TAILWIND); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUASH, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft); + } +} + +DOUBLE_BATTLE_TEST("Quash calculates correct turn order if only one pokemon is left on the opposing side") +{ + GIVEN { + PLAYER(SPECIES_GRENINJA) { Speed(120); } + PLAYER(SPECIES_REGIROCK) { Speed(100); } + OPPONENT(SPECIES_PIDGEOT) { Speed(10); } + OPPONENT(SPECIES_DRAGONITE) { Speed(60); } + } WHEN { + TURN { + MOVE(playerLeft, MOVE_QUASH, target: playerRight); + MOVE(playerRight, MOVE_STONE_EDGE, target: opponentLeft); + MOVE(opponentRight, MOVE_CELEBRATE); + } + TURN { + MOVE(playerLeft, MOVE_QUASH, target: playerRight); + MOVE(playerRight, MOVE_STONE_EDGE, target: opponentRight); + MOVE(opponentRight, MOVE_CELEBRATE); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUASH, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_EDGE, playerRight); + HP_BAR(opponentLeft); + MESSAGE("Foe Pidgeot fainted!"); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUASH, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_EDGE, playerRight); + HP_BAR(opponentRight); + } +} + +DOUBLE_BATTLE_TEST("Quash-affected targets move from fastest to slowest (Gen 8+) or from first affected battler to last (Gen 7-)") +{ + u32 speedLeft, speedRight; + + PARAMETRIZE { speedLeft = 60; speedRight = 50; } + PARAMETRIZE { speedLeft = 50; speedRight = 60; } + GIVEN { + PLAYER(SPECIES_VOLBEAT) { Speed(10); Ability(ABILITY_PRANKSTER); } + PLAYER(SPECIES_WOBBUFFET) { Speed(70); } + OPPONENT(SPECIES_TORCHIC) { Speed(speedLeft); } + OPPONENT(SPECIES_TREECKO) { Speed(speedRight); } } WHEN { TURN { MOVE(playerLeft, MOVE_QUASH, target: opponentRight); + MOVE(playerRight, MOVE_QUASH, target: opponentLeft); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUASH, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUASH, playerRight); + if (B_QUASH_TURN_ORDER < GEN_8) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft); + } + else if (speedLeft > speedRight) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight); + } + else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft); + } + } +} + +DOUBLE_BATTLE_TEST("Quash-affected mon that acted early via After You is not affected by dynamic speed") +{ + GIVEN { + ASSUME(B_RECALC_TURN_AFTER_ACTIONS >= GEN_8); + ASSUME(gMovesInfo[MOVE_TAILWIND].effect == EFFECT_TAILWIND); + ASSUME(gMovesInfo[MOVE_AFTER_YOU].effect == EFFECT_AFTER_YOU); + PLAYER(SPECIES_VOLBEAT) { Speed(20); Ability(ABILITY_PRANKSTER); } + PLAYER(SPECIES_WOBBUFFET) { Speed(30); } + OPPONENT(SPECIES_TORCHIC) { Speed(10); } + OPPONENT(SPECIES_TREECKO) { Speed(40); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_QUASH, target: opponentLeft); + MOVE(opponentRight, MOVE_AFTER_YOU, target: opponentLeft); MOVE(opponentLeft, MOVE_TAILWIND); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_QUASH, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_AFTER_YOU, opponentRight); ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, opponentLeft); - ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); - ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); // this is the relevant part, testing if quash affected battler becomes last to move causing playerRight to not move } }