AI uses Tailwind. (#7515)

This commit is contained in:
surskitty 2025-08-12 17:12:45 -04:00 committed by GitHub
parent c6ee7feaa3
commit 7fb5d98bf6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 90 additions and 28 deletions

View File

@ -125,7 +125,6 @@ bool32 BattlerWillFaintFromWeather(u32 battler, u32 ability);
bool32 BattlerWillFaintFromSecondaryDamage(u32 battler, u32 ability);
bool32 ShouldTryOHKO(u32 battlerAtk, u32 battlerDef, u32 atkAbility, u32 defAbility, u32 move);
bool32 ShouldUseRecoilMove(u32 battlerAtk, u32 battlerDef, u32 recoilDmg, u32 moveIndex);
u32 GetBattlerSideSpeedAverage(u32 battler);
bool32 ShouldAbsorb(u32 battlerAtk, u32 battlerDef, u32 move, s32 damage);
bool32 ShouldRecover(u32 battlerAtk, u32 battlerDef, u32 move, u32 healPercent);
bool32 ShouldSetScreen(u32 battlerAtk, u32 battlerDef, enum BattleMoveEffects moveEffect);

View File

@ -429,13 +429,13 @@ static enum FieldEffectOutcome BenefitsFromPsychicTerrain(u32 battler)
static enum FieldEffectOutcome BenefitsFromTrickRoom(u32 battler)
{
// If we're in singles, we literally only care about speed.
if (!IsDoubleBattle())
if (IsBattle1v1())
{
if (GetBattlerSideSpeedAverage(battler) < GetBattlerSideSpeedAverage(FOE(battler)))
if (gAiLogicData->speedStats[battler] < gAiLogicData->speedStats[FOE(battler)])
return FIELD_EFFECT_POSITIVE;
// If we tie, we shouldn't change trick room state.
else if (GetBattlerSideSpeedAverage(battler) == GetBattlerSideSpeedAverage(FOE(battler)))
return FIELD_EFFECT_NEUTRAL;
else if (gAiLogicData->speedStats[battler] == gAiLogicData->speedStats[FOE(battler)])
return FIELD_EFFECT_NEUTRAL;
else
return FIELD_EFFECT_NEGATIVE;
}

View File

@ -5092,6 +5092,9 @@ case EFFECT_GUARD_SPLIT:
{
if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_TRICK_ROOM))
ADJUST_SCORE(GOOD_EFFECT);
// Set it for next pokemon in singles.
else if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && !hasPartner && (CountUsablePartyMons(battlerAtk) != 0))
ADJUST_SCORE(DECENT_EFFECT);
// Don't unset it on last turn.
else if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer != gBattleTurnCounter && ShouldClearFieldStatus(battlerAtk, STATUS_FIELD_TRICK_ROOM))
ADJUST_SCORE(GOOD_EFFECT);
@ -5200,9 +5203,44 @@ case EFFECT_GUARD_SPLIT:
ADJUST_SCORE(DECENT_EFFECT); // Attacker partner wouldn't go before target
break;
case EFFECT_TAILWIND:
if (GetBattlerSideSpeedAverage(battlerAtk) < GetBattlerSideSpeedAverage(battlerDef))
ADJUST_SCORE(GOOD_EFFECT);
{
if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer != gBattleTurnCounter)
break;
if (HasBattlerSideMoveWithEffect(battlerAtk, EFFECT_ELECTRO_BALL))
ADJUST_SCORE(WEAK_EFFECT);
if (isBattle1v1)
{
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPEED);
if (CountUsablePartyMons(battlerAtk) != 0)
ADJUST_SCORE(WEAK_EFFECT);
}
else
{
u32 tailwindScore = 0;
u32 speed = aiData->speedStats[battlerAtk];
u32 partnerSpeed = aiData->speedStats[BATTLE_PARTNER(battlerAtk)];
u32 foe1Speed = aiData->speedStats[FOE(battlerAtk)];
u32 foe2Speed = aiData->speedStats[BATTLE_PARTNER(FOE(battlerAtk))];
if (speed <= foe1Speed && (speed * 2) > foe1Speed)
tailwindScore += 1;
if (speed <= foe2Speed && (speed * 2) > foe2Speed)
tailwindScore += 1;
if (partnerSpeed <= foe1Speed && (speed * 2) > foe1Speed)
tailwindScore += 1;
if (partnerSpeed <= foe1Speed && (speed * 2) > foe1Speed)
tailwindScore += 1;
if (tailwindScore > 0)
tailwindScore += 1;
ADJUST_SCORE(tailwindScore);
}
break;
}
case EFFECT_LUCKY_CHANT:
if (isBattle1v1 && CountUsablePartyMons(battlerDef) > 0)
ADJUST_SCORE(GOOD_EFFECT);

