diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/MycosynthGolemTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/MycosynthGolemTest.java index 0de986e421..f7172974d0 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/MycosynthGolemTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/MycosynthGolemTest.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package org.mage.test.cards.abilities.other; import mage.constants.PhaseStep; @@ -41,17 +40,13 @@ import org.mage.test.serverside.base.CardTestPlayerBase; */ public class MycosynthGolemTest extends CardTestPlayerBase { - /** - * Mycosynth Golem - * Artifact Creature — Golem 4/5, 11 (11) - * Affinity for artifacts (This spell costs {1} less to cast for each - * artifact you control.) - * Artifact creature spells you cast have affinity for artifacts. (They cost - * {1} less to cast for each artifact you control.) + /** + * Mycosynth Golem Artifact Creature — Golem 4/5, 11 (11) Affinity for + * artifacts (This spell costs {1} less to cast for each artifact you + * control.) Artifact creature spells you cast have affinity for artifacts. + * (They cost {1} less to cast for each artifact you control.) * */ - - // @Ignore // at this time player.getPlayable() does not account for spells that gain abilities @Test public void testSpellsAffinity() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); @@ -60,13 +55,13 @@ public class MycosynthGolemTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Alpha Myr"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpha Myr"); - + setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); assertPermanentCount(playerA, "Alpha Myr", 1); assertHandCount(playerA, "Alpha Myr", 0); - + Permanent mountain = getPermanent("Mountain", playerA); Permanent forest = getPermanent("Forest", playerA); int tappedLands = 0; @@ -79,5 +74,5 @@ public class MycosynthGolemTest extends CardTestPlayerBase { Assert.assertEquals("only one land may be tapped because the cost reduction", 1, tappedLands); } - + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SoulfireGrandMasterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SoulfireGrandMasterTest.java index 795122d82e..bce6619569 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SoulfireGrandMasterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SoulfireGrandMasterTest.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package org.mage.test.cards.abilities.other; import mage.constants.PhaseStep; @@ -39,17 +38,13 @@ import org.mage.test.serverside.base.CardTestPlayerBase; */ public class SoulfireGrandMasterTest extends CardTestPlayerBase { - /** - * Soulfire Grand Master - * Creature - Human Monk 2/2, 1W (2) - * Lifelink - * Instant and sorcery spells you control have lifelink. - * {2}{U/R}{U/R}: The next time you cast an instant or sorcery spell from - * your hand this turn, put that card into your hand instead of into your - * graveyard as it resolves. + /** + * Soulfire Grand Master Creature - Human Monk 2/2, 1W (2) Lifelink Instant + * and sorcery spells you control have lifelink. {2}{U/R}{U/R}: The next + * time you cast an instant or sorcery spell from your hand this turn, put + * that card into your hand instead of into your graveyard as it resolves. * */ - @Test public void testSpellsGainLifelink() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); @@ -57,7 +52,7 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Lightning Bolt"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); - + setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); @@ -75,9 +70,9 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Lightning Bolt"); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{U/R}{U/R}:"); - + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); - + setStopAt(1, PhaseStep.END_TURN); execute(); @@ -87,12 +82,11 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase { assertLife(playerB, 17); } + /** - * Test with Searing Blood - * If the delayed triggered ability triggers, it has to give - * life from lifelink because the source is still Searing Blood + * Test with Searing Blood If the delayed triggered ability triggers, it has + * to give life from lifelink because the source is still Searing Blood */ - // @Ignore // Does not work because as the delayed triggered ability resolves, the source card is no longer on the stack and @Test public void testSearingBlood1() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); @@ -117,9 +111,8 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase { } /** - * Test with Searing Blood - * If the delayed triggered ability triggers, it has to give - * life from lifelink because the source is still Searing Blood + * Test with Searing Blood If the delayed triggered ability triggers, it has + * to give life from lifelink because the source is still Searing Blood */ @Test public void testSearinBlood2() { @@ -146,12 +139,12 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase { assertLife(playerB, 17); assertLife(playerA, 28); // +2 from damage to Silvercoat Lion + 3 from Lighning Bolt + 3 from damage to Player B from Searing Blood - } - + } + /** * Test copied instant spell gives also life - * - */ + * + */ @Test public void testCopySpell() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); @@ -172,20 +165,18 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Lightning Bolt", 1); assertLife(playerB, 14); - assertLife(playerA, 26); + assertLife(playerA, 26); + + } - } - - /** * Test damage of activated ability of a permanent does not gain lifelink - * + * */ - @Test public void testActivatedAbility() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); - + addCard(Zone.HAND, playerA, "Lightning Bolt"); addCard(Zone.BATTLEFIELD, playerA, "Soulfire Grand Master", 1); // {3}, {T}: Rod of Ruin deals 1 damage to target creature or player. @@ -200,21 +191,22 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Rod of Ruin", 1); assertLife(playerB, 19); - assertLife(playerA, 20); + assertLife(playerA, 20); + + } - } /** - * Test that if Soulfire Grand Master has left the battlefield - * spell has no longer lifelink + * Test that if Soulfire Grand Master has left the battlefield spell has no + * longer lifelink */ - + @Test public void testSoulfireLeft() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); - + addCard(Zone.HAND, playerA, "Lightning Bolt"); addCard(Zone.BATTLEFIELD, playerA, "Soulfire Grand Master", 1); - + addCard(Zone.HAND, playerB, "Lightning Bolt", 1); addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); @@ -229,28 +221,30 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Soulfire Grand Master", 1); assertLife(playerB, 17); - assertLife(playerA, 20); + assertLife(playerA, 20); + + } - } /** - * I activated the ability of Soulfire grand master, it resolved, then i cast Stoke the Flames - * on Whisperwood Elemental, my opponenet sacrificed the elemental, so stoke didnt resolve, - * but i still got the life from lifelink. + * I activated the ability of Soulfire grand master, it resolved, then i + * cast Stoke the Flames on Whisperwood Elemental, my opponenet sacrificed + * the elemental, so stoke didnt resolve, but i still got the life from + * lifelink. */ - + @Test public void testSoulfireStokeTheFlames() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8); - + addCard(Zone.HAND, playerA, "Stoke the Flames"); addCard(Zone.BATTLEFIELD, playerA, "Soulfire Grand Master", 1); - + addCard(Zone.BATTLEFIELD, playerB, "Whisperwood Elemental", 1); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{U/R}{U/R}:"); - + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Stoke the Flames", "Whisperwood Elemental"); - activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Sacrifice {this}", null ,"{this} deals 4 damage"); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Sacrifice {this}", null, "{this} deals 4 damage"); setStopAt(1, PhaseStep.END_TURN); execute(); @@ -259,29 +253,28 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase { assertGraveyardCount(playerB, "Whisperwood Elemental", 1); assertLife(playerB, 20); - assertLife(playerA, 20); + assertLife(playerA, 20); - } - - /** - * Check if second ability resolved, the next spell that is counterer - * won't go to hand back because it did not resolve - * + } + + /** + * Check if second ability resolved, the next spell that is counterer won't + * go to hand back because it did not resolve + * */ - @Test public void testSoulfireCounteredSpellDontGoesBack() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8); - + addCard(Zone.HAND, playerA, "Stoke the Flames"); addCard(Zone.BATTLEFIELD, playerA, "Soulfire Grand Master", 1); - + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); addCard(Zone.HAND, playerB, "Counterspell", 1); addCard(Zone.BATTLEFIELD, playerB, "Whisperwood Elemental", 1); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{U/R}{U/R}:"); - + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Stoke the Flames", "Whisperwood Elemental"); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Counterspell", "Stoke the Flames"); @@ -292,26 +285,28 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Stoke the Flames", 1); // no legal target left so the spell is countered and goes to graveyard assertLife(playerB, 20); - assertLife(playerA, 20); + assertLife(playerA, 20); - } - /** - * With a Soulfire Grand Master in play, Deflecting Palm doesn't gain the caster life. - * It should as it has lifelink, and it's Deflecting Palm (an instant) dealing damage. - * I was playing against a human in Standard Constructed. - * + } + + /** + * With a Soulfire Grand Master in play, Deflecting Palm doesn't gain the + * caster life. It should as it has lifelink, and it's Deflecting Palm (an + * instant) dealing damage. I was playing against a human in Standard + * Constructed. + * */ - + @Test public void testWithDeflectingPalm() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); // Instant -{R}{W} - // The next time a source of your choice would deal damage to you this turn, prevent that damage. + // The next time a source of your choice would deal damage to you this turn, prevent that damage. // If damage is prevented this way, Deflecting Palm deals that much damage to that source's controller. addCard(Zone.HAND, playerA, "Deflecting Palm"); addCard(Zone.BATTLEFIELD, playerA, "Soulfire Grand Master", 1); - + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); addCard(Zone.HAND, playerB, "Lightning Bolt", 1); @@ -323,10 +318,10 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase { execute(); assertGraveyardCount(playerB, "Lightning Bolt", 1); - assertGraveyardCount(playerA, "Deflecting Palm", 1); + assertGraveyardCount(playerA, "Deflecting Palm", 1); assertLife(playerB, 17); assertLife(playerA, 23); // damage is prevented + lifelink + 3 - } + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/GainedDiesTriggersTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/GainedDiesTriggersTest.java new file mode 100644 index 0000000000..a937b7ef34 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/GainedDiesTriggersTest.java @@ -0,0 +1,69 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.cards.triggers.dies; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class GainedDiesTriggersTest extends CardTestPlayerBase { + + /** + * Tests that gained dies triggers work as intended + */ + @Test + public void testInfernalScarring() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 3); + + // Enchant creature + // Enchanted creature gets +2/+0 and has "When this creature dies, draw a card." + addCard(Zone.HAND, playerA, "Infernal Scarring", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.HAND, playerB, "Lightning Bolt", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Infernal Scarring", "Silvercoat Lion"); + + // Destroy all creatures. + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertGraveyardCount(playerB, "Lightning Bolt", 1); + assertHandCount(playerA, 1); // draw a card for dying Lion + } + +} diff --git a/Mage/src/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java b/Mage/src/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java index 6a0509da83..3029a00fc1 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java @@ -1,16 +1,16 @@ /* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR @@ -20,21 +20,20 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.abilities.effects.common.continuous; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; import mage.constants.AttachmentType; import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.SubLayer; -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffectImpl; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; @@ -70,7 +69,7 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl { setText(); } - public GainAbilityAttachedEffect(Ability ability, AttachmentType attachmentType) { + public GainAbilityAttachedEffect(Ability ability, AttachmentType attachmentType) { super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); this.ability = ability; this.attachmentType = attachmentType; diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 1479a6a442..ae55b7bea4 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -1432,19 +1432,17 @@ public abstract class GameImpl implements Game, Serializable { */ @Override public boolean checkStateAndTriggered() { - boolean trigger = !getTurn().isEndTurnRequested(); boolean somethingHappened = false; //20091005 - 115.5 while (!isPaused() && !gameOver(null)) { if (!checkStateBasedActions()) { // nothing happened so check triggers - if (trigger) { - state.handleSimultaneousEvent(this); - } - if (isPaused() || gameOver(null) || !trigger || !checkTriggered()) { + state.handleSimultaneousEvent(this); + if (isPaused() || gameOver(null) || getTurn().isEndTurnRequested() || !checkTriggered()) { break; } } + state.handleSimultaneousEvent(this); applyEffects(); // needed e.g if boost effects end and cause creatures to die somethingHappened = true; } diff --git a/Mage/src/mage/game/GameState.java b/Mage/src/mage/game/GameState.java index e3a2a8a978..cdfdd6ccc9 100644 --- a/Mage/src/mage/game/GameState.java +++ b/Mage/src/mage/game/GameState.java @@ -77,12 +77,12 @@ import mage.watchers.Watchers; * * @author BetaSteward_at_googlemail.com * -* since at any time the game state may be copied and restored you cannot rely + * since at any time the game state may be copied and restored you cannot rely * on any object maintaining it's instance it then becomes necessary to only * refer to objects by their ids since these will always remain constant * throughout its lifetime * -*/ + */ public class GameState implements Serializable, Copyable { private static final transient ThreadLocalStringBuilder threadLocalBuilder = new ThreadLocalStringBuilder(1024); @@ -643,7 +643,7 @@ public class GameState implements Serializable, Copyable { } public void handleSimultaneousEvent(Game game) { - if (!simultaneousEvents.isEmpty()) { + if (!simultaneousEvents.isEmpty() && !getTurn().isEndTurnRequested()) { // it can happen, that the events add new simultaneous events, so copy the list before List eventsToHandle = new ArrayList<>(); eventsToHandle.addAll(simultaneousEvents);