Fix AI semi-invulnerable move handling and simplify switching logic (#9180)
This commit is contained in:
parent
a3d9aa7ae2
commit
9119a6cc53
@ -480,7 +480,6 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler)
|
||||
u32 opposingBattler = GetOppositeBattler(battler);
|
||||
u32 incomingMove = GetIncomingMove(battler, opposingBattler, gAiLogicData);
|
||||
enum Type incomingType = CheckDynamicMoveType(GetBattlerMon(opposingBattler), incomingMove, opposingBattler, MON_IN_BATTLE);
|
||||
bool32 isOpposingBattlerChargingOrInvulnerable = !BreaksThroughSemiInvulnerablity(opposingBattler, incomingMove) || IsTwoTurnNotSemiInvulnerableMove(opposingBattler, incomingMove);
|
||||
s32 i, j;
|
||||
|
||||
if (!(gAiThinkingStruct->aiFlags[battler] & AI_FLAG_SMART_SWITCHING))
|
||||
@ -530,42 +529,42 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler)
|
||||
{
|
||||
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_FLASH_FIRE;
|
||||
}
|
||||
if (incomingType == TYPE_WATER || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_WATER))
|
||||
if (incomingType == TYPE_WATER)
|
||||
{
|
||||
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_WATER_ABSORB;
|
||||
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_DRY_SKIN;
|
||||
if (GetConfig(B_REDIRECT_ABILITY_IMMUNITY) >= GEN_5)
|
||||
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_STORM_DRAIN;
|
||||
}
|
||||
if (incomingType == TYPE_ELECTRIC || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_ELECTRIC))
|
||||
if (incomingType == TYPE_ELECTRIC)
|
||||
{
|
||||
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_VOLT_ABSORB;
|
||||
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_MOTOR_DRIVE;
|
||||
if (GetConfig(B_REDIRECT_ABILITY_IMMUNITY) >= GEN_5)
|
||||
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_LIGHTNING_ROD;
|
||||
}
|
||||
if (incomingType == TYPE_GRASS || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_GRASS))
|
||||
if (incomingType == TYPE_GRASS)
|
||||
{
|
||||
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_SAP_SIPPER;
|
||||
}
|
||||
if (incomingType == TYPE_GROUND || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_GROUND))
|
||||
if (incomingType == TYPE_GROUND)
|
||||
{
|
||||
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_EARTH_EATER;
|
||||
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_LEVITATE;
|
||||
}
|
||||
if (IsSoundMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsSoundMove(incomingMove)))
|
||||
if (IsSoundMove(incomingMove))
|
||||
{
|
||||
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_SOUNDPROOF;
|
||||
}
|
||||
if (IsBallisticMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsBallisticMove(incomingMove)))
|
||||
if (IsBallisticMove(incomingMove))
|
||||
{
|
||||
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_BULLETPROOF;
|
||||
}
|
||||
if (IsWindMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsWindMove(incomingMove)))
|
||||
if (IsWindMove(incomingMove))
|
||||
{
|
||||
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_WIND_RIDER;
|
||||
}
|
||||
if (IsPowderMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsPowderMove(incomingMove)))
|
||||
if (IsPowderMove(incomingMove))
|
||||
{
|
||||
if (GetConfig(B_POWDER_OVERCOAT) >= GEN_6)
|
||||
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_OVERCOAT;
|
||||
@ -617,14 +616,23 @@ static bool32 ShouldSwitchIfOpponentChargingOrInvulnerable(u32 battler)
|
||||
{
|
||||
u32 opposingBattler = GetOppositeBattler(battler);
|
||||
u32 incomingMove = GetIncomingMove(battler, opposingBattler, gAiLogicData);
|
||||
|
||||
bool32 isOpposingBattlerChargingOrInvulnerable = !BreaksThroughSemiInvulnerablity(opposingBattler, incomingMove) || IsTwoTurnNotSemiInvulnerableMove(opposingBattler, incomingMove);
|
||||
enum BattleMoveEffects effect = GetMoveEffect(incomingMove);
|
||||
|
||||
if (IsDoubleBattle() || !(gAiThinkingStruct->aiFlags[battler] & AI_FLAG_SMART_SWITCHING))
|
||||
return FALSE;
|
||||
|
||||
// Two-turn attacks that charge without entering semi-invulnerable state (e.g. Solar Beam).
|
||||
// First turn of Fly/Dive/Bounce/Sky Drop: move is selected this turn but user is not yet semi-invulnerable.
|
||||
// Opponent is already semi-invulnerable.
|
||||
if (!(IsTwoTurnNotSemiInvulnerableMove(opposingBattler, incomingMove)
|
||||
|| ((effect == EFFECT_SEMI_INVULNERABLE || effect == EFFECT_SKY_DROP) && !IsSemiInvulnerable(opposingBattler, CHECK_ALL))
|
||||
|| IsSemiInvulnerable(opposingBattler, CHECK_ALL)))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// In a world with a unified ShouldSwitch function, also want to check whether we already win 1v1 and if we do don't switch; not worth doubling the HasBadOdds computation for now
|
||||
if (isOpposingBattlerChargingOrInvulnerable && gAiLogicData->mostSuitableMonId[battler] != PARTY_SIZE && RandomPercentage(RNG_AI_SWITCH_FREE_TURN, GetSwitchChance(SHOULD_SWITCH_FREE_TURN)))
|
||||
if (gAiLogicData->mostSuitableMonId[battler] != PARTY_SIZE && RandomPercentage(RNG_AI_SWITCH_FREE_TURN, GetSwitchChance(SHOULD_SWITCH_FREE_TURN)))
|
||||
return SetSwitchinAndSwitch(battler, PARTY_SIZE);
|
||||
|
||||
return FALSE;
|
||||
@ -654,7 +662,7 @@ static bool32 ShouldSwitchIfTrapperInParty(u32 battler)
|
||||
for (i = firstId; i < lastId; i++)
|
||||
{
|
||||
if (IsAceMon(battler, i))
|
||||
return FALSE;
|
||||
continue;
|
||||
|
||||
monAbility = GetMonAbility(&party[i]);
|
||||
|
||||
|
||||
@ -54,7 +54,7 @@ static bool32 AI_IsDoubleSpreadMove(u32 battlerAtk, u32 move)
|
||||
if (moveTargetType == MOVE_TARGET_BOTH && battlerAtk == BATTLE_PARTNER(battlerDef))
|
||||
continue;
|
||||
|
||||
if (IsBattlerAlive(battlerDef) && !IsSemiInvulnerable(battlerDef, move))
|
||||
if (IsBattlerAlive(battlerDef) && (!IsSemiInvulnerable(battlerDef, CHECK_ALL) || BreaksThroughSemiInvulnerablity(battlerDef, move)))
|
||||
numOfTargets++;
|
||||
}
|
||||
|
||||
|
||||
@ -1030,6 +1030,21 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if player's m
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out on turn 1 if it predicts a semi-invulnerable move and it has a good switchin")
|
||||
{
|
||||
PASSES_RANDOMLY(PREDICT_MOVE_CHANCE, 100, RNG_AI_PREDICT_MOVE);
|
||||
PASSES_RANDOMLY(SHOULD_SWITCH_FREE_TURN_PERCENTAGE, 100, RNG_AI_SWITCH_FREE_TURN);
|
||||
GIVEN {
|
||||
ASSUME(GetMoveType(MOVE_DIVE) == TYPE_WATER);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING | AI_FLAG_OMNISCIENT | AI_FLAG_PREDICT_MOVE);
|
||||
PLAYER(SPECIES_LUVDISC) { Level(1); Moves(MOVE_DIVE); }
|
||||
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_SCRATCH); }
|
||||
OPPONENT(SPECIES_PIKACHU) { Moves(MOVE_THUNDERBOLT); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_DIVE); EXPECT_SWITCH(opponent, 1); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has an absorber but current mon has SE move 33% of the time")
|
||||
{
|
||||
PASSES_RANDOMLY(33, 100, RNG_AI_SWITCH_ABSORBING_STAY_IN);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user