From a49c988b89f8e25043f2fb951ad6d85804601193 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Sun, 16 Jun 2024 19:53:35 +0100 Subject: [PATCH 1/5] Adds Toxic Chain's effect --- src/battle_util.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/battle_util.c b/src/battle_util.c index 2e1cbd2d42..ea743e0459 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5799,6 +5799,22 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect++; } break; + case ABILITY_TOXIC_CHAIN: + if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && IsBattlerAlive(gBattlerTarget) + && !gProtectStructs[gBattlerAttacker].confusionSelfDmg + && CanBePoisoned(gBattlerAttacker, gBattlerTarget) + && TARGET_TURN_DAMAGED // Need to actually hit the target + && (Random() % 3) == 0) + { + gBattleScripting.moveEffect = MOVE_EFFECT_TOXIC; + PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gLastUsedAbility); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_AbilityStatusEffect; + gHitMarker |= HITMARKER_STATUS_ABILITY_EFFECT; + effect++; + } + break; case ABILITY_STENCH: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && IsBattlerAlive(gBattlerTarget) From 9b8c47ac5c3669acb3f568b6f4f18c1ecc2a8010 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Mon, 17 Jun 2024 16:51:08 +0100 Subject: [PATCH 2/5] Add some Toxic Chain tests --- test/battle/ability/toxic_chain.c | 76 +++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 test/battle/ability/toxic_chain.c diff --git a/test/battle/ability/toxic_chain.c b/test/battle/ability/toxic_chain.c new file mode 100644 index 0000000000..bcbdae082f --- /dev/null +++ b/test/battle/ability/toxic_chain.c @@ -0,0 +1,76 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Toxic Chain inflicts bad poison when attacking") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_TACKLE].category != DAMAGE_CATEGORY_STATUS); + PLAYER(SPECIES_OKIDOGI) { Ability(ABILITY_TOXIC_CHAIN); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + ABILITY_POPUP(player, ABILITY_TOXIC_CHAIN); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + MESSAGE("Foe Wobbuffet is badly poisoned!"); + STATUS_ICON(opponent, badPoison: TRUE); + } THEN { + EXPECT(opponent->status1 & STATUS1_TOXIC_POISON); + } +} + +SINGLE_BATTLE_TEST("Toxic Chain inflicts bad poison on any hit of a multi-hit move") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_DOUBLE_SLAP].category != DAMAGE_CATEGORY_STATUS); + ASSUME(gMovesInfo[MOVE_DOUBLE_SLAP].effect == EFFECT_MULTI_HIT); + ASSUME(gItemsInfo[ITEM_PECHA_BERRY].holdEffect == HOLD_EFFECT_CURE_PSN); + PLAYER(SPECIES_OKIDOGI) { Ability(ABILITY_TOXIC_CHAIN); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_PECHA_BERRY); } + } WHEN { + TURN { MOVE(player, MOVE_DOUBLE_SLAP); } + } SCENE { + ABILITY_POPUP(player, ABILITY_TOXIC_CHAIN); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + MESSAGE("Foe Wobbuffet is badly poisoned!"); + STATUS_ICON(opponent, badPoison: TRUE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + STATUS_ICON(opponent, badPoison: FALSE); + ABILITY_POPUP(player, ABILITY_TOXIC_CHAIN); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + MESSAGE("Foe Wobbuffet is badly poisoned!"); + STATUS_ICON(opponent, badPoison: TRUE); + } THEN { + EXPECT(opponent->status1 & STATUS1_TOXIC_POISON); + } +} + +DOUBLE_BATTLE_TEST("Toxic Chain can inflict bad poison on both foes") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_BLIZZARD].category != DAMAGE_CATEGORY_STATUS); + ASSUME(gMovesInfo[MOVE_BLIZZARD].target == MOVE_TARGET_BOTH); + PLAYER(SPECIES_OKIDOGI) { Ability(ABILITY_TOXIC_CHAIN); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(playerLeft, MOVE_BLIZZARD); } + } SCENE { + HP_BAR(opponentLeft); + ABILITY_POPUP(playerLeft, ABILITY_TOXIC_CHAIN); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponentLeft); + MESSAGE("Foe Wobbuffet is badly poisoned!"); + STATUS_ICON(opponentLeft, badPoison: TRUE); + HP_BAR(opponentRight); + ABILITY_POPUP(playerLeft, ABILITY_TOXIC_CHAIN); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponentRight); + MESSAGE("Foe Wynaut is badly poisoned!"); + STATUS_ICON(opponentRight, badPoison: TRUE); + } THEN { + EXPECT(opponentLeft->status1 & STATUS1_TOXIC_POISON); + EXPECT(opponentRight->status1 & STATUS1_TOXIC_POISON); + } +} + +TO_DO_BATTLE_TEST("Toxic Chain makes Lum/Pecha Berry trigger before being knocked off"); From 161f61284a189626230a06d2517ae9ec9008e67e Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Mon, 17 Jun 2024 17:48:05 +0100 Subject: [PATCH 3/5] Use RandomWeighted; fix Test --- include/random.h | 1 + src/battle_util.c | 2 +- test/battle/ability/toxic_chain.c | 10 +++++++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/include/random.h b/include/random.h index c399ae3cf2..95f48e0540 100644 --- a/include/random.h +++ b/include/random.h @@ -183,6 +183,7 @@ enum RandomTag RNG_SPEED_TIE, RNG_STATIC, RNG_STENCH, + RNG_TOXIC_CHAIN, RNG_TRI_ATTACK, RNG_QUICK_DRAW, RNG_QUICK_CLAW, diff --git a/src/battle_util.c b/src/battle_util.c index ea743e0459..54cc6a239b 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5805,7 +5805,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && CanBePoisoned(gBattlerAttacker, gBattlerTarget) && TARGET_TURN_DAMAGED // Need to actually hit the target - && (Random() % 3) == 0) + && RandomWeighted(RNG_TOXIC_CHAIN, 7, 3)) { gBattleScripting.moveEffect = MOVE_EFFECT_TOXIC; PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gLastUsedAbility); diff --git a/test/battle/ability/toxic_chain.c b/test/battle/ability/toxic_chain.c index bcbdae082f..17ee455992 100644 --- a/test/battle/ability/toxic_chain.c +++ b/test/battle/ability/toxic_chain.c @@ -3,8 +3,10 @@ SINGLE_BATTLE_TEST("Toxic Chain inflicts bad poison when attacking") { + PASSES_RANDOMLY(3, 10, RNG_TOXIC_CHAIN); GIVEN { ASSUME(gMovesInfo[MOVE_TACKLE].category != DAMAGE_CATEGORY_STATUS); + ASSUME(gMovesInfo[MOVE_TACKLE].power > 0); PLAYER(SPECIES_OKIDOGI) { Ability(ABILITY_TOXIC_CHAIN); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -24,6 +26,7 @@ SINGLE_BATTLE_TEST("Toxic Chain inflicts bad poison on any hit of a multi-hit mo GIVEN { ASSUME(gMovesInfo[MOVE_DOUBLE_SLAP].category != DAMAGE_CATEGORY_STATUS); ASSUME(gMovesInfo[MOVE_DOUBLE_SLAP].effect == EFFECT_MULTI_HIT); + ASSUME(gMovesInfo[MOVE_DOUBLE_SLAP].power > 0); ASSUME(gItemsInfo[ITEM_PECHA_BERRY].holdEffect == HOLD_EFFECT_CURE_PSN); PLAYER(SPECIES_OKIDOGI) { Ability(ABILITY_TOXIC_CHAIN); } OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_PECHA_BERRY); } @@ -48,14 +51,15 @@ SINGLE_BATTLE_TEST("Toxic Chain inflicts bad poison on any hit of a multi-hit mo DOUBLE_BATTLE_TEST("Toxic Chain can inflict bad poison on both foes") { GIVEN { - ASSUME(gMovesInfo[MOVE_BLIZZARD].category != DAMAGE_CATEGORY_STATUS); - ASSUME(gMovesInfo[MOVE_BLIZZARD].target == MOVE_TARGET_BOTH); + ASSUME(gMovesInfo[MOVE_RAZOR_LEAF].category != DAMAGE_CATEGORY_STATUS); + ASSUME(gMovesInfo[MOVE_RAZOR_LEAF].target == MOVE_TARGET_BOTH); + ASSUME(gMovesInfo[MOVE_RAZOR_LEAF].power > 0); PLAYER(SPECIES_OKIDOGI) { Ability(ABILITY_TOXIC_CHAIN); } PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WYNAUT); } WHEN { - TURN { MOVE(playerLeft, MOVE_BLIZZARD); } + TURN { MOVE(playerLeft, MOVE_RAZOR_LEAF, WITH_RNG(RNG_TOXIC_CHAIN, TRUE)); } } SCENE { HP_BAR(opponentLeft); ABILITY_POPUP(playerLeft, ABILITY_TOXIC_CHAIN); From a26050fbde3152ff3f03ca0b350db0fac9b9a03e Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Tue, 18 Jun 2024 11:46:23 +0100 Subject: [PATCH 4/5] Adds Toxic Chain interaction with Knock Off --- src/battle_util.c | 12 +++++++++++ test/battle/ability/toxic_chain.c | 34 ++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/battle_util.c b/src/battle_util.c index 54cc6a239b..e0146c265f 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -7976,6 +7976,18 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) case HOLD_EFFECT_MARANGA_BERRY: // consume and boost sp. defense if used special move effect = DamagedStatBoostBerryEffect(battler, STAT_SPDEF, DAMAGE_CATEGORY_SPECIAL); break; + case HOLD_EFFECT_CURE_STATUS: // only Toxic Chain's interaction with Knock Off + case HOLD_EFFECT_CURE_PSN: + if (gBattleMons[battler].status1 & STATUS1_PSN_ANY && !UnnerveOn(battler, gLastUsedItem) && gLastUsedAbility == ABILITY_TOXIC_CHAIN && gMovesInfo[gCurrentMove].effect == EFFECT_KNOCK_OFF) + { + gBattleScripting.battler = battler; + gBattleMons[battler].status1 &= ~(STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER); + BattleScriptExecute(BattleScript_BerryCurePsnEnd2); + BtlController_EmitSetMonData(battler, 0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1); + MarkBattlerForControllerExec(battler); + effect = ITEM_STATUS_CHANGE; + } + break; case HOLD_EFFECT_STICKY_BARB: if (TARGET_TURN_DAMAGED && (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) diff --git a/test/battle/ability/toxic_chain.c b/test/battle/ability/toxic_chain.c index 17ee455992..d6aba06f70 100644 --- a/test/battle/ability/toxic_chain.c +++ b/test/battle/ability/toxic_chain.c @@ -77,4 +77,36 @@ DOUBLE_BATTLE_TEST("Toxic Chain can inflict bad poison on both foes") } } -TO_DO_BATTLE_TEST("Toxic Chain makes Lum/Pecha Berry trigger before being knocked off"); +SINGLE_BATTLE_TEST("Toxic Chain makes Lum/Pecha Berry trigger before being knocked off") +{ + u16 item = 0; + + PARAMETRIZE { item = ITEM_PECHA_BERRY; } + PARAMETRIZE { item = ITEM_LUM_BERRY; } + + GIVEN { + ASSUME(gMovesInfo[MOVE_KNOCK_OFF].category != DAMAGE_CATEGORY_STATUS); + ASSUME(gMovesInfo[MOVE_KNOCK_OFF].effect == EFFECT_KNOCK_OFF); + ASSUME(gMovesInfo[MOVE_KNOCK_OFF].power > 0); + ASSUME(gItemsInfo[ITEM_PECHA_BERRY].holdEffect == HOLD_EFFECT_CURE_PSN); + ASSUME(gItemsInfo[ITEM_LUM_BERRY].holdEffect == HOLD_EFFECT_CURE_STATUS); + PLAYER(SPECIES_OKIDOGI) { Ability(ABILITY_TOXIC_CHAIN); } + OPPONENT(SPECIES_WOBBUFFET) { Item(item); } + } WHEN { + TURN { MOVE(player, MOVE_KNOCK_OFF, WITH_RNG(RNG_TOXIC_CHAIN, TRUE)); } + } SCENE { + ABILITY_POPUP(player, ABILITY_TOXIC_CHAIN); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + MESSAGE("Foe Wobbuffet is badly poisoned!"); + STATUS_ICON(opponent, badPoison: TRUE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + STATUS_ICON(opponent, badPoison: FALSE); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF); + MESSAGE("Okidogi knocked off Foe Wobbuffet's Pecha Berry!"); + MESSAGE("Okidogi knocked off Foe Wobbuffet's Lum Berry!"); + } + } THEN { + EXPECT(opponent->status1 == 0); + } +} From 1fd23ec929ba901fdfceaec306ed01d511420ebf Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Tue, 18 Jun 2024 11:50:30 +0100 Subject: [PATCH 5/5] Fix potential issue with abilities that would cause Toxic Chain to not activate --- src/battle_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_util.c b/src/battle_util.c index e0146c265f..4ba6b5a0f9 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -7978,7 +7978,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) break; case HOLD_EFFECT_CURE_STATUS: // only Toxic Chain's interaction with Knock Off case HOLD_EFFECT_CURE_PSN: - if (gBattleMons[battler].status1 & STATUS1_PSN_ANY && !UnnerveOn(battler, gLastUsedItem) && gLastUsedAbility == ABILITY_TOXIC_CHAIN && gMovesInfo[gCurrentMove].effect == EFFECT_KNOCK_OFF) + if (gBattleMons[battler].status1 & STATUS1_PSN_ANY && !UnnerveOn(battler, gLastUsedItem) && GetBattlerAbility(gBattlerAttacker) == ABILITY_TOXIC_CHAIN && gMovesInfo[gCurrentMove].effect == EFFECT_KNOCK_OFF) { gBattleScripting.battler = battler; gBattleMons[battler].status1 &= ~(STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER);