From 3dd0f95ddd5cfe24ed124d634f9a81fb78854440 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Fri, 21 Feb 2025 23:11:07 +0100 Subject: [PATCH] Fixes Tera Shell activating on moves that have no effect on target (#6271) --- include/battle_script_commands.h | 1 - src/battle_script_commands.c | 46 ++++++++++--------------------- src/battle_util.c | 47 ++++++++++++++++++++++---------- test/battle/ability/tera_shell.c | 15 ++++++++++ test/battle/trainer_control.h | 1 + 5 files changed, 62 insertions(+), 48 deletions(-) diff --git a/include/battle_script_commands.h b/include/battle_script_commands.h index 72f3773fdd..cc6c09012c 100644 --- a/include/battle_script_commands.h +++ b/include/battle_script_commands.h @@ -51,7 +51,6 @@ void StealTargetItem(u8 battlerStealer, u8 battlerItem); u8 GetCatchingBattler(void); u32 GetHighestStatId(u32 battlerId); bool32 ProteanTryChangeType(u32 battler, u32 ability, u32 move, u32 moveType); -bool32 ShouldTeraShellDistortTypeMatchups(u32 move, u32 battlerDef); bool32 IsMoveNotAllowedInSkyBattles(u32 move); bool32 DoSwitchInAbilities(u32 battlerId); u8 GetFirstFaintedPartyIndex(u8 battlerId); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index efbac6019e..266e62b1b8 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1148,19 +1148,6 @@ bool32 ProteanTryChangeType(u32 battler, u32 ability, u32 move, u32 moveType) return FALSE; } -bool32 ShouldTeraShellDistortTypeMatchups(u32 move, u32 battlerDef) -{ - if (!gSpecialStatuses[battlerDef].distortedTypeMatchups - && gBattleMons[battlerDef].species == SPECIES_TERAPAGOS_TERASTAL - && gBattleMons[battlerDef].hp == gBattleMons[battlerDef].maxHP - && !IsBattleMoveStatus(move) - && !(gBattleStruct->moveResultFlags[battlerDef] & MOVE_RESULT_NO_EFFECT) - && GetBattlerAbility(battlerDef) == ABILITY_TERA_SHELL) - return TRUE; - - return FALSE; -} - bool32 IsMoveNotAllowedInSkyBattles(u32 move) { return (gBattleStruct->isSkyBattle && IsMoveSkyBattleBanned(gCurrentMove)); @@ -2009,10 +1996,21 @@ static void Cmd_critcalc(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static inline void GetShellSideArmCategory(u32 battlerDef) +static inline void CalculateAndSetMoveDamage(struct DamageCalculationData *damageCalcData, u32 battlerDef) { if (GetMoveEffect(gCurrentMove) == EFFECT_SHELL_SIDE_ARM) gBattleStruct->swapDamageCategory = (gBattleStruct->shellSideArmCategory[gBattlerAttacker][battlerDef] != GetMoveCategory(gCurrentMove)); + + damageCalcData->battlerDef = battlerDef; + damageCalcData->isCrit = gSpecialStatuses[battlerDef].criticalHit; + gBattleStruct->moveDamage[battlerDef] = CalculateMoveDamage(damageCalcData, 0); + + // Slighly hacky but we need to check move result flags for distortion match-up as well but it can only be done after damage calcs + if (gSpecialStatuses[battlerDef].distortedTypeMatchups && gBattleStruct->moveResultFlags[battlerDef] & MOVE_RESULT_NO_EFFECT) + { + gSpecialStatuses[battlerDef].distortedTypeMatchups = FALSE; + gSpecialStatuses[battlerDef].teraShellAbilityDone = FALSE; + } } static void Cmd_damagecalc(void) @@ -2044,28 +2042,12 @@ static void Cmd_damagecalc(void) || gBattleStruct->moveResultFlags[battlerDef] & MOVE_RESULT_NO_EFFECT) continue; - if (ShouldTeraShellDistortTypeMatchups(gCurrentMove, battlerDef)) - { - gSpecialStatuses[battlerDef].distortedTypeMatchups = TRUE; - gSpecialStatuses[battlerDef].teraShellAbilityDone = TRUE; - } - GetShellSideArmCategory(battlerDef); - damageCalcData.battlerDef = battlerDef; - damageCalcData.isCrit = gSpecialStatuses[battlerDef].criticalHit; - gBattleStruct->moveDamage[battlerDef] = CalculateMoveDamage(&damageCalcData, 0); + CalculateAndSetMoveDamage(&damageCalcData, battlerDef); } } else { - if (ShouldTeraShellDistortTypeMatchups(gCurrentMove, gBattlerTarget)) - { - gSpecialStatuses[gBattlerTarget].distortedTypeMatchups = TRUE; - gSpecialStatuses[gBattlerTarget].teraShellAbilityDone = TRUE; - } - GetShellSideArmCategory(gBattlerTarget); - damageCalcData.battlerDef = gBattlerTarget; - damageCalcData.isCrit = gSpecialStatuses[gBattlerTarget].criticalHit; - gBattleStruct->moveDamage[gBattlerTarget] = CalculateMoveDamage(&damageCalcData, 0); + CalculateAndSetMoveDamage(&damageCalcData, gBattlerTarget); } gBattlescriptCurrInstr = cmd->nextInstr; diff --git a/src/battle_util.c b/src/battle_util.c index ad725db7e6..b49be3b52a 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -65,6 +65,7 @@ static u32 GetFlingPowerFromItemId(u32 itemId); static void SetRandomMultiHitCounter(); static u32 GetBattlerItemHoldEffectParam(u32 battler, u32 item); static bool32 CanBeInfinitelyConfused(u32 battler); + ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12(u32 percent); ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12_Floored(u32 percent); @@ -201,10 +202,10 @@ static const struct BattleWeatherInfo sBattleWeatherInfo[BATTLE_WEATHER_COUNT] = }, }; -static u8 CalcBeatUpPower(void) +static u32 CalcBeatUpPower(void) { - u8 basePower; - u16 species; + u32 basePower; + u32 species; struct Pokemon *party = GetBattlerParty(gBattlerAttacker); // Party slot is incremented by the battle script for Beat Up after this damage calculation @@ -214,6 +215,18 @@ static u8 CalcBeatUpPower(void) return basePower; } +static bool32 ShouldTeraShellDistortTypeMatchups(u32 move, u32 battlerDef, u32 abilityDef) +{ + if (!gSpecialStatuses[battlerDef].distortedTypeMatchups + && gBattleMons[battlerDef].species == SPECIES_TERAPAGOS_TERASTAL + && gBattleMons[battlerDef].hp == gBattleMons[battlerDef].maxHP + && !IsBattleMoveStatus(move) + && abilityDef == ABILITY_TERA_SHELL) + return TRUE; + + return FALSE; +} + bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move) { u32 ability = GetBattlerAbility(battlerAtk); @@ -10562,7 +10575,7 @@ s32 CalculateMoveDamageVars(struct DamageCalculationData *damageCalcData, u32 fi holdEffectAtk, holdEffectDef, abilityAtk, abilityDef); } -static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 moveType, u32 battlerDef, u32 defType, u32 battlerAtk, bool32 recordAbilities) +static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 moveType, u32 battlerDef, u32 defAbility, u32 defType, u32 battlerAtk, bool32 recordAbilities) { uq4_12_t mod = GetTypeModifier(moveType, defType); u32 abilityAtk = GetBattlerAbility(battlerAtk); @@ -10602,11 +10615,15 @@ static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 move mod = UQ_4_12(1.0); } - if (gSpecialStatuses[battlerDef].distortedTypeMatchups || (AI_DATA->aiCalcInProgress && ShouldTeraShellDistortTypeMatchups(move, battlerDef))) + if (gSpecialStatuses[battlerDef].distortedTypeMatchups || (mod > UQ_4_12(0.0) && ShouldTeraShellDistortTypeMatchups(move, battlerDef, defAbility))) { mod = UQ_4_12(0.5); if (recordAbilities) - RecordAbilityBattle(battlerDef, GetBattlerAbility(battlerDef)); + { + RecordAbilityBattle(battlerDef, defAbility); + gSpecialStatuses[battlerDef].distortedTypeMatchups = TRUE; + gSpecialStatuses[battlerDef].teraShellAbilityDone = TRUE; + } } *modifier = uq4_12_multiply(*modifier, mod); @@ -10616,9 +10633,9 @@ static inline void TryNoticeIllusionInTypeEffectiveness(u32 move, u32 moveType, { // Check if the type effectiveness would've been different if the pokemon really had the types as the disguise. uq4_12_t presumedModifier = UQ_4_12(1.0); - MulByTypeEffectiveness(&presumedModifier, move, moveType, battlerDef, gSpeciesInfo[illusionSpecies].types[0], battlerAtk, FALSE); + MulByTypeEffectiveness(&presumedModifier, move, moveType, battlerDef, ABILITY_ILLUSION, 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); + MulByTypeEffectiveness(&presumedModifier, move, moveType, battlerDef, ABILITY_ILLUSION, gSpeciesInfo[illusionSpecies].types[1], battlerAtk, FALSE); if (presumedModifier != resultingModifier) RecordAbilityBattle(battlerDef, ABILITY_ILLUSION); @@ -10654,11 +10671,11 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(u32 move, u32 mov u32 types[3]; GetBattlerTypes(battlerDef, FALSE, types); - MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, types[0], battlerAtk, recordAbilities); + MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, defAbility, types[0], battlerAtk, recordAbilities); if (types[1] != types[0]) - MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, types[1], battlerAtk, recordAbilities); + MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, defAbility, 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); + MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, defAbility, types[2], battlerAtk, recordAbilities); if (moveType == TYPE_FIRE && gDisableStructs[battlerDef].tarShot) modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); @@ -10740,9 +10757,9 @@ uq4_12_t CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u16 a if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY) { - MulByTypeEffectiveness(&modifier, move, moveType, 0, gSpeciesInfo[speciesDef].types[0], 0, FALSE); + MulByTypeEffectiveness(&modifier, move, moveType, 0, 0, gSpeciesInfo[speciesDef].types[0], 0, FALSE); if (gSpeciesInfo[speciesDef].types[1] != gSpeciesInfo[speciesDef].types[0]) - MulByTypeEffectiveness(&modifier, move, moveType, 0, gSpeciesInfo[speciesDef].types[1], 0, FALSE); + MulByTypeEffectiveness(&modifier, move, moveType, 0, 0, gSpeciesInfo[speciesDef].types[1], 0, FALSE); if (moveType == TYPE_GROUND && abilityDef == ABILITY_LEVITATE && !(gFieldStatuses & STATUS_FIELD_GRAVITY)) modifier = UQ_4_12(0.0); @@ -10778,9 +10795,9 @@ uq4_12_t GetOverworldTypeEffectiveness(struct Pokemon *mon, u8 moveType) if (moveType != TYPE_MYSTERY) { - MulByTypeEffectiveness(&modifier, MOVE_POUND, moveType, 0, type1, 0, FALSE); + MulByTypeEffectiveness(&modifier, MOVE_POUND, moveType, 0, 0, type1, 0, FALSE); if (type2 != type1) - MulByTypeEffectiveness(&modifier, MOVE_POUND, moveType, 0, type2, 0, FALSE); + MulByTypeEffectiveness(&modifier, MOVE_POUND, moveType, 0, 0, type2, 0, FALSE); if ((modifier <= UQ_4_12(1.0) && abilityDef == ABILITY_WONDER_GUARD) || (moveType == TYPE_FIRE && abilityDef == ABILITY_FLASH_FIRE) diff --git a/test/battle/ability/tera_shell.c b/test/battle/ability/tera_shell.c index ef2993a0e7..63dda3207a 100644 --- a/test/battle/ability/tera_shell.c +++ b/test/battle/ability/tera_shell.c @@ -97,3 +97,18 @@ DOUBLE_BATTLE_TEST("Tera Shell only makes the first hit against Terapagos from a NOT MESSAGE("It's not very effective…"); } } + +SINGLE_BATTLE_TEST("Tera Shell respects immunity") +{ + GIVEN { + PLAYER(SPECIES_TERAPAGOS_TERASTAL) { Ability(ABILITY_TERA_SHELL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SHADOW_BALL); } + } SCENE { + NONE_OF { + ABILITY_POPUP(player, ABILITY_TERA_SHELL); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SHADOW_BALL, opponent); + } + } +} diff --git a/test/battle/trainer_control.h b/test/battle/trainer_control.h index 703a436274..62a8045694 100644 --- a/test/battle/trainer_control.h +++ b/test/battle/trainer_control.h @@ -704,3 +704,4 @@ }, }, }, +