From aafc5814600d6f711eb41593996c75819e4a8e2a Mon Sep 17 00:00:00 2001 From: GGbond Date: Tue, 27 Jan 2026 22:44:42 +0800 Subject: [PATCH] Fix Psych Up wrong battler message and Gen 6+ crit ratio copying (#9015) --- data/battle_scripts_1.s | 1 + include/config/battle.h | 1 + include/constants/generational_changes.h | 1 + src/battle_script_commands.c | 10 +- test/battle/move_effect/psych_up.c | 120 ++++++++++++++++++++++- 5 files changed, 131 insertions(+), 2 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 7007410e15..37aa8e48b9 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -3767,6 +3767,7 @@ BattleScript_EffectBellyDrum:: BattleScript_EffectPsychUp:: attackcanceler + accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE copyfoestats attackanimation waitanimation diff --git a/include/config/battle.h b/include/config/battle.h index 9d2814cb85..0d02e2873a 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -90,6 +90,7 @@ #define B_MINIMIZE_EVASION GEN_LATEST // In Gen5+, Minimize raises evasion by 2 stages instead of 1. #define B_GROWTH_STAT_RAISE GEN_LATEST // In Gen5+, Growth raises Attack in addition to Special Attack by 1 stage each. Under the effects of the sun, it raises them by 2 stages each instead. #define B_FOCUS_ENERGY_CRIT_RATIO GEN_LATEST // In Gen3+, Focus Energy increases critical hit ratio by 2 instead of 1. +#define B_PSYCH_UP_CRIT_RATIO GEN_LATEST // In Gen6+, Psych Up also copies the target's critical hit ratio. // Other move settings #define B_INCINERATE_GEMS GEN_LATEST // In Gen6+, Incinerate can destroy Gems. diff --git a/include/constants/generational_changes.h b/include/constants/generational_changes.h index ad700a6ea2..37b2b3f357 100644 --- a/include/constants/generational_changes.h +++ b/include/constants/generational_changes.h @@ -84,6 +84,7 @@ F(MINIMIZE_EVASION, minimizeEvasion, (u32, GEN_COUNT - 1)) \ F(GROWTH_STAT_RAISE, growthStatRaise, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(FOCUS_ENERGY_CRIT_RATIO, focusEnergyCritRatio, (u32, GEN_COUNT - 1)) \ + F(PSYCH_UP_CRIT_RATIO, psychUpCritRatio, (u32, GEN_COUNT - 1)) \ /* Other move settings */ \ F(INCINERATE_GEMS, incinerateGems, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(CAN_SPITE_FAIL, canSpiteFail, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 1421eaf9e6..c25cdf14b6 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -12288,7 +12288,15 @@ static void Cmd_copyfoestats(void) { gBattleMons[gBattlerAttacker].statStages[i] = gBattleMons[gBattlerTarget].statStages[i]; } - gBattleScripting.battler = gBattlerTarget; + if (GetConfig(CONFIG_PSYCH_UP_CRIT_RATIO) >= GEN_6) + { + // Copy crit boosts (Focus Energy, Dragon Cheer, G-Max Chi Strike) + gBattleMons[gBattlerAttacker].volatiles.focusEnergy = gBattleMons[gBattlerTarget].volatiles.focusEnergy; + gBattleMons[gBattlerAttacker].volatiles.dragonCheer = gBattleMons[gBattlerTarget].volatiles.dragonCheer; + gBattleMons[gBattlerAttacker].volatiles.bonusCritStages = gBattleMons[gBattlerTarget].volatiles.bonusCritStages; + } + gEffectBattler = gBattlerTarget; + gBattleScripting.battler = gBattlerAttacker; gBattlescriptCurrInstr = cmd->nextInstr; } diff --git a/test/battle/move_effect/psych_up.c b/test/battle/move_effect/psych_up.c index 36241a4ed7..ca2ecd3f5a 100644 --- a/test/battle/move_effect/psych_up.c +++ b/test/battle/move_effect/psych_up.c @@ -1,4 +1,122 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Psych Up (Move Effect) test titles") +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_PSYCH_UP) == EFFECT_PSYCH_UP); +} + +SINGLE_BATTLE_TEST("Psych Up displays the correct battlers when used by the player") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_SWORDS_DANCE) == EFFECT_ATTACK_UP_2); + PLAYER(SPECIES_TORNADUS) { Speed(66); } + OPPONENT(SPECIES_LANDORUS) { Speed(99); } + } WHEN { + TURN { MOVE(opponent, MOVE_SWORDS_DANCE); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_PSYCH_UP); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, opponent); + MESSAGE("Tornadus copied the opposing Landorus's stat changes!"); + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], opponent->statStages[STAT_ATK]); + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2); + } +} + +SINGLE_BATTLE_TEST("Psych Up displays the correct battlers when used by the opponent") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_SWORDS_DANCE) == EFFECT_ATTACK_UP_2); + PLAYER(SPECIES_TORNADUS) { Speed(66); } + OPPONENT(SPECIES_LANDORUS) { Speed(99); } + } WHEN { + TURN { MOVE(player, MOVE_SWORDS_DANCE); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(opponent, MOVE_PSYCH_UP); MOVE(player, MOVE_CELEBRATE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, player); + MESSAGE("The opposing Landorus copied Tornadus's stat changes!"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], player->statStages[STAT_ATK]); + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2); + } +} + +SINGLE_BATTLE_TEST("Psych Up ignores Spiky Shield and Baneful Bunker but fails against Crafty Shield") +{ + u32 protectMove = MOVE_NONE; + bool32 shouldFail = FALSE; + + PARAMETRIZE { protectMove = MOVE_SPIKY_SHIELD; shouldFail = FALSE; } + PARAMETRIZE { protectMove = MOVE_BANEFUL_BUNKER; shouldFail = FALSE; } + PARAMETRIZE { protectMove = MOVE_CRAFTY_SHIELD; shouldFail = TRUE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_SWORDS_DANCE) == EFFECT_ATTACK_UP_2); + ASSUME(GetMoveEffect(MOVE_SPIKY_SHIELD) == EFFECT_PROTECT); + ASSUME(GetMoveEffect(MOVE_BANEFUL_BUNKER) == EFFECT_PROTECT); + ASSUME(GetMoveEffect(MOVE_CRAFTY_SHIELD) == EFFECT_PROTECT); + PLAYER(SPECIES_TORNADUS) { Speed(66); } + OPPONENT(SPECIES_LANDORUS) { Speed(99); } + } WHEN { + TURN { MOVE(opponent, MOVE_SWORDS_DANCE); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(opponent, protectMove); MOVE(player, MOVE_PSYCH_UP); } + } SCENE { + if (shouldFail) { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCH_UP, player); + MESSAGE("Tornadus copied the opposing Landorus's stat changes!"); + } + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCH_UP, player); + MESSAGE("Tornadus copied the opposing Landorus's stat changes!"); + } + } THEN { + if (shouldFail) { + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + } else { + EXPECT_EQ(player->statStages[STAT_ATK], opponent->statStages[STAT_ATK]); + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2); + } + } +} + +SINGLE_BATTLE_TEST("Psych Up does not copy the target's critical hit ratio (Gen5)") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_FOCUS_ENERGY) == EFFECT_FOCUS_ENERGY); + WITH_CONFIG(CONFIG_PSYCH_UP_CRIT_RATIO, GEN_5); + WITH_CONFIG(CONFIG_FOCUS_ENERGY_CRIT_RATIO, GEN_9); + PLAYER(SPECIES_TORNADUS) { Speed(66); } + OPPONENT(SPECIES_LANDORUS) { Speed(99); } + } WHEN { + TURN { MOVE(opponent, MOVE_FOCUS_ENERGY); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_PSYCH_UP); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCH_UP, player); + MESSAGE("Tornadus copied the opposing Landorus's stat changes!"); + } THEN { + EXPECT(opponent->volatiles.focusEnergy); + EXPECT(!player->volatiles.focusEnergy); + } +} + +SINGLE_BATTLE_TEST("Psych Up copies the target's critical hit ratio (Gen6+)") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_FOCUS_ENERGY) == EFFECT_FOCUS_ENERGY); + WITH_CONFIG(CONFIG_PSYCH_UP_CRIT_RATIO, GEN_6); + WITH_CONFIG(CONFIG_FOCUS_ENERGY_CRIT_RATIO, GEN_9); + PLAYER(SPECIES_TORNADUS) { Speed(66); } + OPPONENT(SPECIES_LANDORUS) { Speed(99); } + } WHEN { + TURN { MOVE(opponent, MOVE_FOCUS_ENERGY); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_PSYCH_UP); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCH_UP, player); + MESSAGE("Tornadus copied the opposing Landorus's stat changes!"); + } THEN { + EXPECT(opponent->volatiles.focusEnergy); + EXPECT(player->volatiles.focusEnergy); + } +}