View File

@ -3687,27 +3687,6 @@ bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof)
return FALSE;
}
u32 GetBattlerSideSpeedAverage(u32 battler)
{
u32 speed1 = 0;
u32 speed2 = 0;
u32 numBattlersAlive = 0;
if (IsBattlerAlive(battler))
{
speed1 = gAiLogicData->speedStats[battler];
numBattlersAlive++;
}
if (HasPartner(battler))
{
speed2 = gAiLogicData->speedStats[BATTLE_PARTNER(battler)];
numBattlersAlive++;
}
return (speed1 + speed2) / numBattlersAlive;
}
bool32 ShouldUseRecoilMove(u32 battlerAtk, u32 battlerDef, u32 recoilDmg, u32 moveIndex)
{
if (recoilDmg >= gBattleMons[battlerAtk].hp //Recoil kills attacker

View File

@ -340,6 +340,24 @@ AI_SINGLE_BATTLE_TEST("AI uses Skill Swap against Poison Heal")
}
}
AI_SINGLE_BATTLE_TEST("AI uses Trick Room (singles)")
{
u32 speed;
PARAMETRIZE { speed = 10; }
PARAMETRIZE { speed = 20; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT);
PLAYER(SPECIES_WOBBUFFET) { Speed(11); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(speed); Moves(MOVE_TACKLE, MOVE_TRICK_ROOM); }
} WHEN {
if (speed == 10)
TURN { EXPECT_MOVE(opponent, MOVE_TRICK_ROOM); }
else
TURN { NOT_EXPECT_MOVE(opponent, MOVE_TRICK_ROOM); }
}
}
AI_SINGLE_BATTLE_TEST("AI uses Quick Guard against Quick Attack when opponent would take poison damage")
{
PASSES_RANDOMLY(PREDICT_MOVE_CHANCE, 100, RNG_AI_PREDICT_MOVE);

View File

@ -594,6 +594,34 @@ AI_DOUBLE_BATTLE_TEST("AI uses Trick Room intelligently")
TURN { NOT_EXPECT_MOVE(opponentRight, MOVE_TRICK_ROOM); }
}
}
AI_DOUBLE_BATTLE_TEST("AI uses Tailwind")
{
u32 speed1, speed2, speed3, speed4;
PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 20; speed4 = 20; }
PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 5; speed4 = 5; }
PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 15; speed4 = 15; }
PARAMETRIZE { speed1 = 1; speed2 = 1; speed3 = 5; speed4 = 5; }
PARAMETRIZE { speed1 = 1; speed2 = 20; speed3 = 15; speed4 = 15; }
PARAMETRIZE { speed1 = 1; speed2 = 20; speed3 = 20; speed4 = 15; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_AFTER_YOU) == EFFECT_AFTER_YOU);
ASSUME(GetMoveEffect(MOVE_TRICK_ROOM) == EFFECT_TRICK_ROOM);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_DOUBLE_BATTLE);
PLAYER(SPECIES_WOBBUFFET) { Speed(speed1); }
PLAYER(SPECIES_WOBBUFFET) { Speed(speed2); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(speed3); Moves(MOVE_TAILWIND, MOVE_HEADBUTT); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(speed4); Moves(MOVE_TAILWIND, MOVE_HEADBUTT); }
} WHEN {
if (speed3 > 10)
TURN { EXPECT_MOVE(opponentLeft, MOVE_TAILWIND); }
else
TURN { NOT_EXPECT_MOVE(opponentLeft, MOVE_TAILWIND); }
}
}
AI_DOUBLE_BATTLE_TEST("AI uses Guard Split to improve its stats")
{