diff --git a/include/random.h b/include/random.h index 84318bedc0..df3d1c20be 100644 --- a/include/random.h +++ b/include/random.h @@ -167,6 +167,7 @@ enum RandomTag RNG_QUICK_DRAW, RNG_QUICK_CLAW, RNG_TRACE, + RNG_FOREWARN, RNG_FICKLE_BEAM, RNG_AI_ABILITY, RNG_AI_SWITCH_HASBADODDS, diff --git a/src/battle_util.c b/src/battle_util.c index bc97de9362..f79e0cee7b 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3335,12 +3335,43 @@ static void ForewarnChooseMove(u32 battler) } } - for (bestId = 0, i = 1; i < count; i++) + if (count == 0) { - if (data[i].power > data[bestId].power) - bestId = i; - else if (data[i].power == data[bestId].power && Random() & 1) + Free(data); + return; + } + + u32 tieCount = 1; + u8 bestPower = data[0].power; + + bestId = 0; + for (i = 1; i < count; i++) + { + if (data[i].power > bestPower) + { + bestPower = data[i].power; bestId = i; + tieCount = 1; + } + else if (data[i].power == bestPower) + { + tieCount++; + } + } + + if (tieCount > 1) + { + u32 tieIndex = RandomUniform(RNG_FOREWARN, 0, tieCount - 1); + for (i = 0, bestId = 0; i < count; i++) + { + if (data[i].power != bestPower) + continue; + if (tieIndex-- == 0) + { + bestId = i; + break; + } + } } gEffectBattler = data[bestId].battler; @@ -4155,7 +4186,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab } return effect; // Note: It returns effect as to not record the ability if Frisk does not activate. case ABILITY_FOREWARN: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (!gSpecialStatuses[battler].switchInAbilityDone && !IsOpposingSideEmpty(battler)) { ForewarnChooseMove(battler); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_FOREWARN; diff --git a/test/battle/ability/forewarn.c b/test/battle/ability/forewarn.c index 810fcd975f..dd1c99da99 100644 --- a/test/battle/ability/forewarn.c +++ b/test/battle/ability/forewarn.c @@ -1,4 +1,84 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Forewarn (Ability) test titles") +DOUBLE_BATTLE_TEST("Forewarn warns about the highest power move among all opposing battlers") +{ + GIVEN { + PLAYER(SPECIES_MUSHARNA) { Ability(ABILITY_FOREWARN); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_ZUBAT) { Moves(MOVE_CRUNCH, MOVE_CELEBRATE); } + OPPONENT(SPECIES_EXCADRILL) { Moves(MOVE_FISSURE, MOVE_CELEBRATE); } + } WHEN { + TURN {} + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_FOREWARN); + MESSAGE("Forewarn alerted Musharna to the opposing Excadrill's Fissure!"); + } +} + +SINGLE_BATTLE_TEST("Forewarn randomly chooses between same-power moves on one opponent") +{ + PASSES_RANDOMLY(1, 3, RNG_FOREWARN); + GIVEN { + ASSUME(GetMovePower(MOVE_TACKLE) == GetMovePower(MOVE_POUND)); + ASSUME(GetMovePower(MOVE_TACKLE) == GetMovePower(MOVE_SCRATCH)); + PLAYER(SPECIES_MUSHARNA) { Ability(ABILITY_FOREWARN); } + OPPONENT(SPECIES_ZUBAT) { Moves(MOVE_TACKLE, MOVE_POUND, MOVE_SCRATCH, MOVE_CELEBRATE); } + } WHEN { + TURN {} + } SCENE { + ABILITY_POPUP(player, ABILITY_FOREWARN); + MESSAGE("Forewarn alerted Musharna to the opposing Zubat's Tackle!"); + } +} + +DOUBLE_BATTLE_TEST("Forewarn randomly chooses between opponents with same-power moves") +{ + PASSES_RANDOMLY(1, 4, RNG_FOREWARN); + GIVEN { + ASSUME(GetMovePower(MOVE_TACKLE) == GetMovePower(MOVE_POUND)); + ASSUME(GetMovePower(MOVE_TACKLE) == GetMovePower(MOVE_SCRATCH)); + ASSUME(GetMovePower(MOVE_TACKLE) == GetMovePower(MOVE_QUICK_ATTACK)); + PLAYER(SPECIES_MUSHARNA) { Ability(ABILITY_FOREWARN); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_ZUBAT) { Moves(MOVE_TACKLE, MOVE_POUND, MOVE_PECK, MOVE_CELEBRATE); } + OPPONENT(SPECIES_EXCADRILL) { Moves(MOVE_SCRATCH, MOVE_QUICK_ATTACK, MOVE_ABSORB, MOVE_CELEBRATE); } + } WHEN { + TURN {} + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_FOREWARN); + MESSAGE("Forewarn alerted Musharna to the opposing Zubat's Tackle!"); + } +} + +DOUBLE_BATTLE_TEST("Forewarn does not trigger if a mon switches in while the opposing field is empty") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_U_TURN) == EFFECT_HIT_ESCAPE); + ASSUME(GetMoveEffect(MOVE_HEALING_WISH) == EFFECT_HEALING_WISH); + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_MUSHARNA) { Ability(ABILITY_FOREWARN); } + OPPONENT(SPECIES_WYNAUT) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_TREECKO); + OPPONENT(SPECIES_TORCHIC); + } WHEN { + TURN { + MOVE(opponentRight, MOVE_HEALING_WISH); + MOVE(playerLeft, MOVE_U_TURN, target: opponentLeft); + SEND_OUT(playerLeft, 2); + SEND_OUT(opponentLeft, 2); + SEND_OUT(opponentRight, 3); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEALING_WISH, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, playerLeft); + HP_BAR(opponentLeft); + NOT ABILITY_POPUP(playerLeft, ABILITY_FOREWARN); + MESSAGE("2 sent out Treecko!"); + MESSAGE("2 sent out Torchic!"); + NOT ABILITY_POPUP(playerLeft, ABILITY_FOREWARN); + } +}