AI smarter status handling (#6550)

This commit is contained in:
Pawkkie 2025-04-08 11:47:45 -04:00 committed by GitHub
parent 69f2f9f151
commit 54928726cb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 134 additions and 33 deletions

View File

@ -177,11 +177,13 @@ bool32 AI_CanGetFrostbite(u32 battler, u32 ability);
bool32 AI_CanBeConfused(u32 battlerAtk, u32 battlerDef, u32 move, u32 ability);
bool32 IsBattlerIncapacitated(u32 battler, u32 ability);
bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove);
bool32 ShouldPoisonSelf(u32 battler, u32 ability);
bool32 ShouldPoison(u32 battlerAtk, u32 battlerDef);
bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove);
bool32 AI_CanParalyze(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove);
bool32 AI_CanConfuse(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove);
bool32 ShouldBurnSelf(u32 battler, u32 ability);
bool32 ShouldBurn(u32 battlerAtk, u32 battlerDef);
bool32 ShouldFreezeOrFrostbite(u32 battlerAtk, u32 battlerDef);
bool32 ShouldParalyze(u32 battlerAtk, u32 battlerDef);
bool32 AI_CanBurn(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove);
bool32 AI_CanGiveFrostbite(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove);
bool32 AI_CanBeInfatuated(u32 battlerAtk, u32 battlerDef, u32 defAbility);

View File

@ -1456,6 +1456,8 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_TOXIC:
if (!AI_CanPoison(battlerAtk, battlerDef, abilityDef, move, aiData->partnerMove))
ADJUST_SCORE(-10);
if (!ShouldPoison(battlerAtk, battlerDef))
ADJUST_SCORE(-5);
break;
case EFFECT_LIGHT_SCREEN:
if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_LIGHTSCREEN
@ -1499,6 +1501,8 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_PARALYZE:
if (!AI_CanParalyze(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, aiData->partnerMove))
ADJUST_SCORE(-10);
if (!ShouldParalyze(battlerAtk, battlerDef))
ADJUST_SCORE(-5);
break;
case EFFECT_SUBSTITUTE:
if (gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE || aiData->abilities[battlerDef] == ABILITY_INFILTRATOR)
@ -1791,6 +1795,8 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_WILL_O_WISP:
if (!AI_CanBurn(battlerAtk, battlerDef, aiData->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove))
ADJUST_SCORE(-10);
if (!ShouldBurn(battlerAtk, battlerDef))
ADJUST_SCORE(-5);
break;
case EFFECT_MEMENTO:
if (CountUsablePartyMons(battlerAtk) == 0 || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
@ -4057,11 +4063,11 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_TOXIC_ORB:
if (!ShouldPoisonSelf(battlerAtk, aiData->abilities[battlerAtk]))
if (!ShouldPoison(battlerAtk, battlerAtk))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_FLAME_ORB:
if (!ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk]) && CanBeBurned(battlerAtk, aiData->abilities[battlerDef]))
if (!ShouldBurn(battlerAtk, battlerAtk) && CanBeBurned(battlerAtk, aiData->abilities[battlerDef]))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_BLACK_SLUDGE:
@ -4107,11 +4113,11 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
case HOLD_EFFECT_CHOICE_BAND:
break;
case HOLD_EFFECT_TOXIC_ORB:
if (ShouldPoisonSelf(battlerAtk, aiData->abilities[battlerAtk]))
if (ShouldPoison(battlerAtk, battlerAtk))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_FLAME_ORB:
if (ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk]))
if (ShouldBurn(battlerAtk, battlerAtk))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_BLACK_SLUDGE:
@ -4761,11 +4767,11 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_TOXIC_ORB:
if (ShouldPoisonSelf(battlerAtk, aiData->abilities[battlerAtk]))
if (ShouldPoison(battlerAtk, battlerAtk))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_FLAME_ORB:
if (ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk]))
if (ShouldBurn(battlerAtk, battlerAtk))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_BLACK_SLUDGE:

View File

