AI respects partner when using spread moves in double battles (#4518)
* Fixes Earthquake AI in double battles * earthquake_ai_fix * Use CompareDamagingMoves to fix spread damage issue
This commit is contained in:
parent
6f12da0a67
commit
635db6312c
@ -191,7 +191,6 @@ enum {
|
||||
EFFECT_ROUND,
|
||||
EFFECT_BRINE,
|
||||
EFFECT_RETALIATE,
|
||||
EFFECT_BULLDOZE,
|
||||
EFFECT_FOUL_PLAY,
|
||||
EFFECT_PSYSHOCK,
|
||||
EFFECT_ROOST,
|
||||
|
||||
@ -423,7 +423,7 @@ static void SetBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u3
|
||||
// Simulate dmg for both ai controlled mons and for player controlled mons.
|
||||
for (battlerDef = 0; battlerDef < battlersCount; battlerDef++)
|
||||
{
|
||||
if (battlerAtk == battlerDef)
|
||||
if (battlerAtk == battlerDef || !IsBattlerAlive(battlerDef))
|
||||
continue;
|
||||
|
||||
SaveBattlerData(battlerDef);
|
||||
@ -3028,21 +3028,6 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
else if (IsAbilityOfRating(aiData->abilities[battlerAtk], 0) || IsAbilityOfRating(aiData->abilities[battlerDef], 10))
|
||||
ADJUST_SCORE(DECENT_EFFECT); // we want to transfer our bad ability or take their awesome ability
|
||||
break;
|
||||
case EFFECT_EARTHQUAKE:
|
||||
case EFFECT_MAGNITUDE:
|
||||
if (!IsBattlerGrounded(battlerAtkPartner)
|
||||
|| (IsBattlerGrounded(battlerAtkPartner)
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner, move) == AI_IS_SLOWER
|
||||
&& IsUngroundingEffect(gMovesInfo[aiData->partnerMove].effect)))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
else if (IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_FIRE)
|
||||
|| IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_ELECTRIC)
|
||||
|| IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_POISON)
|
||||
|| IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_ROCK))
|
||||
ADJUST_SCORE(-10); // partner will be hit by earthquake and is weak to it
|
||||
else if (IsBattlerAlive(battlerAtkPartner))
|
||||
ADJUST_SCORE(-3);
|
||||
break;
|
||||
}
|
||||
|
||||
// lightning rod, flash fire against enemy handled in AI_CheckBadMove
|
||||
@ -3082,6 +3067,18 @@ static s32 CompareMoveAccuracies(u32 battlerAtk, u32 battlerDef, u32 moveSlot1,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool32 ShouldUseSpreadDamageMove(u32 battlerAtk, u32 move, u32 moveIndex, u32 hitsToFaintOpposingBattler)
|
||||
{
|
||||
u32 partnerBattler = BATTLE_PARTNER(battlerAtk);
|
||||
u32 noOfHitsToFaintPartner = GetNoOfHitsToKOBattler(battlerAtk, partnerBattler, moveIndex);
|
||||
return (IsDoubleBattle()
|
||||
&& noOfHitsToFaintPartner != 0 // Immunity check
|
||||
&& IsBattlerAlive(partnerBattler)
|
||||
&& gMovesInfo[move].target == MOVE_TARGET_FOES_AND_ALLY
|
||||
&& !(noOfHitsToFaintPartner < 4 && hitsToFaintOpposingBattler == 1)
|
||||
&& noOfHitsToFaintPartner < 7);
|
||||
}
|
||||
|
||||
static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId)
|
||||
{
|
||||
u32 i;
|
||||
@ -3099,7 +3096,13 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId)
|
||||
if (moves[i] != MOVE_NONE && gMovesInfo[moves[i]].power)
|
||||
{
|
||||
noOfHits[i] = GetNoOfHitsToKOBattler(battlerAtk, battlerDef, i);
|
||||
if (noOfHits[i] < leastHits && noOfHits[i] != 0)
|
||||
if (ShouldUseSpreadDamageMove(battlerAtk,moves[i], i, noOfHits[i]))
|
||||
{
|
||||
noOfHits[i] = -1;
|
||||
viableMoveScores[i] = 0;
|
||||
isTwoTurnNotSemiInvulnerableMove[i] = FALSE;
|
||||
}
|
||||
else if (noOfHits[i] < leastHits && noOfHits[i] != 0)
|
||||
{
|
||||
leastHits = noOfHits[i];
|
||||
}
|
||||
|
||||
@ -371,14 +371,17 @@ bool32 IsDamageMoveUnusable(u32 move, u32 battlerAtk, u32 battlerDef)
|
||||
s32 moveType;
|
||||
struct AiLogicData *aiData = AI_DATA;
|
||||
u32 battlerDefAbility;
|
||||
GET_MOVE_TYPE(move, moveType);
|
||||
|
||||
if (DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move))
|
||||
battlerDefAbility = ABILITY_NONE;
|
||||
else
|
||||
battlerDefAbility = aiData->abilities[battlerDef];
|
||||
|
||||
SetTypeBeforeUsingMove(move, battlerAtk);
|
||||
GET_MOVE_TYPE(move, moveType);
|
||||
// Battler doesn't see partners Ability for some reason.
|
||||
// This is a small hack to avoid the issue but should be investigated
|
||||
if (battlerDef == BATTLE_PARTNER(battlerAtk))
|
||||
battlerDefAbility = GetBattlerAbility(battlerDef);
|
||||
|
||||
switch (battlerDefAbility)
|
||||
{
|
||||
@ -472,14 +475,13 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes
|
||||
|
||||
gBattleStruct->dynamicMoveType = 0;
|
||||
|
||||
|
||||
SetTypeBeforeUsingMove(move, battlerAtk);
|
||||
GET_MOVE_TYPE(move, moveType);
|
||||
effectivenessMultiplier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, aiData->abilities[battlerDef], FALSE);
|
||||
|
||||
if (gMovesInfo[move].power)
|
||||
isDamageMoveUnusable = IsDamageMoveUnusable(move, battlerAtk, battlerDef);
|
||||
|
||||
effectivenessMultiplier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, aiData->abilities[battlerDef], FALSE);
|
||||
if (gMovesInfo[move].power && !isDamageMoveUnusable)
|
||||
{
|
||||
s32 critChanceIndex, normalDmg, fixedBasePower, n;
|
||||
|
||||
@ -8846,7 +8846,6 @@ static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32
|
||||
if (gBattleStruct->lastMoveFailed & gBitTable[battlerAtk])
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(2.0));
|
||||
break;
|
||||
case EFFECT_BULLDOZE:
|
||||
case EFFECT_MAGNITUDE:
|
||||
case EFFECT_EARTHQUAKE:
|
||||
if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && !(gStatuses3[battlerDef] & STATUS3_SEMI_INVULNERABLE))
|
||||
|
||||
@ -1228,12 +1228,6 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
|
||||
.battleTvScore = 0, // TODO: Assign points
|
||||
},
|
||||
|
||||
[EFFECT_BULLDOZE] =
|
||||
{
|
||||
.battleScript = BattleScript_EffectHit,
|
||||
.battleTvScore = 0, // TODO: Assign points
|
||||
},
|
||||
|
||||
[EFFECT_FOUL_PLAY] =
|
||||
{
|
||||
.battleScript = BattleScript_EffectHit,
|
||||
|
||||
@ -12504,7 +12504,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
|
||||
.description = COMPOUND_STRING(
|
||||
"Stomps down on the ground.\n"
|
||||
"Lowers Speed."),
|
||||
.effect = EFFECT_BULLDOZE,
|
||||
.effect = EFFECT_EARTHQUAKE,
|
||||
.power = 60,
|
||||
.type = TYPE_GROUND,
|
||||
.accuracy = 100,
|
||||
|
||||
@ -882,3 +882,76 @@ AI_SINGLE_BATTLE_TEST("AI will choose Superpower over Outrage with Contrary")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AI_DOUBLE_BATTLE_TEST("AI will not choose Earthquake if it damages the partner")
|
||||
{
|
||||
u32 species;
|
||||
|
||||
PARAMETRIZE { species = SPECIES_CHARIZARD; }
|
||||
PARAMETRIZE { species = SPECIES_CHARMANDER; }
|
||||
PARAMETRIZE { species = SPECIES_CHIKORITA; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_PHANPY) { Moves(MOVE_EARTHQUAKE, MOVE_TACKLE); }
|
||||
OPPONENT(species) { Moves(MOVE_CELEBRATE); }
|
||||
} WHEN {
|
||||
if (species == SPECIES_CHARIZARD)
|
||||
TURN { EXPECT_MOVE(opponentLeft, MOVE_EARTHQUAKE); }
|
||||
else
|
||||
TURN { EXPECT_MOVE(opponentLeft, MOVE_TACKLE, target: playerLeft); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_DOUBLE_BATTLE_TEST("AI will choose Earthquake if partner is not alive")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_EARTHQUAKE, MOVE_TACKLE); }
|
||||
OPPONENT(SPECIES_PIKACHU) { HP(1); Moves(MOVE_CELEBRATE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentRight); }
|
||||
TURN { EXPECT_MOVE(opponentLeft, MOVE_EARTHQUAKE); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_DOUBLE_BATTLE_TEST("AI will choose Earthquake if it kill an opposing mon and does 1/3 of damage to AI")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_EARTHQUAKE, MOVE_TACKLE); }
|
||||
OPPONENT(SPECIES_PARAS) { Moves(MOVE_CELEBRATE); }
|
||||
} WHEN {
|
||||
TURN { EXPECT_MOVE(opponentLeft, MOVE_EARTHQUAKE); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_DOUBLE_BATTLE_TEST("AI will the see a corresponding absorbing ability on partner to one of its moves")
|
||||
{
|
||||
u32 ability;
|
||||
PARAMETRIZE { ability = ABILITY_LIGHTNING_ROD; }
|
||||
PARAMETRIZE { ability = ABILITY_STATIC; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_DISCHARGE].target == MOVE_TARGET_FOES_AND_ALLY);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_DISCHARGE, MOVE_TACKLE); }
|
||||
OPPONENT(SPECIES_PIKACHU) { HP(1); Ability(ability); Moves(MOVE_CELEBRATE); }
|
||||
} WHEN {
|
||||
if (ability == ABILITY_LIGHTNING_ROD)
|
||||
TURN { EXPECT_MOVE(opponentLeft, MOVE_DISCHARGE); }
|
||||
else
|
||||
TURN { EXPECT_MOVE(opponentLeft, MOVE_TACKLE); }
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ SINGLE_BATTLE_TEST("Grassy Terrain decreases power of Earthquake and Bulldoze by
|
||||
PARAMETRIZE { terrain = TRUE; move = MOVE_BULLDOZE; } // 3
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].effect == EFFECT_EARTHQUAKE);
|
||||
ASSUME(gMovesInfo[MOVE_BULLDOZE].effect == EFFECT_BULLDOZE);
|
||||
ASSUME(gMovesInfo[MOVE_BULLDOZE].effect == EFFECT_EARTHQUAKE);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user