From 7b741c77f0bf80219bb9e2d59936bd456e5601f2 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Wed, 17 Dec 2025 06:44:44 -0300 Subject: [PATCH] Added Light Ball tests (#8526) --- include/constants/generational_changes.h | 2 +- src/battle_dome.c | 2 +- src/battle_util.c | 2 +- test/battle/hold_effect/light_ball.c | 97 +++++++++++++++++++++++- test/battle/move_effect/acrobatics.c | 2 +- test/battle/move_effect/retaliate.c | 10 +-- 6 files changed, 105 insertions(+), 10 deletions(-) diff --git a/include/constants/generational_changes.h b/include/constants/generational_changes.h index 96c917840c..f94b7e8309 100644 --- a/include/constants/generational_changes.h +++ b/include/constants/generational_changes.h @@ -10,7 +10,7 @@ F(CONFUSION_SELF_DMG_CHANCE, confusionSelfDmgChance, (u32, GEN_COUNT - 1)) \ F(MULTI_HIT_CHANCE, multiHitChance, (u32, GEN_COUNT - 1)) \ F(WHITEOUT_MONEY, whiteoutMoney, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ - F(LIGHT_BALL_ATTACK_BOOST, lightBallAttackBoost, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ + F(LIGHT_BALL_ATTACK_BOOST, lightBallAttackBoost, (u32, GEN_COUNT - 1)) \ /* Experience settings */ \ F(EXP_CATCH, expCatch, (u32, GEN_COUNT - 1)) \ F(TRAINER_EXP_MULTIPLIER, trainerExpMultiplier, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ diff --git a/src/battle_dome.c b/src/battle_dome.c index df96737f8d..30075fad88 100644 --- a/src/battle_dome.c +++ b/src/battle_dome.c @@ -5158,7 +5158,7 @@ static u16 GetWinningMove(int winnerTournamentId, int loserTournamentId, u8 roun typeMultiplier = CalcPartyMonTypeEffectivenessMultiplier(moves[i * 4 + j], targetSpecies, targetAbility); if (typeMultiplier == UQ_4_12(0)) moveScores[i * MAX_MON_MOVES + j] += 0; - else if (typeMultiplier >= UQ_4_12(2)) + else if (typeMultiplier >= UQ_4_12(2.0)) moveScores[i * MAX_MON_MOVES + j] += movePower * 2; else if (typeMultiplier <= UQ_4_12(0.5)) moveScores[i * MAX_MON_MOVES + j] += movePower / 2; diff --git a/src/battle_util.c b/src/battle_util.c index da74fe3b7f..888580f3fd 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -7876,7 +7876,7 @@ static inline u32 CalcAttackStat(struct DamageContext *ctx) modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0)); break; case HOLD_EFFECT_LIGHT_BALL: - if (atkBaseSpeciesId == SPECIES_PIKACHU && (B_LIGHT_BALL_ATTACK_BOOST >= GEN_4 || IsBattleMoveSpecial(move))) + if (atkBaseSpeciesId == SPECIES_PIKACHU && (GetConfig(CONFIG_LIGHT_BALL_ATTACK_BOOST) >= GEN_4 || IsBattleMoveSpecial(move))) modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0)); break; case HOLD_EFFECT_CHOICE_BAND: diff --git a/test/battle/hold_effect/light_ball.c b/test/battle/hold_effect/light_ball.c index 8c0d507b45..c35dd75343 100644 --- a/test/battle/hold_effect/light_ball.c +++ b/test/battle/hold_effect/light_ball.c @@ -1,4 +1,99 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Light Ball (Hold Effect) test titles") +ASSUMPTIONS +{ + ASSUME(GetItemHoldEffect(ITEM_LIGHT_BALL) == HOLD_EFFECT_LIGHT_BALL); +} + +static const u32 speciesToCheck[] = { + SPECIES_PICHU, + SPECIES_PIKACHU, + SPECIES_PIKACHU_COSPLAY, + SPECIES_PIKACHU_ROCK_STAR, + SPECIES_PIKACHU_BELLE, + SPECIES_PIKACHU_POP_STAR, + SPECIES_PIKACHU_PHD, + SPECIES_PIKACHU_LIBRE, + SPECIES_PIKACHU_ORIGINAL, + SPECIES_PIKACHU_HOENN, + SPECIES_PIKACHU_SINNOH, + SPECIES_PIKACHU_UNOVA, + SPECIES_PIKACHU_KALOS, + SPECIES_PIKACHU_ALOLA, + SPECIES_PIKACHU_PARTNER, + SPECIES_PIKACHU_WORLD, + SPECIES_PIKACHU_GMAX, +}; + +SINGLE_BATTLE_TEST("Light Ball doubles Pikachu's Special Attack", s16 damage) +{ + u32 species = 0, item = 0; + + for (u32 j = 0; j < ARRAY_COUNT(speciesToCheck); j++) { + PARAMETRIZE { item = ITEM_NONE; species = speciesToCheck[j]; } + PARAMETRIZE { item = ITEM_LIGHT_BALL; species = speciesToCheck[j]; } + } + + GIVEN { + ASSUME(GetMoveCategory(MOVE_THUNDERSHOCK) == DAMAGE_CATEGORY_SPECIAL); + if (species == SPECIES_PIKACHU_GMAX) { + PLAYER(SPECIES_PIKACHU) { Item(item); GigantamaxFactor(TRUE); } + } else { + PLAYER(species) { Item(item); } + } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (species == SPECIES_PIKACHU_GMAX) { + TURN { MOVE(player, MOVE_THUNDERSHOCK, gimmick: GIMMICK_DYNAMAX); } + } else { + TURN { MOVE(player, MOVE_THUNDERSHOCK); } + } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } THEN { + if (i == 1) { // First check to avoid boosting other species + EXPECT_EQ(results[i - 1].damage, results[i].damage); + } else if (i % 2 == 1) { // Every 2nd test afterwards + EXPECT_MUL_EQ(results[i - 1].damage, Q_4_12(2.0), results[i].damage); + } + } +} + +SINGLE_BATTLE_TEST("Light Ball doubles Pikachu's Attack (Gen4+)", s16 damage) +{ + u32 species = 0, item = 0, config = 0; + + for (u32 j = 0; j < ARRAY_COUNT(speciesToCheck); j++) { + PARAMETRIZE { item = ITEM_NONE; config = GEN_3; species = speciesToCheck[j]; } + PARAMETRIZE { item = ITEM_LIGHT_BALL; config = GEN_3; species = speciesToCheck[j]; } + PARAMETRIZE { item = ITEM_LIGHT_BALL; config = GEN_4; species = speciesToCheck[j]; } + } + + GIVEN { + WITH_CONFIG(CONFIG_LIGHT_BALL_ATTACK_BOOST, config); + ASSUME(GetMoveCategory(MOVE_SPARK) == DAMAGE_CATEGORY_PHYSICAL); + if (species == SPECIES_PIKACHU_GMAX) { + PLAYER(SPECIES_PIKACHU) { Item(item); GigantamaxFactor(TRUE); } + } else { + PLAYER(species) { Item(item); } + } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (species == SPECIES_PIKACHU_GMAX) { + TURN { MOVE(player, MOVE_SPARK, gimmick: GIMMICK_DYNAMAX); } + } else { + TURN { MOVE(player, MOVE_SPARK); } + } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } THEN { + if (i == 2) { // First check to avoid boosting other species + EXPECT_EQ(results[i - 2].damage, results[i].damage); // No item vs Light Ball + EXPECT_EQ(results[i - 1].damage, results[i].damage); // Gen 3 vs Gen 4 + } else if (i % 3 == 2) { // Every 3rd test afterwards + EXPECT_MUL_EQ(results[i - 2].damage, Q_4_12(2.0), results[i].damage); // No item vs Light Ball + EXPECT_MUL_EQ(results[i - 1].damage, Q_4_12(2.0), results[i].damage); // Gen 3 vs Gen 4 + } + } +} diff --git a/test/battle/move_effect/acrobatics.c b/test/battle/move_effect/acrobatics.c index 1229d4e2c5..916580d90b 100644 --- a/test/battle/move_effect/acrobatics.c +++ b/test/battle/move_effect/acrobatics.c @@ -20,7 +20,7 @@ SINGLE_BATTLE_TEST("Acrobatics doubles in power if the user has no held item", s } SCENE { HP_BAR(player, captureDamage: &results[i].damage); } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(2), results[1].damage); + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage); } } diff --git a/test/battle/move_effect/retaliate.c b/test/battle/move_effect/retaliate.c index 47b1c3325d..850340858c 100644 --- a/test/battle/move_effect/retaliate.c +++ b/test/battle/move_effect/retaliate.c @@ -21,7 +21,7 @@ SINGLE_BATTLE_TEST("Retaliate doubles in base power the turn after an ally faint HP_BAR(opponent, captureDamage: &damage[0]); HP_BAR(opponent, captureDamage: &damage[1]); } THEN { - EXPECT_MUL_EQ(damage[1], Q_4_12(2), damage[0]); + EXPECT_MUL_EQ(damage[1], Q_4_12(2.0), damage[0]); } } @@ -40,7 +40,7 @@ SINGLE_BATTLE_TEST("Retaliate doubles in base power the turn after an ally faint HP_BAR(player, captureDamage: &damage[0]); HP_BAR(player, captureDamage: &damage[1]); } THEN { - EXPECT_MUL_EQ(damage[1], Q_4_12(2), damage[0]); + EXPECT_MUL_EQ(damage[1], Q_4_12(2.0), damage[0]); } } @@ -92,7 +92,7 @@ DOUBLE_BATTLE_TEST("Retaliate works with passive damage") HP_BAR(opponentRight, captureDamage: &damage[0]); HP_BAR(opponentRight, captureDamage: &damage[1]); } THEN { - EXPECT_MUL_EQ(damage[1], Q_4_12(2), damage[0]); + EXPECT_MUL_EQ(damage[1], Q_4_12(2.0), damage[0]); } } @@ -115,7 +115,7 @@ SINGLE_BATTLE_TEST("Retaliate works with Perish Song") HP_BAR(opponent, captureDamage: &damage[0]); HP_BAR(opponent, captureDamage: &damage[1]); } THEN { - EXPECT_MUL_EQ(damage[1], Q_4_12(2), damage[0]); + EXPECT_MUL_EQ(damage[1], Q_4_12(2.0), damage[0]); } } @@ -135,6 +135,6 @@ SINGLE_BATTLE_TEST("Retaliate works with self-inflicted fainting") HP_BAR(opponent, captureDamage: &damage[0]); HP_BAR(opponent, captureDamage: &damage[1]); } THEN { - EXPECT_MUL_EQ(damage[1], Q_4_12(2), damage[0]); + EXPECT_MUL_EQ(damage[1], Q_4_12(2.0), damage[0]); } }