From 919fb184fef007829b1aa242c5776ace52a240fc Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Mon, 27 Feb 2023 09:12:52 +0100 Subject: [PATCH] AI knows how to handle Illusion (#2726) * AI knows how to handle Illusion --- include/battle.h | 1 + src/battle_ai_util.c | 66 ++++++++++++++++++++++++++++++++++++++------ src/battle_util.c | 28 +++++++++++++++---- 3 files changed, 80 insertions(+), 15 deletions(-) diff --git a/include/battle.h b/include/battle.h index 611041366..1ae49b1a2 100644 --- a/include/battle.h +++ b/include/battle.h @@ -252,6 +252,7 @@ struct AI_SavedBattleMon u16 moves[MAX_MON_MOVES]; u16 heldItem; u16 species; + u8 types[3]; }; struct AiPartyMon diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 22e88aac4..495e2ea1d 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -538,23 +538,73 @@ void SaveBattlerData(u8 battlerId) AI_THINKING_STRUCT->saved[battlerId].species = gBattleMons[battlerId].species; for (i = 0; i < 4; i++) AI_THINKING_STRUCT->saved[battlerId].moves[i] = gBattleMons[battlerId].moves[i]; + AI_THINKING_STRUCT->saved[battlerId].types[0] = gBattleMons[battlerId].type1; + AI_THINKING_STRUCT->saved[battlerId].types[1] = gBattleMons[battlerId].type2; } } +static bool32 ShouldFailForIllusion(u16 illusionSpecies, u32 battlerId) +{ + u32 i, j; + + if (BATTLE_HISTORY->abilities[battlerId] == ABILITY_ILLUSION) + return FALSE; + + // Don't fall for Illusion if the mon used a move it cannot know. + for (i = 0; i < MAX_MON_MOVES; i++) + { + u16 move = BATTLE_HISTORY->usedMoves[battlerId][i]; + if (move == MOVE_NONE) + continue; + + for (j = 0; gLevelUpLearnsets[illusionSpecies][j].move != MOVE_UNAVAILABLE; j++) + { + if (gLevelUpLearnsets[illusionSpecies][j].move == move) + break; + } + // The used move is in the learnsets of the fake species. + if (gLevelUpLearnsets[illusionSpecies][j].move != MOVE_UNAVAILABLE) + continue; + + // The used move can be learned from Tm/Hm or Move Tutors. + if (CanLearnTeachableMove(illusionSpecies, move)) + continue; + + // 'Illegal move', AI won't fail for the illusion. + return FALSE; + } + + return TRUE; +} + void SetBattlerData(u8 battlerId) { if (!IsBattlerAIControlled(battlerId)) { - struct Pokemon *illusionMon; - u32 i; + u32 i, species, illusionSpecies; + + // Simulate Illusion + species = gBattleMons[battlerId].species; + illusionSpecies = GetIllusionMonSpecies(battlerId); + if (illusionSpecies != SPECIES_NONE && ShouldFailForIllusion(illusionSpecies, battlerId)) + { + // If the battler's type has not been changed, AI assumes the types of the illusion mon. + if (gBattleMons[battlerId].type1 == gSpeciesInfo[species].types[0] + && gBattleMons[battlerId].type2 == gSpeciesInfo[species].types[1]) + { + gBattleMons[battlerId].type1 = gSpeciesInfo[illusionSpecies].types[0]; + gBattleMons[battlerId].type2 = gSpeciesInfo[illusionSpecies].types[1]; + } + species = illusionSpecies; + } // Use the known battler's ability. if (BATTLE_HISTORY->abilities[battlerId] != ABILITY_NONE) gBattleMons[battlerId].ability = BATTLE_HISTORY->abilities[battlerId]; // Check if mon can only have one ability. - else if (gSpeciesInfo[gBattleMons[battlerId].species].abilities[1] == ABILITY_NONE - || gSpeciesInfo[gBattleMons[battlerId].species].abilities[1] == gSpeciesInfo[gBattleMons[battlerId].species].abilities[0]) - gBattleMons[battlerId].ability = gSpeciesInfo[gBattleMons[battlerId].species].abilities[0]; + else if (gSpeciesInfo[species].abilities[1] == ABILITY_NONE + || gSpeciesInfo[species].abilities[1] == gSpeciesInfo[species].abilities[0]) + gBattleMons[battlerId].ability = gSpeciesInfo[species].abilities[0]; // The ability is unknown. else gBattleMons[battlerId].ability = ABILITY_NONE; @@ -567,10 +617,6 @@ void SetBattlerData(u8 battlerId) if (BATTLE_HISTORY->usedMoves[battlerId][i] == 0) gBattleMons[battlerId].moves[i] = 0; } - - // Simulate Illusion - if ((illusionMon = GetIllusionMonPtr(battlerId)) != NULL) - gBattleMons[battlerId].species = GetMonData(illusionMon, MON_DATA_SPECIES2); } } @@ -585,6 +631,8 @@ void RestoreBattlerData(u8 battlerId) gBattleMons[battlerId].species = AI_THINKING_STRUCT->saved[battlerId].species; for (i = 0; i < 4; i++) gBattleMons[battlerId].moves[i] = AI_THINKING_STRUCT->saved[battlerId].moves[i]; + gBattleMons[battlerId].type1 = AI_THINKING_STRUCT->saved[battlerId].types[0]; + gBattleMons[battlerId].type2 = AI_THINKING_STRUCT->saved[battlerId].types[1]; } } diff --git a/src/battle_util.c b/src/battle_util.c index 0a00372c0..e10c9dbcf 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -9817,6 +9817,18 @@ static void MulByTypeEffectiveness(u16 *modifier, u16 move, u8 moveType, u8 batt MulModifier(modifier, mod); } +static void TryNoticeIllusionInTypeEffectiveness(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, u16 resultingModifier, u32 illusionSpecies) +{ + // Check if the type effectiveness would've been different if the pokemon really had the types as the disguise. + u16 presumedModifier = UQ_4_12(1.0); + MulByTypeEffectiveness(&presumedModifier, move, moveType, battlerDef, gSpeciesInfo[illusionSpecies].types[0], battlerAtk, FALSE); + if (gSpeciesInfo[illusionSpecies].types[1] != gSpeciesInfo[illusionSpecies].types[0]) + MulByTypeEffectiveness(&presumedModifier, move, moveType, battlerDef, gSpeciesInfo[illusionSpecies].types[1], battlerAtk, FALSE); + + if (presumedModifier != resultingModifier) + RecordAbilityBattle(battlerDef, ABILITY_ILLUSION); +} + static void UpdateMoveResultFlags(u16 modifier) { if (modifier == UQ_4_12(0.0)) @@ -9842,6 +9854,7 @@ static void UpdateMoveResultFlags(u16 modifier) static u16 CalcTypeEffectivenessMultiplierInternal(u16 move, u8 moveType, u8 battlerAtk, u8 battlerDef, bool32 recordAbilities, u16 modifier) { + u32 illusionSpecies; u16 defAbility = GetBattlerAbility(battlerDef); MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, gBattleMons[battlerDef].type1, battlerAtk, recordAbilities); @@ -9851,15 +9864,18 @@ static u16 CalcTypeEffectivenessMultiplierInternal(u16 move, u8 moveType, u8 bat && gBattleMons[battlerDef].type3 != gBattleMons[battlerDef].type1) MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, gBattleMons[battlerDef].type3, battlerAtk, recordAbilities); + if (recordAbilities && (illusionSpecies = GetIllusionMonSpecies(battlerDef))) + TryNoticeIllusionInTypeEffectiveness(move, moveType, battlerAtk, battlerDef, modifier, illusionSpecies); + if (gBattleMoves[move].split == SPLIT_STATUS && move != MOVE_THUNDER_WAVE) { modifier = UQ_4_12(1.0); - #if B_GLARE_GHOST <= GEN_3 - if (move == MOVE_GLARE && IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST)) - { - modifier = UQ_4_12(0.0); - } - #endif + #if B_GLARE_GHOST <= GEN_3 + if (move == MOVE_GLARE && IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST)) + { + modifier = UQ_4_12(0.0); + } + #endif } else if (moveType == TYPE_GROUND && !IsBattlerGrounded2(battlerDef, TRUE) && !(gBattleMoves[move].flags & FLAG_DMG_UNGROUNDED_IGNORE_TYPE_IF_FLYING)) {