@ -3118,21 +3118,110 @@ bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move
return TRUE;
}
bool32 ShouldPoisonSelf(u32 battler, u32 ability)
static inline bool32 DoesBattlerBenefitFromAllVolatileStatus(u32 battler, u32 ability)
{
if (CanBePoisoned(battler, battler, GetBattlerAbility(battler)) && (
ability == ABILITY_MARVEL_SCALE
|| ability == ABILITY_POISON_HEAL
|| ability == ABILITY_QUICK_FEET
|| ability == ABILITY_MAGIC_GUARD
|| (ability == ABILITY_TOXIC_BOOST && HasMoveWithCategory(battler, DAMAGE_CATEGORY_PHYSICAL))
|| (ability == ABILITY_GUTS && HasMoveWithCategory(battler, DAMAGE_CATEGORY_PHYSICAL))
|| HasMoveEffect(battler, EFFECT_FACADE)
|| HasMoveEffect(battler, EFFECT_PSYCHO_SHIFT)))
return TRUE; // battler can be poisoned and has move/ability that synergizes with being poisoned
if (ability == ABILITY_MARVEL_SCALE
|| ability == ABILITY_QUICK_FEET
|| ability == ABILITY_MAGIC_GUARD
|| (ability == ABILITY_GUTS && HasMoveWithCategory(battler, DAMAGE_CATEGORY_PHYSICAL))
|| HasMoveEffect(battler, EFFECT_FACADE)
|| HasMoveEffect(battler, EFFECT_PSYCHO_SHIFT))
return TRUE;
return FALSE;
}
bool32 ShouldPoison(u32 battlerAtk, u32 battlerDef)
{
u32 defAbility = GetBattlerAbility(battlerDef);
// Battler can be poisoned and has move/ability that synergizes with being poisoned
if (CanBePoisoned(battlerAtk, battlerDef, GetBattlerAbility(battlerDef)) && (
DoesBattlerBenefitFromAllVolatileStatus(battlerDef, defAbility)
|| defAbility == ABILITY_POISON_HEAL
|| (defAbility == ABILITY_TOXIC_BOOST && HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL))))
{
if (battlerAtk == battlerDef) // Targeting self
return TRUE;
else
return FALSE;
}
if (battlerAtk == battlerDef)
return FALSE;
else
return TRUE;
}
bool32 ShouldBurn(u32 battlerAtk, u32 battlerDef)
{
u32 defAbility = GetBattlerAbility(battlerDef);
// Battler can be burned and has move/ability that synergizes with being burned
if (CanBeBurned(battlerDef, defAbility) && (
DoesBattlerBenefitFromAllVolatileStatus(battlerDef, defAbility)
|| defAbility == ABILITY_HEATPROOF
|| (defAbility == ABILITY_FLARE_BOOST && HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL))))
{
if (battlerAtk == battlerDef) // Targeting self
return TRUE;
else
return FALSE;
}
if (battlerAtk == battlerDef)
return FALSE;
else
return TRUE;
}
bool32 ShouldFreezeOrFrostbite(u32 battlerAtk, u32 battlerDef)
{
if (!B_USE_FROSTBITE)
{
if (CanBeFrozen(battlerDef))
{
if (battlerAtk == battlerDef) // Targeting self
return FALSE;
else
return TRUE;
}
return FALSE;
}
else
{
u32 defAbility = GetBattlerAbility(battlerDef);
// Battler can be frostbitten and has move/ability that synergizes with being frostbitten
if (CanBeFrozen(battlerDef) &&
DoesBattlerBenefitFromAllVolatileStatus(battlerDef, defAbility))
{
if (battlerAtk == battlerDef) // Targeting self
return TRUE;
else
return FALSE;
}
if (battlerAtk == battlerDef)
return FALSE;
else
return TRUE;
}
}
bool32 ShouldParalyze(u32 battlerAtk, u32 battlerDef)
{
u32 defAbility = GetBattlerAbility(battlerDef);
// Battler can be paralyzed and has move/ability that synergizes with being paralyzed
if (CanBeParalyzed(battlerDef, defAbility) && (
DoesBattlerBenefitFromAllVolatileStatus(battlerDef, defAbility)))
{
if (battlerAtk == battlerDef) // Targeting self
return TRUE;
else
return FALSE;
}
if (battlerAtk == battlerDef)
return FALSE;
else
return TRUE;
}
bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove)
{
if (!CanBePoisoned(battlerAtk, battlerDef, GetBattlerAbility(battlerDef))
@ -3196,20 +3285,6 @@ bool32 AI_CanGetFrostbite(u32 battler, u32 ability)
return TRUE;
}
bool32 ShouldBurnSelf(u32 battler, u32 ability)
{
if (CanBeBurned(battler, ability) && (
ability == ABILITY_QUICK_FEET
|| ability == ABILITY_HEATPROOF
|| ability == ABILITY_MAGIC_GUARD
|| (ability == ABILITY_FLARE_BOOST && HasMoveWithCategory(battler, DAMAGE_CATEGORY_SPECIAL))
|| (ability == ABILITY_GUTS && HasMoveWithCategory(battler, DAMAGE_CATEGORY_PHYSICAL))
|| HasMoveEffect(battler, EFFECT_FACADE)
|| HasMoveEffect(battler, EFFECT_PSYCHO_SHIFT)))
return TRUE;
return FALSE;
}
bool32 AI_CanBurn(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove)
{
if (!CanBeBurned(battlerDef, defAbility)

View File

@ -911,3 +911,21 @@ AI_SINGLE_BATTLE_TEST("AI won't boost stats against opponent with Unaware")
TURN { MOVE(player, MOVE_TACKLE); EXPECT_MOVE(opponent, MOVE_BODY_SLAM); }
}
}
AI_SINGLE_BATTLE_TEST("AI won't use status moves against opponents that would benefit")
{
u32 aiMove;
PARAMETRIZE { aiMove = MOVE_WILL_O_WISP; }
PARAMETRIZE { aiMove = MOVE_TOXIC; }
PARAMETRIZE { aiMove = MOVE_THUNDER_WAVE; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_WILL_O_WISP);
ASSUME(GetMoveEffect(MOVE_TOXIC) == EFFECT_TOXIC);
ASSUME(GetMoveEffect(MOVE_THUNDER_WAVE) == EFFECT_PARALYZE);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(SPECIES_SWELLOW) { Ability(ABILITY_GUTS); Moves(MOVE_TACKLE); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_TACKLE, aiMove); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); EXPECT_MOVE(opponent, MOVE_TACKLE); }
}
}