From 23b880479058716cdc882611da2db2b1266eafd2 Mon Sep 17 00:00:00 2001 From: sneed <56992013+Sneed69@users.noreply.github.com> Date: Tue, 14 May 2024 14:09:26 +0300 Subject: [PATCH] Various AI switching logic fixes (#4571) --- src/battle_ai_switch_items.c | 96 +++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 39 deletions(-) diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 273fb2c998..6e1cf45d64 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -1279,6 +1279,9 @@ static u32 GetSwitchinHazardsDamage(u32 battler, struct BattlePokemon *battleMon // Stealth Rock if ((hazardFlags & SIDE_STATUS_STEALTH_ROCK) && heldItemEffect != HOLD_EFFECT_HEAVY_DUTY_BOOTS) hazardDamage += GetStealthHazardDamageByTypesAndHP(gMovesInfo[MOVE_STEALTH_ROCK].type, defType1, defType2, battleMon->maxHP); + // G-Max Steelsurge + if ((hazardFlags & SIDE_STATUS_STEELSURGE) && heldItemEffect != HOLD_EFFECT_HEAVY_DUTY_BOOTS) + hazardDamage += GetStealthHazardDamageByTypesAndHP(gMovesInfo[MOVE_G_MAX_STEELSURGE].type, defType1, defType2, battleMon->maxHP); // Spikes if ((hazardFlags & SIDE_STATUS_SPIKES) && IsMonGrounded(heldItemEffect, ability, defType1, defType2)) { @@ -1323,40 +1326,46 @@ static u32 GetSwitchinHazardsDamage(u32 battler, struct BattlePokemon *battleMon // Gets damage / healing from weather static s32 GetSwitchinWeatherImpact(void) { - s32 weatherImpact = 0, maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, ability = AI_DATA->switchinCandidate.battleMon.ability; - u16 item = AI_DATA->switchinCandidate.battleMon.item; + s32 weatherDamage = 0, maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, ability = AI_DATA->switchinCandidate.battleMon.ability; + u32 holdEffect = gItemsInfo[AI_DATA->switchinCandidate.battleMon.item].holdEffect; if (WEATHER_HAS_EFFECT) { + s32 weatherImpact = 0; // Damage - if (item != ITEM_SAFETY_GOGGLES) + if (holdEffect != HOLD_EFFECT_SAFETY_GOGGLES && ability != ABILITY_MAGIC_GUARD && ability != ABILITY_OVERCOAT) { - if ((gBattleWeather & B_WEATHER_HAIL) && (AI_DATA->switchinCandidate.battleMon.type1 != TYPE_ICE || AI_DATA->switchinCandidate.battleMon.type2 != TYPE_ICE) - && ability != ABILITY_OVERCOAT && ability != ABILITY_SNOW_CLOAK && ability != ABILITY_ICE_BODY) + if ((gBattleWeather & B_WEATHER_HAIL) + && (AI_DATA->switchinCandidate.battleMon.type1 != TYPE_ICE || AI_DATA->switchinCandidate.battleMon.type2 != TYPE_ICE) + && ability != ABILITY_SNOW_CLOAK && ability != ABILITY_ICE_BODY) { weatherImpact = maxHP / 16; if (weatherImpact == 0) weatherImpact = 1; } - else if ((gBattleWeather & B_WEATHER_SANDSTORM) && (AI_DATA->switchinCandidate.battleMon.type1 != TYPE_GROUND && AI_DATA->switchinCandidate.battleMon.type2 != TYPE_GROUND + else if ((gBattleWeather & B_WEATHER_SANDSTORM) + && (AI_DATA->switchinCandidate.battleMon.type1 != TYPE_GROUND && AI_DATA->switchinCandidate.battleMon.type2 != TYPE_GROUND && AI_DATA->switchinCandidate.battleMon.type1 != TYPE_ROCK && AI_DATA->switchinCandidate.battleMon.type2 != TYPE_ROCK && AI_DATA->switchinCandidate.battleMon.type1 != TYPE_STEEL && AI_DATA->switchinCandidate.battleMon.type2 != TYPE_STEEL - && ability != ABILITY_OVERCOAT && ability != ABILITY_SAND_VEIL && ability != ABILITY_SAND_RUSH && ability != ABILITY_SAND_FORCE)) + && ability != ABILITY_SAND_VEIL && ability != ABILITY_SAND_RUSH && ability != ABILITY_SAND_FORCE)) { weatherImpact = maxHP / 16; if (weatherImpact == 0) weatherImpact = 1; } } - if ((gBattleWeather & B_WEATHER_SUN) && (ability == ABILITY_SOLAR_POWER || ability == ABILITY_DRY_SKIN)) + if ((gBattleWeather & B_WEATHER_SUN) && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA + && (ability == ABILITY_SOLAR_POWER || ability == ABILITY_DRY_SKIN)) { weatherImpact = maxHP / 8; if (weatherImpact == 0) weatherImpact = 1; } + weatherDamage += weatherImpact; + // Healing - if (gBattleWeather & B_WEATHER_RAIN) + if (gBattleWeather & B_WEATHER_RAIN && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA) { if (ability == ABILITY_DRY_SKIN) { @@ -1375,28 +1384,30 @@ static s32 GetSwitchinWeatherImpact(void) { weatherImpact = maxHP / 16; if (weatherImpact == 0) - weatherImpact =1; + weatherImpact = 1; } + + weatherDamage -= weatherImpact; } - return weatherImpact; + return weatherDamage; } // Gets one turn of recurring healing static u32 GetSwitchinRecurringHealing(void) { u32 recurringHealing = 0, maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, ability = AI_DATA->switchinCandidate.battleMon.ability; - u16 item = AI_DATA->switchinCandidate.battleMon.item; + u32 holdEffect = gItemsInfo[AI_DATA->switchinCandidate.battleMon.item].holdEffect; // Items if (ability != ABILITY_KLUTZ) { - if (item == ITEM_BLACK_SLUDGE && (AI_DATA->switchinCandidate.battleMon.type1 == TYPE_POISON || AI_DATA->switchinCandidate.battleMon.type2 == TYPE_POISON)) + if (holdEffect == HOLD_EFFECT_BLACK_SLUDGE && (AI_DATA->switchinCandidate.battleMon.type1 == TYPE_POISON || AI_DATA->switchinCandidate.battleMon.type2 == TYPE_POISON)) { recurringHealing = maxHP / 16; if (recurringHealing == 0) recurringHealing = 1; } - else if (item == ITEM_LEFTOVERS) + else if (holdEffect == HOLD_EFFECT_LEFTOVERS) { recurringHealing = maxHP / 16; if (recurringHealing == 0) @@ -1407,9 +1418,10 @@ static u32 GetSwitchinRecurringHealing(void) // Abilities if (ability == ABILITY_POISON_HEAL && (AI_DATA->switchinCandidate.battleMon.status1 & STATUS1_POISON)) { - recurringHealing = maxHP / 8; - if (recurringHealing == 0) - recurringHealing = 1; + u32 healing = maxHP / 8; + if (healing == 0) + healing = 1; + recurringHealing += healing; } return recurringHealing; } @@ -1418,24 +1430,24 @@ static u32 GetSwitchinRecurringHealing(void) static u32 GetSwitchinRecurringDamage(void) { u32 passiveDamage = 0, maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, ability = AI_DATA->switchinCandidate.battleMon.ability; - u16 item = AI_DATA->switchinCandidate.battleMon.item; + u32 holdEffect = gItemsInfo[AI_DATA->switchinCandidate.battleMon.item].holdEffect; // Items if (ability != ABILITY_MAGIC_GUARD && ability != ABILITY_KLUTZ) { - if (item == ITEM_BLACK_SLUDGE && AI_DATA->switchinCandidate.battleMon.type1 != TYPE_POISON && AI_DATA->switchinCandidate.battleMon.type2 != TYPE_POISON) + if (holdEffect == HOLD_EFFECT_BLACK_SLUDGE && AI_DATA->switchinCandidate.battleMon.type1 != TYPE_POISON && AI_DATA->switchinCandidate.battleMon.type2 != TYPE_POISON) { passiveDamage = maxHP / 8; if (passiveDamage == 0) passiveDamage = 1; } - else if (item == ITEM_LIFE_ORB && ability != ABILITY_SHEER_FORCE) + else if (holdEffect == HOLD_EFFECT_LIFE_ORB && ability != ABILITY_SHEER_FORCE) { passiveDamage = maxHP / 10; if (passiveDamage == 0) passiveDamage = 1; } - else if (item == ITEM_STICKY_BARB) + else if (holdEffect == HOLD_EFFECT_STICKY_BARB) { passiveDamage = maxHP / 8; if(passiveDamage == 0) @@ -1527,8 +1539,8 @@ static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler) u32 recurringHealing = GetSwitchinRecurringHealing(); u32 statusDamage = GetSwitchinStatusDamage(battler); u32 hitsToKO = 0, singleUseItemHeal = 0; - u16 maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, item = AI_DATA->switchinCandidate.battleMon.item, heldItemEffect = gItemsInfo[AI_DATA->switchinCandidate.battleMon.item].holdEffect; - u8 weatherDuration = gWishFutureKnock.weatherDuration, holdEffectParam = gItemsInfo[AI_DATA->switchinCandidate.battleMon.item].holdEffectParam; + u16 maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, item = AI_DATA->switchinCandidate.battleMon.item, heldItemEffect = gItemsInfo[item].holdEffect; + u8 weatherDuration = gWishFutureKnock.weatherDuration, holdEffectParam = gItemsInfo[item].holdEffectParam; u32 opposingBattler = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerPosition(battler))); u32 opposingAbility = gBattleMons[opposingBattler].ability; bool32 usedSingleUseHealingItem = FALSE; @@ -1557,29 +1569,35 @@ static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler) currentHP = currentHP + weatherImpact; // Check if we're at a single use healing item threshold - if (AI_DATA->switchinCandidate.battleMon.ability != ABILITY_KLUTZ && usedSingleUseHealingItem == FALSE) + if (AI_DATA->switchinCandidate.battleMon.ability != ABILITY_KLUTZ && usedSingleUseHealingItem == FALSE + && !(opposingAbility == ABILITY_UNNERVE && GetPocketByItemId(item) == POCKET_BERRIES)) { - if (currentHP < maxHP / 2) + switch (heldItemEffect) { - if (item == ITEM_BERRY_JUICE) - { + case HOLD_EFFECT_RESTORE_HP: + if (currentHP < maxHP / 2) singleUseItemHeal = holdEffectParam; - } - else if (opposingAbility != ABILITY_UNNERVE && heldItemEffect == HOLD_EFFECT_RESTORE_HP) + break; + case HOLD_EFFECT_RESTORE_PCT_HP: + if (currentHP < maxHP / 2) { - // By default, this should only encompass Oran Berry and Sitrus Berry. - singleUseItemHeal = holdEffectParam; + singleUseItemHeal = maxHP / holdEffectParam; if (singleUseItemHeal == 0) singleUseItemHeal = 1; } - } - else if (currentHP < maxHP / CONFUSE_BERRY_HP_FRACTION - && opposingAbility != ABILITY_UNNERVE - && (item == ITEM_AGUAV_BERRY || item == ITEM_FIGY_BERRY || item == ITEM_IAPAPA_BERRY || item == ITEM_MAGO_BERRY || item == ITEM_WIKI_BERRY)) - { - singleUseItemHeal = maxHP / CONFUSE_BERRY_HEAL_FRACTION; - if (singleUseItemHeal == 0) - singleUseItemHeal = 1; + break; + case HOLD_EFFECT_CONFUSE_SPICY: + case HOLD_EFFECT_CONFUSE_DRY: + case HOLD_EFFECT_CONFUSE_SWEET: + case HOLD_EFFECT_CONFUSE_BITTER: + case HOLD_EFFECT_CONFUSE_SOUR: + if (currentHP < maxHP / CONFUSE_BERRY_HP_FRACTION) + { + singleUseItemHeal = maxHP / holdEffectParam; + if (singleUseItemHeal == 0) + singleUseItemHeal = 1; + } + break; } // If we used one, apply it without overcapping our maxHP