Fixes Tera Shell activating on moves that have no effect on target (#6271)

This commit is contained in:
Alex 2025-02-21 23:11:07 +01:00 committed by GitHub
parent bf263efa4b
commit 3dd0f95ddd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 62 additions and 48 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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);
}
}
}

View File

@ -704,3 +704,4 @@
},
},
},