From c03279d835e03485de641b68bdfe6486ed47e073 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 3 Feb 2021 10:27:23 +0400 Subject: [PATCH] * Repeated Reverberation - fixed rollback error on creatures cast (#7474); --- .../mage/cards/r/RepeatedReverberation.java | 53 +++++++++++-------- .../src/mage/cards/r/RingsOfBrighthearth.java | 6 +-- .../single/m20/RepeatedReverberationTest.java | 51 +++++++++++++----- 3 files changed, 70 insertions(+), 40 deletions(-) diff --git a/Mage.Sets/src/mage/cards/r/RepeatedReverberation.java b/Mage.Sets/src/mage/cards/r/RepeatedReverberation.java index b6e30492ec..fe7f5b469e 100644 --- a/Mage.Sets/src/mage/cards/r/RepeatedReverberation.java +++ b/Mage.Sets/src/mage/cards/r/RepeatedReverberation.java @@ -60,8 +60,8 @@ class RepeatedReverberationTriggeredAbility extends DelayedTriggeredAbility { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.SPELL_CAST - || event.getType() == GameEvent.EventType.ACTIVATED_ABILITY; + return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY + || event.getType() == GameEvent.EventType.SPELL_CAST; } @Override @@ -69,28 +69,37 @@ class RepeatedReverberationTriggeredAbility extends DelayedTriggeredAbility { if (!event.getPlayerId().equals(this.getControllerId())) { return false; } - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.isInstantOrSorcery()) { - this.getEffects().clear(); - this.addEffect( - new CopyTargetSpellEffect(true) - .setTargetPointer(new FixedTarget(event.getTargetId(), game)) - ); - this.addEffect( - new CopyTargetSpellEffect(true) - .setTargetPointer(new FixedTarget(event.getTargetId(), game)) - ); - return true; + + // activated ability + if (event.getType() == GameEvent.EventType.ACTIVATED_ABILITY) { + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); + if (stackAbility != null && stackAbility.getStackAbility() instanceof LoyaltyAbility) { + this.getEffects().clear(); + this.addEffect( + new RepeatedReverberationEffect() + .setTargetPointer(new FixedTarget(event.getTargetId(), game)) + ); + return true; + } } - StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); - if (stackAbility != null && stackAbility.getStackAbility() instanceof LoyaltyAbility) { - this.getEffects().clear(); - this.addEffect( - new RepeatedReverberationEffect() - .setTargetPointer(new FixedTarget(event.getTargetId(), game)) - ); - return true; + + // spell + if (event.getType() == GameEvent.EventType.SPELL_CAST) { + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell != null && spell.isInstantOrSorcery()) { + this.getEffects().clear(); + this.addEffect( + new CopyTargetSpellEffect(true) + .setTargetPointer(new FixedTarget(event.getTargetId(), game)) + ); + this.addEffect( + new CopyTargetSpellEffect(true) + .setTargetPointer(new FixedTarget(event.getTargetId(), game)) + ); + return true; + } } + return false; } diff --git a/Mage.Sets/src/mage/cards/r/RingsOfBrighthearth.java b/Mage.Sets/src/mage/cards/r/RingsOfBrighthearth.java index 002107b3cd..bb1382cfa9 100644 --- a/Mage.Sets/src/mage/cards/r/RingsOfBrighthearth.java +++ b/Mage.Sets/src/mage/cards/r/RingsOfBrighthearth.java @@ -1,7 +1,5 @@ - package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.costs.mana.ManaCostsImpl; @@ -15,13 +13,13 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.game.stack.StackAbility; import mage.players.Player; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class RingsOfBrighthearth extends CardImpl { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m20/RepeatedReverberationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m20/RepeatedReverberationTest.java index 92dcac9589..a668b53457 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/m20/RepeatedReverberationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m20/RepeatedReverberationTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.single.m20; import mage.constants.PhaseStep; @@ -8,23 +7,22 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author Jgray1206 */ public class RepeatedReverberationTest extends CardTestPlayerBase { /* Repeated Reverberation {2}{R}{R} - * When you next cast an instant spell, cast a sorcery spell, or activate a loyalty ability this turn, copy that spell or ability twice. + * When you next cast an instant spell, cast a sorcery spell, or activate a loyalty ability this turn, copy that spell or ability twice. * You may choose new targets for the copies. */ String repeatedReverb = "Repeated Reverberation"; - + /** * https://github.com/magefree/mage/issues/5882 * Repeated Reverberation was not working with loyalty counter abilities. */ @Test - public void testRepeatedReverberationWorksWithLoyaltyAbilities() { + public void test_WorksWithLoyaltyAbilities() { /* Ajani Goldmane: {2}{W}{W} starts with 4 Loyality counters * +1: You gain 2 life. * -1: Put a +1/+1 counter on each creature you control. Those creatures gain vigilance until end of turn. @@ -32,7 +30,7 @@ public class RepeatedReverberationTest extends CardTestPlayerBase { */ String ajani = "Ajani Goldmane"; - addCard(Zone.HAND, playerA, ajani); + addCard(Zone.HAND, playerA, ajani); addCard(Zone.HAND, playerA, repeatedReverb); addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); @@ -41,26 +39,26 @@ public class RepeatedReverberationTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, repeatedReverb); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: You gain 2 life"); - setStopAt(1, PhaseStep.BEGIN_COMBAT); setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, ajani, 1); assertGraveyardCount(playerA, repeatedReverb, 1); assertCounterCount(ajani, CounterType.LOYALTY, 5); // 4 + 1 = 5 assertLife(playerA, 26); - assertAllCommandsUsed(); } @Test - public void testRepeatedReverberationWorksWithInstants() { + public void test_WorksWithInstants() { /* Soothing Balm - Instant {1}{W} * Target player gains 5 life * Just an arbitrary instant to test reverb with. */ String soothingBalm = "Soothing Balm"; - + addCard(Zone.HAND, playerA, soothingBalm); addCard(Zone.HAND, playerA, repeatedReverb); addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); @@ -75,20 +73,20 @@ public class RepeatedReverberationTest extends CardTestPlayerBase { setChoice(playerA, "Yes"); //Choose new targets? addTarget(playerA, playerA); - setStopAt(1, PhaseStep.BEGIN_COMBAT); setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertGraveyardCount(playerA, soothingBalm, 1); assertGraveyardCount(playerA, repeatedReverb, 1); assertLife(playerA, 30); assertLife(playerB, 25); - assertAllCommandsUsed(); } @Test - public void testRepeatedReverberationWorksWithSorceries() { + public void test_WorksWithSorceries() { /* Soul Feast - Sorcery {3}{B}{B} * Target player loses 4 life. You gain 4 life. * Just an arbitrary sorcery to test reverb with. @@ -106,15 +104,40 @@ public class RepeatedReverberationTest extends CardTestPlayerBase { setChoice(playerA, "No"); //Choose new targets? setChoice(playerA, "No"); //Choose new targets? - setStopAt(1, PhaseStep.BEGIN_COMBAT); setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertGraveyardCount(playerA, soulFeast, 1); assertGraveyardCount(playerA, repeatedReverb, 1); assertLife(playerA, 32); assertLife(playerB, 8); + } + + @Test + public void test_MustNotWorksWithCreatures() { + addCard(Zone.HAND, playerA, repeatedReverb); // {2}{R}{R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + // + addCard(Zone.HAND, playerA, "Balduvian Bears", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + + // prepare reverb + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 4); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, repeatedReverb); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + // cast creature + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Balduvian Bears", 1); + assertGraveyardCount(playerA, repeatedReverb, 1); } }