Fix Anticipation type effectiveness check (#7840)
Some checks are pending
CI / build (push) Waiting to run
CI / allcontributors (push) Waiting to run
Docs / deploy (push) Waiting to run

This commit is contained in:
spindrift64 2025-10-16 14:36:07 +02:00 committed by GitHub
parent f6c6ed6956
commit de3c031fc5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 37 additions and 26 deletions

View File

@ -165,7 +165,8 @@ struct DamageContext
u32 isCrit:1;
u32 randomFactor:1;
u32 updateFlags:1;
u32 padding1:2;
u32 isAnticipation:1;
u32 padding1:1;
u32 weather:16;
u32 fixedBasePower:8;
u32 padding2:8;

View File

@ -3690,8 +3690,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITY_ANTICIPATION:
if (!gSpecialStatuses[battler].switchInAbilityDone)
{
u32 types[3];
GetBattlerTypes(battler, FALSE, types);
struct DamageContext ctx = {0};
uq4_12_t modifier = UQ_4_12(1.0);
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
{
if (IsBattlerAlive(i) && !IsBattlerAlly(i, battler))
@ -3702,9 +3702,14 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
enum BattleMoveEffects moveEffect = GetMoveEffect(move);
moveType = GetBattleMoveType(move);
if (GetTypeModifier(moveType, types[0]) >= UQ_4_12(2.0)
|| (types[0] != types[1] && GetTypeModifier(moveType, types[1]) >= UQ_4_12(2.0))
|| (types[2] != TYPE_MYSTERY && GetTypeModifier(moveType, types[2]) >= UQ_4_12(2.0))
ctx.battlerAtk = i;
ctx.battlerDef = battler;
ctx.move = move;
ctx.moveType = moveType;
ctx.isAnticipation = TRUE;
modifier = CalcTypeEffectivenessMultiplier(&ctx);
if (modifier >= UQ_4_12(2.0)
|| moveEffect == EFFECT_OHKO
|| moveEffect == EFFECT_SHEER_COLD)
{
@ -7639,13 +7644,13 @@ enum IronBallCheck
};
// Only called directly when calculating damage type effectiveness, and Iron Ball's type effectiveness mechanics
static bool32 IsBattlerGroundedInverseCheck(u32 battler, u32 ability, enum InverseBattleCheck checkInverse, enum IronBallCheck checkIronBall)
static bool32 IsBattlerGroundedInverseCheck(u32 battler, u32 ability, enum InverseBattleCheck checkInverse, enum IronBallCheck checkIronBall, bool32 isAnticipation)
{
enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler, TRUE);
if (!(checkIronBall == IGNORE_IRON_BALL) && holdEffect == HOLD_EFFECT_IRON_BALL)
return TRUE;
if (gFieldStatuses & STATUS_FIELD_GRAVITY)
if (gFieldStatuses & STATUS_FIELD_GRAVITY && isAnticipation == FALSE)
return TRUE;
if (B_ROOTED_GROUNDING >= GEN_4 && gBattleMons[battler].volatiles.root)
return TRUE;
@ -7666,7 +7671,7 @@ static bool32 IsBattlerGroundedInverseCheck(u32 battler, u32 ability, enum Inver
bool32 IsBattlerGrounded(u32 battler)
{
return IsBattlerGroundedInverseCheck(battler, GetBattlerAbility(battler), NOT_INVERSE_BATTLE, CHECK_IRON_BALL);
return IsBattlerGroundedInverseCheck(battler, GetBattlerAbility(battler), NOT_INVERSE_BATTLE, CHECK_IRON_BALL, FALSE);
}
u32 GetMoveSlot(u16 *moves, u32 move)
@ -9522,7 +9527,7 @@ static inline void MulByTypeEffectiveness(struct DamageContext *ctx, uq4_12_t *m
if (ctx->moveType == TYPE_PSYCHIC && defType == TYPE_DARK && gBattleMons[ctx->battlerDef].volatiles.miracleEye && mod == UQ_4_12(0.0))
mod = UQ_4_12(1.0);
if (GetMoveEffect(ctx->move) == EFFECT_SUPER_EFFECTIVE_ON_ARG && defType == GetMoveArgType(ctx->move))
if (GetMoveEffect(ctx->move) == EFFECT_SUPER_EFFECTIVE_ON_ARG && defType == GetMoveArgType(ctx->move) && !ctx->isAnticipation)
mod = UQ_4_12(2.0);
if (ctx->moveType == TYPE_GROUND && defType == TYPE_FLYING && IsBattlerGrounded(ctx->battlerDef) && mod == UQ_4_12(0.0))
mod = UQ_4_12(1.0);
@ -9530,7 +9535,7 @@ static inline void MulByTypeEffectiveness(struct DamageContext *ctx, uq4_12_t *m
mod = UQ_4_12(2.0);
// B_WEATHER_STRONG_WINDS weakens Super Effective moves against Flying-type Pokémon
if (gBattleWeather & B_WEATHER_STRONG_WINDS && HasWeatherEffect())
if (gBattleWeather & B_WEATHER_STRONG_WINDS && HasWeatherEffect() && !ctx->isAnticipation)
{
if (defType == TYPE_FLYING && mod >= UQ_4_12(2.0))
mod = UQ_4_12(1.0);
@ -9621,7 +9626,7 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(struct DamageCont
if (B_GLARE_GHOST < GEN_4 && ctx->move == MOVE_GLARE && IS_BATTLER_OF_TYPE(ctx->battlerDef, TYPE_GHOST))
modifier = UQ_4_12(0.0);
}
else if (ctx->moveType == TYPE_GROUND && !IsBattlerGroundedInverseCheck(ctx->battlerDef, ctx->abilityDef, INVERSE_BATTLE, CHECK_IRON_BALL) && !(MoveIgnoresTypeIfFlyingAndUngrounded(ctx->move)))
else if (ctx->moveType == TYPE_GROUND && !IsBattlerGroundedInverseCheck(ctx->battlerDef, ctx->abilityDef, INVERSE_BATTLE, CHECK_IRON_BALL, ctx->isAnticipation) && !(MoveIgnoresTypeIfFlyingAndUngrounded(ctx->move)))
{
modifier = UQ_4_12(0.0);
if (ctx->updateFlags && ctx->abilityDef == ABILITY_LEVITATE)
@ -9651,7 +9656,7 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(struct DamageCont
&& ctx->moveType == TYPE_GROUND
&& IS_BATTLER_OF_TYPE(ctx->battlerDef, TYPE_FLYING)
&& GetBattlerHoldEffect(ctx->battlerDef, TRUE) == HOLD_EFFECT_IRON_BALL
&& !IsBattlerGroundedInverseCheck(ctx->battlerDef, ctx->abilityDef, NOT_INVERSE_BATTLE, IGNORE_IRON_BALL)
&& !IsBattlerGroundedInverseCheck(ctx->battlerDef, ctx->abilityDef, NOT_INVERSE_BATTLE, IGNORE_IRON_BALL, FALSE)
&& !FlagGet(B_FLAG_INVERSE_BATTLE))
{
modifier = UQ_4_12(1.0);
@ -9685,7 +9690,7 @@ uq4_12_t CalcTypeEffectivenessMultiplier(struct DamageContext *ctx)
if (ctx->move != MOVE_STRUGGLE && ctx->moveType != TYPE_MYSTERY)
{
modifier = CalcTypeEffectivenessMultiplierInternal(ctx, modifier);
if (GetMoveEffect(ctx->move) == EFFECT_TWO_TYPED_MOVE)
if (GetMoveEffect(ctx->move) == EFFECT_TWO_TYPED_MOVE && !ctx->isAnticipation)
{
ctx->moveType = GetMoveArgType(ctx->move);
modifier = CalcTypeEffectivenessMultiplierInternal(ctx, modifier);

View File

@ -16,6 +16,18 @@ SINGLE_BATTLE_TEST("Anticipation causes notifies if an opponent has a super-effe
}
}
SINGLE_BATTLE_TEST("Anticipation does not trigger even when a move is super effective on only 1 type")
{
GIVEN {
PLAYER(SPECIES_WHISCASH) { Ability(ABILITY_ANTICIPATION); }
OPPONENT(SPECIES_PIKACHU) { Moves(MOVE_CELEBRATE, MOVE_THUNDERBOLT); }
} WHEN {
TURN { }
} SCENE {
NOT ABILITY_POPUP(player, ABILITY_ANTICIPATION);
}
}
SINGLE_BATTLE_TEST("Anticipation causes notifies if an opponent has a One-hit KO move")
{
GIVEN {
@ -59,28 +71,21 @@ SINGLE_BATTLE_TEST("Anticipation doesn't consider Normalize into their effective
SINGLE_BATTLE_TEST("Anticipation doesn't consider Scrappy into their effectiveness (Gen5+)")
{
KNOWN_FAILING;
GIVEN {
ASSUME(GetMoveType(MOVE_CLOSE_COMBAT) == TYPE_FIGHTING);
ASSUME(GetSpeciesType(SPECIES_EEVEE, 0) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_EEVEE, 1) == TYPE_NORMAL);
PLAYER(SPECIES_EEVEE) { Ability(ABILITY_ANTICIPATION); }
OPPONENT(SPECIES_KANGASKHAN) { Ability(ABILITY_SCRAPPY); Moves(MOVE_CLOSE_COMBAT, MOVE_TRICK_OR_TREAT, MOVE_SKILL_SWAP, MOVE_CELEBRATE); }
ASSUME(GetSpeciesType(SPECIES_DOUBLADE, 0) == TYPE_STEEL);
ASSUME(GetSpeciesType(SPECIES_DOUBLADE, 1) == TYPE_GHOST);
PLAYER(SPECIES_DOUBLADE) { Ability(ABILITY_ANTICIPATION); }
OPPONENT(SPECIES_KANGASKHAN) { Ability(ABILITY_SCRAPPY); Moves(MOVE_CLOSE_COMBAT, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(opponent, MOVE_TRICK_OR_TREAT); MOVE(player, MOVE_SKILL_SWAP); }
TURN { MOVE(opponent, MOVE_SKILL_SWAP); }
TURN { }
} SCENE {
ABILITY_POPUP(player, ABILITY_ANTICIPATION);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRICK_OR_TREAT, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, opponent);
NOT ABILITY_POPUP(player, ABILITY_ANTICIPATION);
}
}
SINGLE_BATTLE_TEST("Anticipation doesn't consider Gravity into their effectiveness (Gen5+)")
{
KNOWN_FAILING;
GIVEN {
PLAYER(SPECIES_SKARMORY);
OPPONENT(SPECIES_EEVEE) { Ability(ABILITY_ANTICIPATION); Moves(MOVE_EARTHQUAKE, MOVE_GRAVITY, MOVE_SCRATCH, MOVE_POUND); }