Fix for Levitate and Mold Breaker being seen correctly by switch AI, with Levitate tests (#7748)
This commit is contained in:
parent
f671b130a6
commit
b365221a0c
@ -28,6 +28,17 @@ static u32 GetAIEffectGroup(enum BattleMoveEffects effect);
|
||||
static u32 GetAIEffectGroupFromMove(u32 battler, u32 move);
|
||||
|
||||
// Functions
|
||||
static u32 AI_GetMoldBreakerSanitizedAbility(u32 battlerAtk, u32 abilityAtk, u32 abilityDef, u32 holdEffectDef, u32 move)
|
||||
{
|
||||
if (MoveIgnoresTargetAbility(move))
|
||||
return ABILITY_NONE;
|
||||
|
||||
if (holdEffectDef != HOLD_EFFECT_ABILITY_SHIELD && IsMoldBreakerTypeAbility(battlerAtk, abilityAtk))
|
||||
return ABILITY_NONE;
|
||||
|
||||
return abilityDef;
|
||||
}
|
||||
|
||||
static bool32 AI_IsDoubleSpreadMove(u32 battlerAtk, u32 move)
|
||||
{
|
||||
u32 numOfTargets = 0;
|
||||
@ -896,10 +907,10 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
|
||||
ctx.updateFlags = FALSE;
|
||||
ctx.weather = weather;
|
||||
ctx.fixedBasePower = SetFixedMoveBasePower(battlerAtk, move);
|
||||
ctx.abilityAtk = aiData->abilities[battlerAtk];
|
||||
ctx.abilityDef = aiData->abilities[battlerDef];
|
||||
ctx.holdEffectAtk = aiData->holdEffects[battlerAtk];
|
||||
ctx.holdEffectDef = aiData->holdEffects[battlerDef];
|
||||
ctx.abilityAtk = aiData->abilities[battlerAtk];
|
||||
ctx.abilityDef = AI_GetMoldBreakerSanitizedAbility(battlerAtk, ctx.abilityAtk, aiData->abilities[battlerDef], ctx.holdEffectDef, move);
|
||||
ctx.typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(&ctx);
|
||||
|
||||
u32 movePower = GetMovePower(move);
|
||||
|
||||
@ -7732,7 +7732,7 @@ enum IronBallCheck
|
||||
};
|
||||
|
||||
// Only called directly when calculating damage type effectiveness, and Iron Ball's type effectiveness mechanics
|
||||
static bool32 IsBattlerGroundedInverseCheck(u32 battler, enum InverseBattleCheck checkInverse, enum IronBallCheck checkIronBall)
|
||||
static bool32 IsBattlerGroundedInverseCheck(u32 battler, u32 ability, enum InverseBattleCheck checkInverse, enum IronBallCheck checkIronBall)
|
||||
{
|
||||
enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler, TRUE);
|
||||
|
||||
@ -7750,7 +7750,7 @@ static bool32 IsBattlerGroundedInverseCheck(u32 battler, enum InverseBattleCheck
|
||||
return FALSE;
|
||||
if (holdEffect == HOLD_EFFECT_AIR_BALLOON)
|
||||
return FALSE;
|
||||
if (gAiLogicData->aiCalcInProgress ? gAiLogicData->abilities[battler] == ABILITY_LEVITATE : IsAbilityAndRecord(battler, GetBattlerAbility(battler), ABILITY_LEVITATE))
|
||||
if (ability == ABILITY_LEVITATE)
|
||||
return FALSE;
|
||||
if (IS_BATTLER_OF_TYPE(battler, TYPE_FLYING) && (!(checkInverse == INVERSE_BATTLE) || !FlagGet(B_FLAG_INVERSE_BATTLE)))
|
||||
return FALSE;
|
||||
@ -7759,7 +7759,7 @@ static bool32 IsBattlerGroundedInverseCheck(u32 battler, enum InverseBattleCheck
|
||||
|
||||
bool32 IsBattlerGrounded(u32 battler)
|
||||
{
|
||||
return IsBattlerGroundedInverseCheck(battler, NOT_INVERSE_BATTLE, CHECK_IRON_BALL);
|
||||
return IsBattlerGroundedInverseCheck(battler, GetBattlerAbility(battler), NOT_INVERSE_BATTLE, CHECK_IRON_BALL);
|
||||
}
|
||||
|
||||
u32 GetMoveSlot(u16 *moves, u32 move)
|
||||
@ -9713,7 +9713,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, INVERSE_BATTLE, CHECK_IRON_BALL) && !(MoveIgnoresTypeIfFlyingAndUngrounded(ctx->move)))
|
||||
else if (ctx->moveType == TYPE_GROUND && !IsBattlerGroundedInverseCheck(ctx->battlerDef, ctx->abilityDef, INVERSE_BATTLE, CHECK_IRON_BALL) && !(MoveIgnoresTypeIfFlyingAndUngrounded(ctx->move)))
|
||||
{
|
||||
modifier = UQ_4_12(0.0);
|
||||
if (ctx->updateFlags && ctx->abilityDef == ABILITY_LEVITATE)
|
||||
@ -9743,7 +9743,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, NOT_INVERSE_BATTLE, IGNORE_IRON_BALL)
|
||||
&& !IsBattlerGroundedInverseCheck(ctx->battlerDef, ctx->abilityAtk, NOT_INVERSE_BATTLE, IGNORE_IRON_BALL)
|
||||
&& !FlagGet(B_FLAG_INVERSE_BATTLE))
|
||||
{
|
||||
modifier = UQ_4_12(1.0);
|
||||
|
||||
@ -1,4 +1,115 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
TO_DO_BATTLE_TEST("TODO: Write Levitate (Ability) test titles")
|
||||
SINGLE_BATTLE_TEST("Levitate activates when targeted by ground type moves")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(GetMoveType(MOVE_MUD_SLAP) == TYPE_GROUND);
|
||||
PLAYER(SPECIES_LUNATONE) { Ability(ABILITY_LEVITATE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_MUD_SLAP); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(player, ABILITY_LEVITATE);
|
||||
MESSAGE("Lunatone makes Ground-type moves miss with Levitate!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Levitate does not activate if protected")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(GetMoveType(MOVE_MUD_SLAP) == TYPE_GROUND);
|
||||
PLAYER(SPECIES_LUNATONE) { Ability(ABILITY_LEVITATE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_PROTECT); MOVE(opponent, MOVE_MUD_SLAP); }
|
||||
} SCENE {
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(player, ABILITY_LEVITATE);
|
||||
MESSAGE("Lunatone makes Ground-type moves miss with Levitate!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Levitate does not activate on status moves")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(GetMoveType(MOVE_SAND_ATTACK) == TYPE_GROUND);
|
||||
ASSUME(GetMoveCategory(MOVE_SAND_ATTACK) == DAMAGE_CATEGORY_STATUS);
|
||||
PLAYER(SPECIES_LUNATONE) { Ability(ABILITY_LEVITATE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_SAND_ATTACK); }
|
||||
} SCENE {
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(player, ABILITY_LEVITATE);
|
||||
MESSAGE("Lunatone makes Ground-type moves miss with Levitate!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Levitate does not activate if attacked by an opponent with Mold Breaker")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(GetMoveType(MOVE_MUD_SLAP) == TYPE_GROUND);
|
||||
PLAYER(SPECIES_LUNATONE) { Ability(ABILITY_LEVITATE); }
|
||||
OPPONENT(SPECIES_TINKATON) { Ability(ABILITY_MOLD_BREAKER); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_PROTECT); MOVE(opponent, MOVE_MUD_SLAP); }
|
||||
} SCENE {
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(player, ABILITY_LEVITATE);
|
||||
MESSAGE("Lunatone makes Ground-type moves miss with Levitate!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Levitate does not cause single remaining target to take higher damage")
|
||||
{
|
||||
s16 damage[3];
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_REGIROCK) { Speed(1); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
|
||||
OPPONENT(SPECIES_LUNATONE) { Speed(3); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(2); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerRight, MOVE_CELEBRATE); MOVE(playerLeft, MOVE_EARTHQUAKE); }
|
||||
TURN { MOVE(playerRight, MOVE_MEMENTO, target:opponentLeft); MOVE(playerLeft, MOVE_EARTHQUAKE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, playerLeft);
|
||||
HP_BAR(playerRight, captureDamage: &damage[0]);
|
||||
HP_BAR(opponentRight, captureDamage: &damage[1]);
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_MEMENTO, playerRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, playerLeft);
|
||||
HP_BAR(opponentRight, captureDamage: &damage[2]);
|
||||
|
||||
} THEN {
|
||||
EXPECT_EQ(damage[0], damage[1]);
|
||||
EXPECT_EQ(damage[1], damage[2]);
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("Levitate is seen correctly by switch AI")
|
||||
{
|
||||
u32 ability = ABILITY_NONE, item = ITEM_NONE;
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_OWN_TEMPO, item = ITEM_NONE ; }
|
||||
PARAMETRIZE { ability = ABILITY_MOLD_BREAKER, item = ITEM_NONE ; }
|
||||
PARAMETRIZE { ability = ABILITY_MOLD_BREAKER, item = ITEM_ABILITY_SHIELD ; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(GetMoveType(MOVE_MUD_SLAP) == TYPE_GROUND);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT);
|
||||
PLAYER(SPECIES_TINKATON) { Ability(ability); Speed(3); }
|
||||
OPPONENT(SPECIES_PONYTA) { Level(1); Item(ITEM_EJECT_PACK); Moves(MOVE_OVERHEAT); Speed(4); } // Forces switchout
|
||||
OPPONENT(SPECIES_VIKAVOLT) { HP(1); Speed(1); Ability(ABILITY_LEVITATE); Moves(MOVE_FLAMETHROWER); Item(item); }
|
||||
OPPONENT(SPECIES_HYPNO) { Speed(1); Moves(MOVE_IRON_HEAD); }
|
||||
} WHEN {
|
||||
if ((ability == ABILITY_MOLD_BREAKER) && (item != ITEM_ABILITY_SHIELD))
|
||||
TURN { MOVE(player, MOVE_MUD_SLAP); EXPECT_SEND_OUT(opponent, 2); }
|
||||
else
|
||||
TURN { MOVE(player, MOVE_MUD_SLAP); EXPECT_SEND_OUT(opponent, 1); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user