Improve AI type matchup calcs (#7364)
Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com>
This commit is contained in:
parent
cb66393df7
commit
a92f432daf
@ -31,6 +31,7 @@ static bool32 AI_OpponentCanFaintAiWithMod(u32 battler, u32 healAmount);
|
||||
static u32 GetSwitchinHazardsDamage(u32 battler, struct BattlePokemon *battleMon);
|
||||
static bool32 CanAbilityTrapOpponent(u16 ability, u32 opponent);
|
||||
static u32 GetHPHealAmount(u8 itemEffectParam, struct Pokemon *mon);
|
||||
static u32 GetBattleMonTypeMatchup(struct BattlePokemon opposingBattleMon, struct BattlePokemon battleMon);
|
||||
|
||||
static void InitializeSwitchinCandidate(struct Pokemon *mon)
|
||||
{
|
||||
@ -176,11 +177,11 @@ static bool32 AI_DoesChoiceEffectBlockMove(u32 battler, u32 move)
|
||||
static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
|
||||
{
|
||||
//Variable initialization
|
||||
u8 opposingPosition, atkType1, atkType2, defType1, defType2;
|
||||
u8 opposingPosition;
|
||||
s32 i, damageDealt = 0, maxDamageDealt = 0, damageTaken = 0, maxDamageTaken = 0;
|
||||
u32 aiMove, playerMove, aiBestMove = MOVE_NONE, aiAbility = gAiLogicData->abilities[battler], opposingBattler;
|
||||
bool32 getsOneShot = FALSE, hasStatusMove = FALSE, hasSuperEffectiveMove = FALSE;
|
||||
u16 typeEffectiveness = UQ_4_12(1.0); //baseline typing damage
|
||||
u32 typeMatchup;
|
||||
enum BattleMoveEffects aiMoveEffect;
|
||||
u32 hitsToKoPlayer = 0, hitsToKoAI = 0;
|
||||
|
||||
@ -196,12 +197,6 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
|
||||
opposingBattler = GetBattlerAtPosition(opposingPosition);
|
||||
u16 *playerMoves = GetMovesArray(opposingBattler);
|
||||
|
||||
// Gets types of player (opposingBattler) and computer (battler)
|
||||
atkType1 = gBattleMons[opposingBattler].types[0];
|
||||
atkType2 = gBattleMons[opposingBattler].types[1];
|
||||
defType1 = gBattleMons[battler].types[0];
|
||||
defType2 = gBattleMons[battler].types[1];
|
||||
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
aiMove = gBattleMons[battler].moves[i];
|
||||
@ -245,15 +240,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
|
||||
hitsToKoPlayer = GetNoOfHitsToKOBattlerDmg(maxDamageDealt, opposingBattler);
|
||||
|
||||
// Calculate type advantage
|
||||
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType1, defType1)));
|
||||
if (atkType2 != atkType1)
|
||||
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType2, defType1)));
|
||||
if (defType2 != defType1)
|
||||
{
|
||||
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType1, defType2)));
|
||||
if (atkType2 != atkType1)
|
||||
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType2, defType2)));
|
||||
}
|
||||
typeMatchup = GetBattleMonTypeMatchup(gBattleMons[opposingBattler], gBattleMons[battler]);
|
||||
|
||||
// Get max damage mon could take
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
@ -303,7 +290,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
|
||||
}
|
||||
|
||||
// General bad type matchups have more wiggle room
|
||||
if (typeEffectiveness >= UQ_4_12(2.0)) // If the player has at least a 2x type advantage
|
||||
if (typeMatchup > UQ_4_12(2.0)) // If the player has favourable offensive matchup (2.0 is neutral, this must be worse)
|
||||
{
|
||||
if (!hasSuperEffectiveMove // If the AI doesn't have a super effective move
|
||||
&& (gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 2 // And the current mon has at least 1/2 their HP, or 1/4 HP and Regenerator
|
||||
@ -1902,25 +1889,34 @@ static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler)
|
||||
return hitsToKO;
|
||||
}
|
||||
|
||||
static u16 GetSwitchinTypeMatchup(u32 opposingBattler, struct BattlePokemon battleMon)
|
||||
static u32 GetBattleMonTypeMatchup(struct BattlePokemon opposingBattleMon, struct BattlePokemon battleMon)
|
||||
{
|
||||
|
||||
// Check type matchup
|
||||
u16 typeEffectiveness = UQ_4_12(1.0);
|
||||
u8 atkType1 = GetSpeciesType(gBattleMons[opposingBattler].species, 0), atkType2 = GetSpeciesType(gBattleMons[opposingBattler].species, 1),
|
||||
u32 typeEffectiveness1 = UQ_4_12(1.0), typeEffectiveness2 = UQ_4_12(1.0);
|
||||
u8 atkType1 = opposingBattleMon.types[0], atkType2 = opposingBattleMon.types[1],
|
||||
defType1 = battleMon.types[0], defType2 = battleMon.types[1];
|
||||
|
||||
// Multiply type effectiveness by a factor depending on type matchup
|
||||
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType1, defType1)));
|
||||
if (atkType2 != atkType1)
|
||||
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType2, defType1)));
|
||||
// Add each independent defensive type matchup together
|
||||
typeEffectiveness1 = uq4_12_multiply(typeEffectiveness1, (GetTypeModifier(atkType1, defType1)));
|
||||
if (defType2 != defType1)
|
||||
typeEffectiveness1 = uq4_12_multiply(typeEffectiveness1, (GetTypeModifier(atkType1, defType2)));
|
||||
if (typeEffectiveness1 == 0) // Immunity
|
||||
typeEffectiveness1 = UQ_4_12(0.1);
|
||||
|
||||
if (atkType2 != atkType1)
|
||||
{
|
||||
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType1, defType2)));
|
||||
if (atkType2 != atkType1)
|
||||
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType2, defType2)));
|
||||
typeEffectiveness2 = uq4_12_multiply(typeEffectiveness2, (GetTypeModifier(atkType2, defType1)));
|
||||
if (defType2 != defType1)
|
||||
typeEffectiveness2 = uq4_12_multiply(typeEffectiveness2, (GetTypeModifier(atkType2, defType2)));
|
||||
if (typeEffectiveness2 == 0) // Immunity
|
||||
typeEffectiveness2 = UQ_4_12(0.1);
|
||||
}
|
||||
return typeEffectiveness;
|
||||
else
|
||||
{
|
||||
typeEffectiveness2 = typeEffectiveness1;
|
||||
}
|
||||
|
||||
return typeEffectiveness1 + typeEffectiveness2;
|
||||
}
|
||||
|
||||
static int GetRandomSwitchinWithBatonPass(int aliveCount, int bits, int firstId, int lastId, int currentMonId)
|
||||
@ -2049,7 +2045,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
|
||||
s32 defensiveMonHitKOThreshold = 3; // 3HKO threshold that candidate defensive mons must exceed
|
||||
s32 playerMonHP = gBattleMons[opposingBattler].hp, maxDamageDealt = 0, damageDealt = 0;
|
||||
u32 aiMove, hitsToKOAI, maxHitsToKO = 0;
|
||||
u16 bestResist = UQ_4_12(1.0), bestResistEffective = UQ_4_12(1.0), typeMatchup;
|
||||
u32 bestResist = UQ_4_12(2.0), bestResistEffective = UQ_4_12(2.0), typeMatchup; // 2.0 is the default "Neutral" matchup from GetBattleMonTypeMatchup
|
||||
bool32 isFreeSwitch = IsFreeSwitch(switchType, battlerIn1, opposingBattler), isSwitchinFirst, canSwitchinWin1v1;
|
||||
|
||||
// Iterate through mons
|
||||
@ -2082,7 +2078,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
|
||||
|
||||
// Get max number of hits for player to KO AI mon and type matchup for defensive switching
|
||||
hitsToKOAI = GetSwitchinHitsToKO(GetMaxDamagePlayerCouldDealToSwitchin(battler, opposingBattler, gAiLogicData->switchinCandidate.battleMon), battler);
|
||||
typeMatchup = GetSwitchinTypeMatchup(opposingBattler, gAiLogicData->switchinCandidate.battleMon);
|
||||
typeMatchup = GetBattleMonTypeMatchup(gBattleMons[opposingBattler], gAiLogicData->switchinCandidate.battleMon);
|
||||
|
||||
// Check through current mon's moves
|
||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||
|
||||
@ -1286,3 +1286,16 @@ AI_SINGLE_BATTLE_TEST("AI_SMART_MON_CHOICES: AI sees its own weather setting abi
|
||||
TURN { MOVE(player, MOVE_SCRATCH); EXPECT_MOVE(opponent, MOVE_SCRATCH); EXPECT_SEND_OUT(opponent, 2); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI will properly consider immunities when determining switchin type matchup")
|
||||
{
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT);
|
||||
PLAYER(SPECIES_POLIWRATH) { Moves(MOVE_WATER_GUN, MOVE_KARATE_CHOP); }
|
||||
OPPONENT(SPECIES_ZIGZAGOON) { Level(1); Moves(MOVE_SCRATCH); }
|
||||
OPPONENT(SPECIES_CERULEDGE) { Moves(MOVE_SPARK); }
|
||||
OPPONENT(SPECIES_WHIMSICOTT) { Moves(MOVE_MEGA_DRAIN); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_KARATE_CHOP); EXPECT_MOVE(opponent, MOVE_SCRATCH); EXPECT_SEND_OUT(opponent, 2); }
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user