diff --git a/Mage.Sets/src/mage/cards/t/ThousandYearStorm.java b/Mage.Sets/src/mage/cards/t/ThousandYearStorm.java index ef1216e1f91..8c76a771734 100644 --- a/Mage.Sets/src/mage/cards/t/ThousandYearStorm.java +++ b/Mage.Sets/src/mage/cards/t/ThousandYearStorm.java @@ -1,9 +1,5 @@ package mage.cards.t; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.OneShotEffect; @@ -18,8 +14,12 @@ import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.watchers.Watcher; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class ThousandYearStorm extends CardImpl { @@ -65,10 +65,11 @@ class ThousandYearStormEffect extends OneShotEffect { if (spell != null) { ThousandYearWatcher watcher = game.getState().getWatcher(ThousandYearWatcher.class); if (watcher != null) { + String stateSearchId = spell.getId().toString() + source.getSourceId().toString(); // recall only the spells cast before it int numberOfCopies = 0; - if (game.getState().getValue(spell.getId().toString() + source.getSourceId().toString()) != null) { - numberOfCopies = (int) game.getState().getValue(spell.getId().toString() + source.getSourceId().toString()); + if (game.getState().getValue(stateSearchId) != null) { + numberOfCopies = (int) game.getState().getValue(stateSearchId); } if (numberOfCopies > 0) { for (int i = 0; i < numberOfCopies; i++) { @@ -99,19 +100,17 @@ class ThousandYearWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.SPELL_CAST - && !game.getStack().contains(game.getSpell(sourceId))) { + if (event.getType() == GameEvent.EventType.SPELL_CAST && !sourceId.equals(event.getTargetId())) { Spell spell = game.getSpellOrLKIStack(event.getTargetId()); - if (spell != null - && spell.isInstantOrSorcery()) { + if (spell != null && spell.isInstantOrSorcery()) { UUID playerId = event.getPlayerId(); if (playerId != null) { + String stateSearchId = spell.getId().toString() + sourceId.toString(); + // calc current spell amountOfInstantSorcerySpellsCastOnCurrentTurn.putIfAbsent(playerId, 0); amountOfInstantSorcerySpellsCastOnCurrentTurn.compute(playerId, (k, a) -> a + 1); - // remember only the spells cast before it - game.getState().setValue(spell.getId().toString() + sourceId.toString(), amountOfInstantSorcerySpellsCastOnCurrentTurn.get(playerId) - 1); - + game.getState().setValue(stateSearchId, amountOfInstantSorcerySpellsCastOnCurrentTurn.get(playerId) - 1); } } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ThousandYearStormTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ThousandYearStormTest.java new file mode 100644 index 00000000000..1e0198075d6 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ThousandYearStormTest.java @@ -0,0 +1,331 @@ +package org.mage.test.cards.single; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class ThousandYearStormTest extends CardTestPlayerBase { + + /* + Thousand-Year Storm {4}{U}{R} + Enchantment + Whenever you cast an instant or sorcery spell, copy it for each other instant and sorcery spell you’ve cast before it this turn. You may choose new targets for the copies. + */ + + @Test + public void test_CalcBeforeStorm() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.HAND, playerA, "Lightning Bolt", 5); + // + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, "Thousand-Year Storm"); + // + addCard(Zone.BATTLEFIELD, playerB, "Augmenting Automaton"); + + // bolt before + castSpell(1, PhaseStep.UPKEEP, playerA, "Lightning Bolt", playerB); + checkLife("before", 1, PhaseStep.PRECOMBAT_MAIN, playerB, 20 - 3); + + // storm + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thousand-Year Storm"); + + // bolt after (1 + 1x copy) + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Lightning Bolt", playerB); + setChoice(playerA, "No"); // change target for copy 1 + checkLife("copy", 1, PhaseStep.END_COMBAT, playerB, 20 - 3 - 3 * 2); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_CalcStackBefore() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.HAND, playerA, "Lightning Bolt", 5); + // + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, "Thousand-Year Storm"); + // + addCard(Zone.BATTLEFIELD, playerB, "Augmenting Automaton"); + + // bolt stack before + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + // storm + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thousand-Year Storm"); + + // bolt after (1 + 1x copy) + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Lightning Bolt", playerB); + setChoice(playerA, "No"); // change target for copy 1 + checkLife("copy", 1, PhaseStep.END_COMBAT, playerB, 20 - 3 - 3 * 2); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_CalcStackAfter() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.HAND, playerA, "Lightning Bolt", 5); + // + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, "Thousand-Year Storm"); + // + addCard(Zone.BATTLEFIELD, playerB, "Augmenting Automaton"); + + // storm + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thousand-Year Storm"); + // bolt stack after + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + + // bolt after (1 + 1x copy) + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Lightning Bolt", playerB); + setChoice(playerA, "No"); // change target for copy 1 + checkLife("copy", 1, PhaseStep.END_COMBAT, playerB, 20 - 3 - 3 * 2); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_CalcAfterStormOnSameStep() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.HAND, playerA, "Lightning Bolt", 5); + // + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, "Thousand-Year Storm"); + // + addCard(Zone.BATTLEFIELD, playerB, "Augmenting Automaton"); + + // storm + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thousand-Year Storm"); + + // bolt after + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Lightning Bolt", playerB); + // bolt after (1 + 1x copy) + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Lightning Bolt", playerB); + setChoice(playerA, "No"); // change target for copy 1 + checkLife("copy", 1, PhaseStep.END_COMBAT, playerB, 20 - 3 - 3 * 2); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_CalcAfterStormOnDiffStep() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.HAND, playerA, "Lightning Bolt", 5); + // + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, "Thousand-Year Storm"); + // + addCard(Zone.BATTLEFIELD, playerB, "Augmenting Automaton"); + + // storm + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thousand-Year Storm"); + + // bolt after + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Lightning Bolt", playerB); + // bolt after (1 + 1x copy) + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + setChoice(playerA, "No"); // change target for copy 1 + checkLife("copy", 1, PhaseStep.END_TURN, playerB, 20 - 3 - 3 * 2); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_OwnCounts() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.HAND, playerA, "Lightning Bolt", 10); + // + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, "Thousand-Year Storm"); + // + addCard(Zone.BATTLEFIELD, playerB, "Augmenting Automaton"); + + // turn 1 + int startLife = 100; + setLife(playerA, 100); + setLife(playerB, 100); + + // before storm (must counts too) + castSpell(1, PhaseStep.UPKEEP, playerA, "Lightning Bolt", playerB); + checkLife("0x copy", 1, PhaseStep.PRECOMBAT_MAIN, playerB, startLife - 3); + + // storm + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thousand-Year Storm"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + // 1 + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + checkLife("1x copy", 1, PhaseStep.BEGIN_COMBAT, playerB, startLife - 3 - 3 * 2); + setChoice(playerA, "No"); // change target for copy 1 + // 2 + castSpell(1, PhaseStep.DECLARE_ATTACKERS, playerA, "Lightning Bolt", playerB); + setChoice(playerA, "No"); // change target for copy 1 + setChoice(playerA, "No"); // change target for copy 2 + checkLife("2x copy", 1, PhaseStep.END_COMBAT, playerB, startLife - 3 - 3 * 2 - 3 * 3); + // 3 + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + setChoice(playerA, "No"); // change target for copy 1 + setChoice(playerA, "No"); // change target for copy 2 + setChoice(playerA, "No"); // change target for copy 3 + checkLife("3x copy", 1, PhaseStep.END_TURN, playerB, startLife - 3 - 3 * 2 - 3 * 3 - 3 * 4); + + // turn 3 + startLife = startLife - 3 - 3 * 2 - 3 * 3 - 3 * 4; + logger.info("start life on turn 3: " + startLife); + // after storm 0x + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + checkLife("3. no copy", 3, PhaseStep.BEGIN_COMBAT, playerB, startLife - 3); + // after storm 1x + castSpell(3, PhaseStep.DECLARE_ATTACKERS, playerA, "Lightning Bolt", playerB); + setChoice(playerA, "No"); // change target for copy 1 + checkLife("3. 1x copy", 3, PhaseStep.END_COMBAT, playerB, startLife - 3 - 3 * 2); + // after storm 2x + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + setChoice(playerA, "No"); // change target for copy 1 + setChoice(playerA, "No"); // change target for copy 2 + checkLife("3. 2x copy", 3, PhaseStep.END_TURN, playerB, startLife - 3 - 3 * 2 - 3 * 3); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_OpponentDontCounts() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.HAND, playerA, "Lightning Bolt", 10); + // + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 5); + addCard(Zone.HAND, playerB, "Lightning Bolt", 10); + // + addCard(Zone.BATTLEFIELD, playerA, "Thousand-Year Storm"); + // + addCard(Zone.BATTLEFIELD, playerB, "Augmenting Automaton"); + + // turn 1 + + // 1a + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + checkLife("0x copy", 1, PhaseStep.BEGIN_COMBAT, playerB, 20 - 3); + // 1b + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", playerA); + checkLife("0x copy", 1, PhaseStep.BEGIN_COMBAT, playerA, 20 - 3); + + // 2a + castSpell(1, PhaseStep.DECLARE_ATTACKERS, playerA, "Lightning Bolt", playerB); + setChoice(playerA, "No"); // change target for copy 1 + checkLife("1x copy", 1, PhaseStep.END_COMBAT, playerB, 20 - 3 - 3 * 2); + // 2b + castSpell(1, PhaseStep.DECLARE_ATTACKERS, playerB, "Lightning Bolt", playerA); + checkLife("0x copy", 1, PhaseStep.END_COMBAT, playerA, 20 - 3 - 3); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + + @Test + public void test_WaitStackResolvedWithBolts() { + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 5); + addCard(Zone.HAND, playerB, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + + castSpell(2, PhaseStep.END_TURN, playerB, "Lightning Bolt", playerA); + castSpell(2, PhaseStep.END_TURN, playerB, "Lightning Bolt", playerA); + castSpell(2, PhaseStep.END_TURN, playerA, "Lightning Bolt", playerB); + waitStackResolved(2, PhaseStep.END_TURN); + checkLife("A must get damage", 2, PhaseStep.END_TURN, playerA, 20 - 3 - 3); + checkLife("B must get damage", 2, PhaseStep.END_TURN, playerB, 20 - 3); + + setStrictChooseMode(true); + setStopAt(4, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + /* + Lay Claim {5}{U}{U} + Enchant permanent + You control enchanted permanent. + Cycling {2} ({2}, Discard this card: Draw a card.) + */ + @Test + public void test_GetControlNotCounts() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.HAND, playerA, "Lightning Bolt", 2); + // + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 5); + addCard(Zone.HAND, playerB, "Lightning Bolt", 3); + // + addCard(Zone.BATTLEFIELD, playerA, "Thousand-Year Storm"); + // + addCard(Zone.BATTLEFIELD, playerB, "Island", 7); + addCard(Zone.HAND, playerB, "Lay Claim"); + + // turn 2 + + // pump card for A + // 1 + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + checkLife("a 0x copy", 2, PhaseStep.BEGIN_COMBAT, playerB, 20 - 3); + // 2 + castSpell(2, PhaseStep.DECLARE_ATTACKERS, playerA, "Lightning Bolt", playerB); + setChoice(playerA, "No"); // change target for copy 1 + checkLife("a 1x copy", 2, PhaseStep.END_COMBAT, playerB, 20 - 3 - 3 * 2); + + // change controller to B + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lay Claim", "Thousand-Year Storm"); + // cast bolt without pump + castSpell(2, PhaseStep.END_TURN, playerB, "Lightning Bolt", playerA); + checkLife("b 0x copy after control", 3, PhaseStep.UPKEEP, playerA, 20 - 3); + + // turn 4 + + // pump for B + // 1 + castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", playerA); + checkLife("b 0x copy", 4, PhaseStep.BEGIN_COMBAT, playerA, 20 - 3 - 3); + // 2 + castSpell(4, PhaseStep.DECLARE_ATTACKERS, playerB, "Lightning Bolt", playerA); + setChoice(playerB, "No"); // change target for copy 1 + checkLife("b 1x copy", 4, PhaseStep.END_COMBAT, playerA, 20 - 3 - 3 - 3 * 2); + + setStrictChooseMode(true); + setStopAt(4, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } +}