From d5c1d3322cc2712f2e7c21e45f43f931c100a364 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Tue, 15 Jul 2025 17:52:44 +0200 Subject: [PATCH] Fixes Status overwrites and Effect Spore wrong func args (#7340) --- include/battle.h | 3 ++- src/battle_script_commands.c | 6 +++-- src/battle_util.c | 30 ++++++++++++++-------- test/battle/ability/effect_spore.c | 19 ++++++++++++++ test/battle/move_effect/protect.c | 40 ++++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+), 14 deletions(-) diff --git a/include/battle.h b/include/battle.h index b537f779af..17da0add57 100644 --- a/include/battle.h +++ b/include/battle.h @@ -658,7 +658,8 @@ struct BattleStruct u8 anyMonHasTransformed:1; // Only used in battle_tv.c u8 multipleSwitchInState:2; u8 multipleSwitchInCursor:3; - u8 padding1:2; + u8 sleepClauseNotBlocked:1; + u8 padding1:1; u8 multipleSwitchInSortedBattlers[MAX_BATTLERS_COUNT]; void (*savedCallback)(void); u16 usedHeldItems[PARTY_SIZE][NUM_BATTLE_SIDES]; // For each party member and side. For harvest, recycle diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 26144cecd1..533c25171f 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5864,7 +5864,8 @@ static void Cmd_moveend(void) } break; case PROTECT_BANEFUL_BUNKER: - if (!IsProtectivePadsProtected(gBattlerAttacker, GetBattlerHoldEffect(gBattlerAttacker, TRUE))) + if (!IsProtectivePadsProtected(gBattlerAttacker, GetBattlerHoldEffect(gBattlerAttacker, TRUE)) + && CanBePoisoned(gBattlerTarget, gBattlerAttacker, gLastUsedAbility, GetBattlerAbility(gBattlerAttacker))) { gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; gBattleScripting.moveEffect = MOVE_EFFECT_POISON; @@ -5874,7 +5875,8 @@ static void Cmd_moveend(void) } break; case PROTECT_BURNING_BULWARK: - if (!IsProtectivePadsProtected(gBattlerAttacker, GetBattlerHoldEffect(gBattlerAttacker, TRUE))) + if (!IsProtectivePadsProtected(gBattlerAttacker, GetBattlerHoldEffect(gBattlerAttacker, TRUE)) + && CanBeBurned(gBattlerTarget, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))) { gEffectBattler = gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; diff --git a/src/battle_util.c b/src/battle_util.c index 82a139cb26..da9dae3113 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -4624,9 +4624,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 break; case ABILITY_EFFECT_SPORE: { - u32 ability = GetBattlerAbility(gBattlerAttacker); + u32 abilityAtk = GetBattlerAbility(gBattlerAttacker); if ((!IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GRASS) || B_POWDER_GRASS < GEN_6) - && ability != ABILITY_OVERCOAT + && abilityAtk != ABILITY_OVERCOAT && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_SAFETY_GOGGLES) { u32 poison, paralysis, sleep; @@ -4653,7 +4653,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && IsBattlerAlive(gBattlerAttacker) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && IsBattlerTurnDamaged(gBattlerTarget) - && CanBeSlept(gBattlerAttacker, gBattlerTarget, ability, NOT_BLOCKED_BY_SLEEP_CLAUSE) + && CanBeSlept(gBattlerTarget, gBattlerAttacker, abilityAtk, NOT_BLOCKED_BY_SLEEP_CLAUSE) && !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker, TRUE), move)) { if (IsSleepClauseEnabled()) @@ -4672,11 +4672,12 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (B_ABILITY_TRIGGER_CHANCE >= GEN_4 ? RandomPercentage(RNG_POISON_POINT, 30) : RandomChance(RNG_POISON_POINT, 1, 3)) { POISON_POINT: + u32 abilityAtk = GetBattlerAbility(gBattlerAttacker); if (IsBattlerAlive(gBattlerAttacker) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && IsBattlerTurnDamaged(gBattlerTarget) - && CanBePoisoned(gBattlerTarget, gBattlerAttacker, gLastUsedAbility, GetBattlerAbility(gBattlerAttacker)) - && !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker, TRUE), move)) + && CanBePoisoned(gBattlerTarget, gBattlerAttacker, gLastUsedAbility, abilityAtk) + && !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, abilityAtk, GetBattlerHoldEffect(gBattlerAttacker, TRUE), move)) { gEffectBattler = gBattlerAttacker; gBattleScripting.battler = gBattlerTarget; @@ -4691,11 +4692,12 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (B_ABILITY_TRIGGER_CHANCE >= GEN_4 ? RandomPercentage(RNG_STATIC, 30) : RandomChance(RNG_STATIC, 1, 3)) { STATIC: + u32 abilityAtk = GetBattlerAbility(gBattlerAttacker); if (IsBattlerAlive(gBattlerAttacker) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && IsBattlerTurnDamaged(gBattlerTarget) - && CanBeParalyzed(gBattlerTarget, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) - && !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker, TRUE), move)) + && CanBeParalyzed(gBattlerTarget, gBattlerAttacker, abilityAtk) + && !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, abilityAtk, GetBattlerHoldEffect(gBattlerAttacker, TRUE), move)) { gEffectBattler = gBattlerAttacker; gBattleScripting.battler = gBattlerTarget; @@ -5498,9 +5500,13 @@ bool32 IsBattlerTerrainAffected(u32 battler, u32 terrainFlag) bool32 CanBeSlept(u32 battlerAtk, u32 battlerDef, u32 abilityDef, enum SleepClauseBlock isBlockedBySleepClause) { - if (IsSleepClauseActiveForSide(GetBattlerSide(battlerDef)) && isBlockedBySleepClause) + if (IsSleepClauseActiveForSide(GetBattlerSide(battlerDef)) && isBlockedBySleepClause != NOT_BLOCKED_BY_SLEEP_CLAUSE) return FALSE; + if (isBlockedBySleepClause == NOT_BLOCKED_BY_SLEEP_CLAUSE) + gBattleStruct->sleepClauseNotBlocked = TRUE; + + bool32 effect = FALSE; if (CanSetNonVolatileStatus( battlerAtk, battlerDef, @@ -5508,8 +5514,10 @@ bool32 CanBeSlept(u32 battlerAtk, u32 battlerDef, u32 abilityDef, enum SleepClau abilityDef, MOVE_EFFECT_SLEEP, // also covers yawn CHECK_TRIGGER)) - return TRUE; - return FALSE; + effect = TRUE; + + gBattleStruct->sleepClauseNotBlocked = FALSE; + return effect; } bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef) @@ -5665,7 +5673,7 @@ bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u { battleScript = BattleScript_CantMakeAsleep; } - else if (CanSleepDueToSleepClause(battlerAtk, battlerDef, option)) + else if (!gBattleStruct->sleepClauseNotBlocked && CanSleepDueToSleepClause(battlerAtk, battlerDef, option)) { battleScript = BattleScript_SleepClauseBlocked; } diff --git a/test/battle/ability/effect_spore.c b/test/battle/ability/effect_spore.c index 57f5399b53..912c311ca7 100644 --- a/test/battle/ability/effect_spore.c +++ b/test/battle/ability/effect_spore.c @@ -88,3 +88,22 @@ SINGLE_BATTLE_TEST("Effect Spore causes sleep 11% of the time") STATUS_ICON(player, sleep: TRUE); } } + +SINGLE_BATTLE_TEST("Effect Spore will check if it can inflict status onto attacker, not itself") +{ + PASSES_RANDOMLY(11, 100, RNG_EFFECT_SPORE); + GIVEN { + ASSUME(B_ABILITY_TRIGGER_CHANCE >= GEN_5); + ASSUME(MoveMakesContact(MOVE_SCRATCH)); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_BRELOOM) { Status1(STATUS1_BURN); Ability(ABILITY_EFFECT_SPORE); } + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH); } + TURN {} + } SCENE { + ABILITY_POPUP(opponent, ABILITY_EFFECT_SPORE); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_SLP, player); + MESSAGE("The opposing Breloom's Effect Spore made Wobbuffet sleep!"); + STATUS_ICON(player, sleep: TRUE); + } +} diff --git a/test/battle/move_effect/protect.c b/test/battle/move_effect/protect.c index 75efb2faa6..111cb3ca42 100644 --- a/test/battle/move_effect/protect.c +++ b/test/battle/move_effect/protect.c @@ -194,6 +194,26 @@ SINGLE_BATTLE_TEST("Protect: Baneful Bunker poisons pokemon for moves making con } } +SINGLE_BATTLE_TEST("Protect: Baneful Bunker can't poison pokemon if they are already statused") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_NUZZLE); } + TURN { MOVE(opponent, MOVE_BANEFUL_BUNKER); MOVE(player, MOVE_SCRATCH); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_NUZZLE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BANEFUL_BUNKER, opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + HP_BAR(opponent); + STATUS_ICON(player, STATUS1_POISON); + } + } +} + SINGLE_BATTLE_TEST("Protect: Burning Bulwark burns pokemon for moves making contact") { u16 usedMove = MOVE_NONE; @@ -226,6 +246,26 @@ SINGLE_BATTLE_TEST("Protect: Burning Bulwark burns pokemon for moves making cont } } +SINGLE_BATTLE_TEST("Protect: Burning Bulwark can't burn pokemon if they are already statused") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_NUZZLE); } + TURN { MOVE(opponent, MOVE_BURNING_BULWARK); MOVE(player, MOVE_SCRATCH); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_NUZZLE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BURNING_BULWARK, opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + HP_BAR(opponent); + STATUS_ICON(player, STATUS1_BURN); + } + } +} + SINGLE_BATTLE_TEST("Protect: Recoil damage is not applied if target was protected") { u32 j, k;