Improve AI's Skill Swap handling in double battles (#5360)

* Improve AI contrary

* Update src/battle_ai_util.c

Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com>

* Fix additionalEffects loop

* moves[i] to aiMove

* Update src/battle_ai_util.c

Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com>

---------

Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com>
This commit is contained in:
Pawkkie 2024-09-10 18:00:29 -04:00 committed by GitHub
parent b478881fc6
commit 047289a639
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 80 additions and 4 deletions

View file

@ -114,6 +114,7 @@ bool32 HasMoveEffectANDArg(u32 battlerId, u32 effect, u32 argument);
bool32 HasMoveWithAdditionalEffect(u32 battlerId, u32 moveEffect);
bool32 HasMoveWithCriticalHitChance(u32 battlerId);
bool32 HasMoveWithMoveEffectExcept(u32 battlerId, u32 moveEffect, u32 exception);
bool32 HasMoveThatLowersOwnStats(u32 battlerId);
bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool32 ignoreStatus, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect);
bool32 HasAnyKnownMove(u32 battlerId);
bool32 IsAromaVeilProtectedMove(u32 move);
@ -138,6 +139,7 @@ bool32 ShouldFakeOut(u32 battlerAtk, u32 battlerDef, u32 move);
bool32 HasThawingMove(u32 battler);
bool32 IsStatRaisingEffect(u32 effect);
bool32 IsStatLoweringEffect(u32 effect);
bool32 IsSelfStatLoweringEffect(u32 effect);
bool32 IsAttackBoostMoveEffect(u32 effect);
bool32 IsUngroundingEffect(u32 effect);
bool32 IsSemiInvulnerable(u32 battlerDef, u32 move);

View file

@ -3049,15 +3049,26 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_SKILL_SWAP:
if (aiData->abilities[battlerAtk] != aiData->abilities[BATTLE_PARTNER(battlerAtk)] && !attackerHasBadAbility)
{
// Partner abilities
if (aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_TRUANT)
{
RETURN_SCORE_PLUS(10);
ADJUST_SCORE(10);
}
else if (aiData->abilities[battlerAtk] == ABILITY_COMPOUND_EYES
&& HasMoveWithLowAccuracy(battlerAtkPartner, FOE(battlerAtkPartner), 90, TRUE, atkPartnerAbility, aiData->abilities[FOE(battlerAtkPartner)], atkPartnerHoldEffect, aiData->holdEffects[FOE(battlerAtkPartner)]))
else if (aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_INTIMIDATE)
{
RETURN_SCORE_PLUS(3);
ADJUST_SCORE(DECENT_EFFECT);
}
// Active mon abilities
if (aiData->abilities[battlerAtk] == ABILITY_COMPOUND_EYES
&& HasMoveWithLowAccuracy(battlerAtkPartner, FOE(battlerAtkPartner), 90, TRUE, atkPartnerAbility, aiData->abilities[FOE(battlerAtkPartner)], atkPartnerHoldEffect, aiData->holdEffects[FOE(battlerAtkPartner)]))
{
ADJUST_SCORE(GOOD_EFFECT);
}
else if (aiData->abilities[battlerAtk] == ABILITY_CONTRARY && HasMoveThatLowersOwnStats(battlerAtkPartner))
{
ADJUST_SCORE(GOOD_EFFECT);
}
return score;
}
break;
case EFFECT_ROLE_PLAY:

View file

@ -2089,6 +2089,26 @@ bool32 HasAnyKnownMove(u32 battlerId)
return FALSE;
}
bool32 HasMoveThatLowersOwnStats(u32 battlerId)
{
s32 i, j;
u32 aiMove;
u16 *moves = GetMovesArray(battlerId);
for (i = 0; i < MAX_MON_MOVES; i++)
{
aiMove = moves[i];
if (aiMove != MOVE_NONE && aiMove != MOVE_UNAVAILABLE)
{
for (j = 0; j < gMovesInfo[aiMove].numAdditionalEffects; j++)
{
if (IsSelfStatLoweringEffect(gMovesInfo[aiMove].additionalEffects[j].moveEffect) && gMovesInfo[aiMove].additionalEffects[j].self)
return TRUE;
}
}
}
return FALSE;
}
bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool32 ignoreStatus, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect)
{
s32 i;
@ -2294,6 +2314,34 @@ bool32 IsStatLoweringEffect(u32 effect)
}
}
bool32 IsSelfStatLoweringEffect(u32 effect)
{
// Self stat lowering moves like Overheart, Superpower etc.
switch (effect)
{
case MOVE_EFFECT_ATK_MINUS_1:
case MOVE_EFFECT_DEF_MINUS_1:
case MOVE_EFFECT_SPD_MINUS_1:
case MOVE_EFFECT_SP_ATK_MINUS_1:
case MOVE_EFFECT_SP_DEF_MINUS_1:
case MOVE_EFFECT_EVS_MINUS_1:
case MOVE_EFFECT_ACC_MINUS_1:
case MOVE_EFFECT_ATK_MINUS_2:
case MOVE_EFFECT_DEF_MINUS_2:
case MOVE_EFFECT_SPD_MINUS_2:
case MOVE_EFFECT_SP_ATK_MINUS_2:
case MOVE_EFFECT_SP_DEF_MINUS_2:
case MOVE_EFFECT_EVS_MINUS_2:
case MOVE_EFFECT_ACC_MINUS_2:
case MOVE_EFFECT_V_CREATE:
case MOVE_EFFECT_ATK_DEF_DOWN:
case MOVE_EFFECT_DEF_SPDEF_DOWN:
return TRUE;
default:
return FALSE;
}
}
bool32 HasDamagingMove(u32 battlerId)
{
u32 i;

View file

@ -239,3 +239,18 @@ AI_SINGLE_BATTLE_TEST("AI chooses moves that cure inactive party members")
TURN { EXPECT_MOVE(opponent, MOVE_HEAL_BELL); }
}
}
AI_DOUBLE_BATTLE_TEST("AI prioritizes Skill Swapping Contrary to allied mons that would benefit from it")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_SKILL_SWAP].effect == EFFECT_SKILL_SWAP);
ASSUME(gMovesInfo[MOVE_OVERHEAT].additionalEffects[0].moveEffect == MOVE_EFFECT_SP_ATK_MINUS_2);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_DOUBLE_BATTLE);
PLAYER(SPECIES_WOBBUFFET) { Speed(3); }
PLAYER(SPECIES_WOBBUFFET) { Speed(3); }
OPPONENT(SPECIES_SPINDA) { Ability(ABILITY_CONTRARY); Speed(5); Moves(MOVE_SKILL_SWAP, MOVE_ENCORE, MOVE_FAKE_TEARS, MOVE_SWAGGER); }
OPPONENT(SPECIES_ARCANINE) { Ability(ABILITY_INTIMIDATE); Speed(4); Moves (MOVE_OVERHEAT); }
} WHEN {
TURN { EXPECT_MOVE(opponentLeft, MOVE_SKILL_SWAP, target:opponentRight); EXPECT_MOVE(opponentRight, MOVE_OVERHEAT); }
}
}