diff --git a/include/battle.h b/include/battle.h index 35b70a8032..2155c6ac6e 100644 --- a/include/battle.h +++ b/include/battle.h @@ -855,9 +855,29 @@ STATIC_ASSERT(sizeof(((struct BattleStruct *)0)->palaceFlags) * 8 >= MAX_BATTLER #define TARGET_TURN_DAMAGED ((gSpecialStatuses[gBattlerTarget].physicalDmg != 0 || gSpecialStatuses[gBattlerTarget].specialDmg != 0) || (gBattleStruct->enduredDamage & (1u << gBattlerTarget))) #define BATTLER_TURN_DAMAGED(battlerId) ((gSpecialStatuses[battlerId].physicalDmg != 0 || gSpecialStatuses[battlerId].specialDmg != 0) || (gBattleStruct->enduredDamage & (1u << battler))) -#define IS_BATTLER_OF_TYPE(battlerId, type)((GetBattlerType(battlerId, 0, FALSE) == type || GetBattlerType(battlerId, 1, FALSE) == type || (GetBattlerType(battlerId, 2, FALSE) != TYPE_MYSTERY && GetBattlerType(battlerId, 2, FALSE) == type))) -#define IS_BATTLER_OF_BASE_TYPE(battlerId, type)((GetBattlerType(battlerId, 0, TRUE) == type || GetBattlerType(battlerId, 1, TRUE) == type || (GetBattlerType(battlerId, 2, TRUE) != TYPE_MYSTERY && GetBattlerType(battlerId, 2, TRUE) == type))) -#define IS_BATTLER_TYPELESS(battlerId)(GetBattlerType(battlerId, 0, FALSE) == TYPE_MYSTERY && GetBattlerType(battlerId, 1, FALSE) == TYPE_MYSTERY && GetBattlerType(battlerId, 2, FALSE) == TYPE_MYSTERY) +/* Checks if 'battlerId' is any of the types. + * Passing multiple types is more efficient than calling this multiple + * times with one type because it shares the 'GetBattlerTypes' result. */ +#define _IS_BATTLER_ANY_TYPE(battlerId, ignoreTera, ...) \ + ({ \ + u32 types[3]; \ + GetBattlerTypes(battlerId, ignoreTera, types); \ + RECURSIVELY(R_FOR_EACH(_IS_BATTLER_ANY_TYPE_HELPER, __VA_ARGS__)) FALSE; \ + }) + +#define _IS_BATTLER_ANY_TYPE_HELPER(type) (types[0] == type) || (types[1] == type) || (types[2] == type) || + +#define IS_BATTLER_ANY_TYPE(battlerId, ...) _IS_BATTLER_ANY_TYPE(battlerId, FALSE, __VA_ARGS__) +#define IS_BATTLER_OF_TYPE IS_BATTLER_ANY_TYPE +#define IS_BATTLER_ANY_BASE_TYPE(battlerId, ...) _IS_BATTLER_ANY_TYPE(battlerId, TRUE, __VA_ARGS__) +#define IS_BATTLER_OF_BASE_TYPE IS_BATTLER_ANY_BASE_TYPE + +#define IS_BATTLER_TYPELESS(battlerId) \ + ({ \ + u32 types[3]; \ + GetBattlerTypes(battlerId, FALSE, types); \ + types[0] == TYPE_MYSTERY && types[1] == TYPE_MYSTERY && types[2] == TYPE_MYSTERY; \ + }) #define SET_BATTLER_TYPE(battlerId, type) \ { \ diff --git a/include/battle_util.h b/include/battle_util.h index b2a28001ab..9b22dc583d 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -309,7 +309,8 @@ bool32 AreBattlersOfOppositeGender(u32 battler1, u32 battler2); bool32 AreBattlersOfSameGender(u32 battler1, u32 battler2); u32 CalcSecondaryEffectChance(u32 battler, u32 battlerAbility, const struct AdditionalEffect *additionalEffect); bool32 MoveEffectIsGuaranteed(u32 battler, u32 battlerAbility, const struct AdditionalEffect *additionalEffect); -u8 GetBattlerType(u32 battler, u8 typeIndex, bool32 ignoreTera); +void GetBattlerTypes(u32 battler, bool32 ignoreTera, u32 types[static 3]); +u32 GetBattlerType(u32 battler, u32 typeIndex, bool32 ignoreTera); bool8 CanMonParticipateInSkyBattle(struct Pokemon *mon); bool8 IsMonBannedFromSkyBattles(u16 species); void RemoveBattlerType(u32 battler, u8 type); diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 28ac8f48d1..cb85ba8c78 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -2354,12 +2354,15 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-10); break; case EFFECT_SOAK: + { + u32 types[3]; + GetBattlerTypes(battlerDef, FALSE, types); + // TODO: Use the type of the move like 'VARIOUS_TRY_SOAK'? if (PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) - || (GetBattlerType(battlerDef, 0, FALSE) == TYPE_WATER - && GetBattlerType(battlerDef, 1, FALSE) == TYPE_WATER - && GetBattlerType(battlerDef, 2, FALSE) == TYPE_MYSTERY)) + || (types[0] == TYPE_WATER && types[1] == TYPE_WATER && types[2] == TYPE_MYSTERY)) ADJUST_SCORE(-10); // target is already water-only break; + } case EFFECT_THIRD_TYPE: switch (move) { @@ -4433,7 +4436,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(GOOD_EFFECT); break; case EFFECT_SALT_CURE: - if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_WATER) || IS_BATTLER_OF_TYPE(battlerDef, TYPE_STEEL)) + if (IS_BATTLER_ANY_TYPE(battlerDef, TYPE_WATER, TYPE_STEEL)) ADJUST_SCORE(DECENT_EFFECT); break; } // move effect checks diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 940d73c6ed..a27bc56d72 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -1601,9 +1601,7 @@ bool32 ShouldSetSandstorm(u32 battler, u32 ability, u32 holdEffect) || ability == ABILITY_OVERCOAT || ability == ABILITY_MAGIC_GUARD || holdEffect == HOLD_EFFECT_SAFETY_GOGGLES - || IS_BATTLER_OF_TYPE(battler, TYPE_ROCK) - || IS_BATTLER_OF_TYPE(battler, TYPE_STEEL) - || IS_BATTLER_OF_TYPE(battler, TYPE_GROUND) + || IS_BATTLER_ANY_TYPE(battler, TYPE_ROCK, TYPE_GROUND, TYPE_STEEL) || HasMoveEffect(battler, EFFECT_SHORE_UP) || HasMoveEffect(battler, EFFECT_WEATHER_BALL)) { @@ -2568,9 +2566,7 @@ static u32 GetPoisonDamage(u32 battlerId) static bool32 BattlerAffectedBySandstorm(u32 battlerId, u32 ability) { - if (!IS_BATTLER_OF_TYPE(battlerId, TYPE_ROCK) - && !IS_BATTLER_OF_TYPE(battlerId, TYPE_GROUND) - && !IS_BATTLER_OF_TYPE(battlerId, TYPE_STEEL) + if (!IS_BATTLER_ANY_TYPE(battlerId, TYPE_ROCK, TYPE_GROUND, TYPE_STEEL) && ability != ABILITY_SAND_VEIL && ability != ABILITY_SAND_FORCE && ability != ABILITY_SAND_RUSH @@ -2975,7 +2971,7 @@ bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u3 || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) || PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove)) return FALSE; - else if (defAbility != ABILITY_CORROSION && (IS_BATTLER_OF_TYPE(battlerDef, TYPE_POISON) || IS_BATTLER_OF_TYPE(battlerDef, TYPE_STEEL))) + else if (defAbility != ABILITY_CORROSION && IS_BATTLER_ANY_TYPE(battlerDef, TYPE_POISON, TYPE_STEEL)) return FALSE; else if (IsValidDoubleBattle(battlerAtk) && AI_DATA->abilities[BATTLE_PARTNER(battlerDef)] == ABILITY_PASTEL_VEIL) return FALSE; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 812266b0fc..99b2e0469f 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2213,19 +2213,13 @@ END: // B_WEATHER_STRONG_WINDS prints a string when it's about to reduce the power // of a move that is Super Effective against a Flying-type Pokémon. - if (gBattleWeather & B_WEATHER_STRONG_WINDS) + if ((gBattleWeather & B_WEATHER_STRONG_WINDS) + && GetTypeModifier(moveType, TYPE_FLYING) >= UQ_4_12(2.0) + && IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_FLYING)) { - if ((GetBattlerType(gBattlerTarget, 0, FALSE) == TYPE_FLYING - && GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 0, FALSE)) >= UQ_4_12(2.0)) - || (GetBattlerType(gBattlerTarget, 1, FALSE) == TYPE_FLYING - && GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 1, FALSE)) >= UQ_4_12(2.0)) - || (GetBattlerType(gBattlerTarget, 2, FALSE) == TYPE_FLYING - && GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 2, FALSE)) >= UQ_4_12(2.0))) - { - gBattlerAbility = gBattlerTarget; - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_AttackWeakenedByStrongWinds; - } + gBattlerAbility = gBattlerTarget; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_AttackWeakenedByStrongWinds; } } @@ -8697,7 +8691,7 @@ static bool32 HasAttackerFaintedTarget(void) bool32 CanPoisonType(u8 battlerAttacker, u8 battlerTarget) { return GetBattlerAbility(battlerAttacker) == ABILITY_CORROSION - || (!IS_BATTLER_OF_TYPE(battlerTarget, TYPE_STEEL) && !IS_BATTLER_OF_TYPE(battlerTarget, TYPE_POISON)); + || !IS_BATTLER_ANY_TYPE(battlerTarget, TYPE_POISON, TYPE_STEEL); } bool32 CanParalyzeType(u8 battlerAttacker, u8 battlerTarget) @@ -10012,9 +10006,10 @@ static void Cmd_various(void) case VARIOUS_TRY_SOAK: { VARIOUS_ARGS(const u8 *failInstr); - if ((GetBattlerType(gBattlerTarget, 0, FALSE) == gMovesInfo[gCurrentMove].type - && GetBattlerType(gBattlerTarget, 1, FALSE) == gMovesInfo[gCurrentMove].type) - || GetActiveGimmick(gBattlerTarget) == GIMMICK_TERA) + u32 types[3]; + GetBattlerTypes(gBattlerTarget, FALSE, types); + if ((types[0] == gMovesInfo[gCurrentMove].type && types[1] == gMovesInfo[gCurrentMove].type) + || GetActiveGimmick(gBattlerTarget) == GIMMICK_TERA) { gBattlescriptCurrInstr = cmd->failInstr; } @@ -15188,7 +15183,7 @@ static void Cmd_handleballthrow(void) ballMultiplier = 150; break; case BALL_NET: - if (IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_WATER) || IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_BUG)) + if (IS_BATTLER_ANY_TYPE(gBattlerTarget, TYPE_WATER, TYPE_BUG)) ballMultiplier = B_NET_BALL_MODIFIER >= GEN_7 ? 350 : 300; break; case BALL_DIVE: @@ -16635,9 +16630,8 @@ void BS_TryReflectType(void) { NATIVE_ARGS(const u8 *failInstr); u16 targetBaseSpecies = GET_BASE_SPECIES_ID(gBattleMons[gBattlerTarget].species); - u8 targetType1 = GetBattlerType(gBattlerTarget, 0, FALSE); - u8 targetType2 = GetBattlerType(gBattlerTarget, 1, FALSE); - u8 targetType3 = GetBattlerType(gBattlerTarget, 2, FALSE); + u32 targetTypes[3]; + GetBattlerTypes(gBattlerTarget, FALSE, targetTypes); if (targetBaseSpecies == SPECIES_ARCEUS || targetBaseSpecies == SPECIES_SILVALLY) { @@ -16651,32 +16645,32 @@ void BS_TryReflectType(void) { gBattlescriptCurrInstr = cmd->failInstr; } - else if (targetType1 == TYPE_MYSTERY && targetType2 == TYPE_MYSTERY && targetType3 != TYPE_MYSTERY) + else if (targetTypes[0] == TYPE_MYSTERY && targetTypes[1] == TYPE_MYSTERY && targetTypes[2] != TYPE_MYSTERY) { gBattleMons[gBattlerAttacker].types[0] = TYPE_NORMAL; gBattleMons[gBattlerAttacker].types[1] = TYPE_NORMAL; - gBattleMons[gBattlerAttacker].types[2] = targetType3; + gBattleMons[gBattlerAttacker].types[2] = targetTypes[2]; gBattlescriptCurrInstr = cmd->nextInstr; } - else if (targetType1 == TYPE_MYSTERY && targetType2 != TYPE_MYSTERY) + else if (targetTypes[0] == TYPE_MYSTERY && targetTypes[1] != TYPE_MYSTERY) { - gBattleMons[gBattlerAttacker].types[0] = targetType2; - gBattleMons[gBattlerAttacker].types[1] = targetType2; - gBattleMons[gBattlerAttacker].types[2] = targetType3; + gBattleMons[gBattlerAttacker].types[0] = targetTypes[1]; + gBattleMons[gBattlerAttacker].types[1] = targetTypes[1]; + gBattleMons[gBattlerAttacker].types[2] = targetTypes[2]; gBattlescriptCurrInstr = cmd->nextInstr; } - else if (targetType1 != TYPE_MYSTERY && targetType2 == TYPE_MYSTERY) + else if (targetTypes[0] != TYPE_MYSTERY && targetTypes[1] == TYPE_MYSTERY) { - gBattleMons[gBattlerAttacker].types[0] = targetType1; - gBattleMons[gBattlerAttacker].types[1] = targetType1; - gBattleMons[gBattlerAttacker].types[2] = targetType3; + gBattleMons[gBattlerAttacker].types[0] = targetTypes[0]; + gBattleMons[gBattlerAttacker].types[1] = targetTypes[0]; + gBattleMons[gBattlerAttacker].types[2] = targetTypes[2]; gBattlescriptCurrInstr = cmd->nextInstr; } else { - gBattleMons[gBattlerAttacker].types[0] = targetType1; - gBattleMons[gBattlerAttacker].types[1] = targetType2; - gBattleMons[gBattlerAttacker].types[2] = targetType3; + gBattleMons[gBattlerAttacker].types[0] = targetTypes[0]; + gBattleMons[gBattlerAttacker].types[1] = targetTypes[1]; + gBattleMons[gBattlerAttacker].types[2] = targetTypes[2]; gBattlescriptCurrInstr = cmd->nextInstr; } } diff --git a/src/battle_util.c b/src/battle_util.c index 380564038d..f1d34f8ac8 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -2333,9 +2333,7 @@ u8 DoBattlerEndTurnEffects(void) && ability != ABILITY_SAND_FORCE && ability != ABILITY_SAND_RUSH && ability != ABILITY_OVERCOAT - && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_ROCK) - && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GROUND) - && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_STEEL) + && !IS_BATTLER_ANY_TYPE(gBattlerAttacker, TYPE_ROCK, TYPE_GROUND, TYPE_STEEL) && !(gStatuses3[gBattlerAttacker] & (STATUS3_UNDERGROUND | STATUS3_UNDERWATER)) && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_SAFETY_GOGGLES) { @@ -2906,7 +2904,7 @@ u8 DoBattlerEndTurnEffects(void) && !IsBattlerProtectedByMagicGuard(battler, ability)) { gBattlerTarget = battler; - if (IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_STEEL) || IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_WATER)) + if (IS_BATTLER_ANY_TYPE(gBattlerTarget, TYPE_STEEL, TYPE_WATER)) gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 4; else gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 8; @@ -10556,13 +10554,14 @@ static void UpdateMoveResultFlags(uq4_12_t modifier) static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, bool32 recordAbilities, uq4_12_t modifier, u32 defAbility) { u32 illusionSpecies; + u32 types[3]; + GetBattlerTypes(battlerDef, FALSE, types); - MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 0, FALSE), battlerAtk, recordAbilities); - if (GetBattlerType(battlerDef, 1, FALSE) != GetBattlerType(battlerDef, 0, FALSE)) - MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 1, FALSE), battlerAtk, recordAbilities); - if (GetBattlerType(battlerDef, 2, FALSE) != TYPE_MYSTERY && GetBattlerType(battlerDef, 2, FALSE) != GetBattlerType(battlerDef, 1, FALSE) - && GetBattlerType(battlerDef, 2, FALSE) != GetBattlerType(battlerDef, 0, FALSE)) - MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 2, FALSE), battlerAtk, recordAbilities); + MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, types[0], battlerAtk, recordAbilities); + if (types[1] != types[0]) + MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, types[1], battlerAtk, recordAbilities); + if (types[2] != TYPE_MYSTERY && types[2] != types[1] && types[2] != types[0]) + MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, types[2], battlerAtk, recordAbilities); if (moveType == TYPE_FIRE && gDisableStructs[battlerDef].tarShot) modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); @@ -10593,8 +10592,9 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(u32 move, u32 mov } // Thousand Arrows ignores type modifiers for flying mons - if (!IsBattlerGrounded(battlerDef) && (gMovesInfo[move].ignoreTypeIfFlyingAndUngrounded) - && (GetBattlerType(battlerDef, 0, FALSE) == TYPE_FLYING || GetBattlerType(battlerDef, 1, FALSE) == TYPE_FLYING || GetBattlerType(battlerDef, 2, FALSE) == TYPE_FLYING)) + if (!IsBattlerGrounded(battlerDef) + && (gMovesInfo[move].ignoreTypeIfFlyingAndUngrounded) + && IS_BATTLER_OF_TYPE(battlerDef, TYPE_FLYING)) { modifier = UQ_4_12(1.0); } @@ -11109,8 +11109,9 @@ bool32 TryBattleFormChange(u32 battler, u32 method) bool32 DoBattlersShareType(u32 battler1, u32 battler2) { s32 i; - u8 types1[3] = {GetBattlerType(battler1, 0, FALSE), GetBattlerType(battler1, 1, FALSE), GetBattlerType(battler1, 2, FALSE)}; - u8 types2[3] = {GetBattlerType(battler2, 0, FALSE), GetBattlerType(battler2, 1, FALSE), GetBattlerType(battler2, 2, FALSE)}; + u32 types1[3], types2[3]; + GetBattlerTypes(battler1, FALSE, types1); + GetBattlerTypes(battler2, FALSE, types2); if (types1[2] == TYPE_MYSTERY) types1[2] = types1[0]; @@ -11881,31 +11882,40 @@ bool8 IsMonBannedFromSkyBattles(u16 species) } } -u8 GetBattlerType(u32 battler, u8 typeIndex, bool32 ignoreTera) +void GetBattlerTypes(u32 battler, bool32 ignoreTera, u32 types[static 3]) { - u32 teraType = GetBattlerTeraType(battler); - u16 types[3] = {0}; + // Terastallization. + bool32 isTera = GetActiveGimmick(battler) == GIMMICK_TERA; + if (!ignoreTera && isTera) + { + u32 teraType = GetBattlerTeraType(battler); + if (teraType != TYPE_STELLAR) + { + types[0] = types[1] = types[2] = teraType; + return; + } + } + types[0] = gBattleMons[battler].types[0]; types[1] = gBattleMons[battler].types[1]; types[2] = gBattleMons[battler].types[2]; - // Handle Terastallization - if (GetActiveGimmick(battler) == GIMMICK_TERA && teraType != TYPE_STELLAR && !ignoreTera) - return GetBattlerTeraType(battler); - - // Handle Roost's Flying-type suppression - if (typeIndex == 0 || typeIndex == 1) + // Roost. + if (!isTera && (gBattleResources->flags->flags[battler] & RESOURCE_FLAG_ROOST)) { - if (gBattleResources->flags->flags[battler] & RESOURCE_FLAG_ROOST - && GetActiveGimmick(battler) != GIMMICK_TERA) - { - if (types[0] == TYPE_FLYING && types[1] == TYPE_FLYING) - return B_ROOST_PURE_FLYING >= GEN_5 ? TYPE_NORMAL : TYPE_MYSTERY; - else - return types[typeIndex] == TYPE_FLYING ? TYPE_MYSTERY : types[typeIndex]; - } + if (types[0] == TYPE_FLYING && types[1] == TYPE_FLYING) + types[0] = types[1] = B_ROOST_PURE_FLYING >= GEN_5 ? TYPE_NORMAL : TYPE_MYSTERY; + else if (types[0] == TYPE_FLYING) + types[0] = TYPE_MYSTERY; + else if (types[1] == TYPE_FLYING) + types[1] = TYPE_MYSTERY; } +} +u32 GetBattlerType(u32 battler, u32 typeIndex, bool32 ignoreTera) +{ + u32 types[3]; + GetBattlerTypes(battler, ignoreTera, types); return types[typeIndex]; }