From 9f22eb06880118543cb956b6eac43aae312a2aed Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 31 Dec 2015 11:01:52 +0100 Subject: [PATCH] * Fixed that cards without cost (e.g. Ancestral Vision) could be cast from graveyard (e.g. by using Yawgmoth Will). Fixed that cards could be suspended from graveyard (e.g. with Yawgmoth Will). --- .../sets/darksteel/SwordOfLightAndShadow.java | 8 +- .../mage/sets/urzassaga/YawgmothsWill.java | 12 +- .../test/cards/continuous/WonderTest.java | 9 +- .../PlayCardsFromGraveyardTest.java | 115 ++++++++++++++++++ .../abilities/keyword/SuspendAbility.java | 4 + .../main/java/mage/players/PlayerImpl.java | 3 + 6 files changed, 134 insertions(+), 17 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/continuous/playereffects/PlayCardsFromGraveyardTest.java diff --git a/Mage.Sets/src/mage/sets/darksteel/SwordOfLightAndShadow.java b/Mage.Sets/src/mage/sets/darksteel/SwordOfLightAndShadow.java index 7efdd4e8fe6..8d2ac2f019a 100644 --- a/Mage.Sets/src/mage/sets/darksteel/SwordOfLightAndShadow.java +++ b/Mage.Sets/src/mage/sets/darksteel/SwordOfLightAndShadow.java @@ -101,7 +101,7 @@ public class SwordOfLightAndShadow extends CardImpl { if (ability instanceof SwordOfLightAndShadowAbility) { Player controller = game.getPlayer(ability.getControllerId()); if (controller != null) { - // Target may only be added if possible target exists. Else the gain life effect won't trigger, becuase there is no valid target for the + // Target may only be added if possible target exists. Else the gain life effect won't trigger, because there is no valid target for the // return to hand ability if (controller.getGraveyard().count(new FilterCreatureCard(), ability.getSourceId(), ability.getControllerId(), game) > 0) { ability.addTarget(new TargetCardInYourGraveyard(0, 1, new FilterCreatureCard("creature card from your graveyard"))); @@ -115,8 +115,8 @@ public class SwordOfLightAndShadow extends CardImpl { class SwordOfLightAndShadowAbility extends TriggeredAbilityImpl { public SwordOfLightAndShadowAbility() { - super(Zone.BATTLEFIELD, new SwordOfLightAndShadowReturnToHandTargetEffect(), false); - this.addEffect(new GainLifeEffect(3)); + super(Zone.BATTLEFIELD, new GainLifeEffect(3), false); + this.addEffect(new SwordOfLightAndShadowReturnToHandTargetEffect()); } @@ -177,7 +177,7 @@ class SwordOfLightAndShadowReturnToHandTargetEffect extends OneShotEffect { case GRAVEYARD: Card card = game.getCard(targetId); if (card != null) { - controller.moveCards(card, null, Zone.HAND, source, game); + controller.moveCards(card, Zone.HAND, source, game); } else { result = false; } diff --git a/Mage.Sets/src/mage/sets/urzassaga/YawgmothsWill.java b/Mage.Sets/src/mage/sets/urzassaga/YawgmothsWill.java index caa8114d837..74026a6ed7b 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/YawgmothsWill.java +++ b/Mage.Sets/src/mage/sets/urzassaga/YawgmothsWill.java @@ -99,13 +99,7 @@ class CanPlayCardsFromGraveyardEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - for (UUID playerId: controller.getInRange()) { - Player player = game.getPlayer(playerId); - if (player != null) - { - player.setPlayCardsFromGraveyard(true); - } - } + controller.setPlayCardsFromGraveyard(true); return true; } return false; @@ -148,12 +142,12 @@ class YawgmothsWillReplacementEffect extends ReplacementEffectImpl { } return true; } - + @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == EventType.ZONE_CHANGE; } - + @Override public boolean applies(GameEvent event, Ability source, Game game) { if (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/WonderTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/WonderTest.java index f6172358274..440074c3e2b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/WonderTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/WonderTest.java @@ -9,15 +9,16 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * Wonder - * As long as Wonder is in your graveyard and you control an Island, creatures you control have flying. + * Wonder As long as Wonder is in your graveyard and you control an Island, + * creatures you control have flying. * * @author magenoxx_at_gmail.com */ public class WonderTest extends CardTestPlayerBase { /** - * Tests creatures for Flying gained from Wonder ability when all conditions were met + * Tests creatures for Flying gained from Wonder ability when all conditions + * were met */ @Test public void testCardWithAllConditionsMet() { @@ -38,7 +39,7 @@ public class WonderTest extends CardTestPlayerBase { // check no flying in graveyard for (Card card : playerA.getGraveyard().getCards(currentGame)) { - if (card.equals("Runeclaw Bear")) { + if (card.getName().equals("Runeclaw Bear")) { Assert.assertFalse(card.getAbilities().contains(FlyingAbility.getInstance())); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/playereffects/PlayCardsFromGraveyardTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/playereffects/PlayCardsFromGraveyardTest.java new file mode 100644 index 00000000000..76fae031227 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/playereffects/PlayCardsFromGraveyardTest.java @@ -0,0 +1,115 @@ +/* + * 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.continuous.playereffects; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class PlayCardsFromGraveyardTest extends CardTestPlayerBase { + + @Test + public void testYawgmothsWill() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); + // Until end of turn, you may play cards from your graveyard. + // If a card would be put into your graveyard from anywhere this turn, exile that card instead. + addCard(Zone.HAND, playerA, "Yawgmoth's Will"); // {2}{B} + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); + // You gain 3 life. + // Draw a card. + addCard(Zone.GRAVEYARD, playerA, "Reviving Dose"); // {2}{W} + addCard(Zone.BATTLEFIELD, playerA, "Corpse Traders"); + + addCard(Zone.BATTLEFIELD, playerB, "Mountain"); + addCard(Zone.HAND, playerB, "Lightning Bolt"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Yawgmoth's Will"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reviving Dose"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + // 10/4/2004 It will exile itself since it goes to the graveyard after its effect starts. + assertExileCount("Yawgmoth's Will", 1); + + assertGraveyardCount(playerB, "Lightning Bolt", 1); + + assertExileCount("Reviving Dose", 1); + assertExileCount("Silvercoat Lion", 1); + + assertHandCount(playerA, 1); + + assertLife(playerA, 23); + assertLife(playerB, 20); + } + + /** + * The null casting cost cards(Ancestral Visions, Lotus Bloom) can be cast + * from graveyard with Yawgmoth's Will + */ + @Test + public void testYawgmothsWillNoCastingCost() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + // Until end of turn, you may play cards from your graveyard. + // If a card would be put into your graveyard from anywhere this turn, exile that card instead. + addCard(Zone.HAND, playerA, "Yawgmoth's Will"); // {2}{B} + // Suspend 4-{U} + // Target player draws three cards. + addCard(Zone.GRAVEYARD, playerA, "Ancestral Vision"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Yawgmoth's Will"); + // you may not suspend it from graveyard + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Suspend"); + // It may not be possible to cast it from graveyard + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ancestral Vision"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + // 10/4/2004 It will exile itself since it goes to the graveyard after its effect starts. + assertExileCount("Yawgmoth's Will", 1); + + assertHandCount(playerA, 0); + assertExileCount("Ancestral Vision", 0); + + assertGraveyardCount(playerA, "Ancestral Vision", 1); + + assertLife(playerA, 20); + assertLife(playerB, 20); + } + +} diff --git a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java index fbb97482b8f..7327a9c4ec8 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java @@ -234,6 +234,10 @@ public class SuspendAbility extends ActivatedAbilityImpl { @Override public boolean canActivate(UUID playerId, Game game) { + if (!game.getState().getZone(getSourceId()).equals(Zone.HAND)) { + // Supend can only be activated from hand + return false; + } MageObject object = game.getObject(sourceId); return (object.getCardType().contains(CardType.INSTANT) || object.hasAbility(FlashAbility.getInstance().getId(), game) diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index db5d33b7d2d..31bcdbb769e 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -1291,6 +1291,9 @@ public abstract class PlayerImpl implements Player, Serializable { if (Zone.GRAVEYARD.equals(zone) && canPlayCardsFromGraveyard()) { for (ActivatedAbility ability : object.getAbilities().getPlayableAbilities(Zone.HAND)) { if (canUse || ability.getAbilityType().equals(AbilityType.SPECIAL_ACTION)) { + if (ability.getManaCosts().isEmpty() && ability.getCosts().isEmpty() && ability instanceof SpellAbility) { + continue; // You can't play spells from graveyard that have no costs + } if (ability.canActivate(playerId, game)) { useable.put(ability.getId(), ability); }