From 9a4489b47f2af46e38519515e3f3c3f9334b9929 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 23 Jun 2021 05:05:51 +0400 Subject: [PATCH] * Cast an instant or sorcery spell this turn - fixed rollback error when you cast graveyard spell as first in turn (#7918); --- .../test/cards/single/mh1/LavaDartTest.java | 30 ++++++++ .../single/stx/ShowOfConfidenceTest.java | 73 +++++++++++++++++++ .../watchers/common/SpellsCastWatcher.java | 14 +--- 3 files changed, 105 insertions(+), 12 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/mh1/LavaDartTest.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/stx/ShowOfConfidenceTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mh1/LavaDartTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh1/LavaDartTest.java new file mode 100644 index 0000000000..e11fb02c43 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh1/LavaDartTest.java @@ -0,0 +1,30 @@ +package org.mage.test.cards.single.mh1; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class LavaDartTest extends CardTestPlayerBase { + + @Test + public void test_Play() { + // Lava Dart deals 1 damage to any target. + // Flashback-Sacrifice a Mountain. + addCard(Zone.GRAVEYARD, playerA, "Lava Dart"); // {R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback", playerB); + setChoice(playerA, "Mountain"); // sacrifice cost + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 20 - 1); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/stx/ShowOfConfidenceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/stx/ShowOfConfidenceTest.java new file mode 100644 index 0000000000..2f4b77d495 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/stx/ShowOfConfidenceTest.java @@ -0,0 +1,73 @@ +package org.mage.test.cards.single.stx; + +import mage.abilities.keyword.VigilanceAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.watchers.common.SpellsCastWatcher; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class ShowOfConfidenceTest extends CardTestPlayerBase { + + @Test + public void test_SpellsCastWatcher() { + // When you cast this spell, copy it for each other instant or sorcery spell you've cast this turn. You may choose new targets for the copies. + // Put a +1/+1 counter on target creature. It gains vigilance until end of turn. + addCard(Zone.HAND, playerA, "Show of Confidence"); // {1}{W} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + // + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.GRAVEYARD, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // + // Each instant and sorcery card in your graveyard gains flashback until end of turn. The flashback cost is equal to its mana cost. + addCard(Zone.HAND, playerA, "Past in Flames", 1); // {3}{R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + + // prepare NPE error for watcher + runCode("prepare watcher's NPE", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> { + // possible bug: NPE after wrong computeIfAbsent usage (lists desync) + SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); + watcher.getSpellsCastThisTurn(player.getId()); + }); + + // prepare flashback + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 4); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Past in Flames"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // prepare spells count (1x from hand, 1x from grave) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback {R}", playerB); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // cast and copy 3x + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Show of Confidence", "Balduvian Bears"); + setChoice(playerA, "No"); // no change target (copy 1) + setChoice(playerA, "No"); // no change target (copy 2) + setChoice(playerA, "No"); // no change target (copy 3) + + // test watcher's copy + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + runCode("test watcher's copy", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> { + SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); + SpellsCastWatcher copiedWatcher = watcher.copy(); + Assert.assertEquals("original watcher must see 4 spells", 4, watcher.getSpellsCastThisTurn(player.getId()).size()); + Assert.assertEquals("copied watcher must see 4 spells", 4, copiedWatcher.getSpellsCastThisTurn(player.getId()).size()); + }); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, "Balduvian Bears", CounterType.P1P1, 4); + assertAbility(playerA, "Balduvian Bears", VigilanceAbility.getInstance(), true); + } +} diff --git a/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java b/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java index a074640a1f..c4235d5b9b 100644 --- a/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java @@ -36,18 +36,8 @@ public class SpellsCastWatcher extends Watcher { } } if (spell != null) { - List spells; - List graveyardSpells; - if (!spellsCast.containsKey(spell.getControllerId())) { - spells = new ArrayList<>(); - spellsCast.put(spell.getControllerId(), spells); - graveyardSpells = new ArrayList<>(); - spellsCastFromGraveyard.put(spell.getControllerId(), graveyardSpells); - - } else { - spells = spellsCast.get(spell.getControllerId()); - graveyardSpells = spellsCastFromGraveyard.get(spell.getControllerId()); - } + List spells = spellsCast.computeIfAbsent(spell.getControllerId(), x -> new ArrayList<>()); + List graveyardSpells = spellsCastFromGraveyard.computeIfAbsent(spell.getControllerId(), x -> new ArrayList<>()); spells.add(spell.copy()); // copy needed because attributes like color could be changed later if (event.getZone() == Zone.GRAVEYARD) { graveyardSpells.add(spell.copy());