From 9be613beb9e82892d6b438f1bd3949e069f7ea3b Mon Sep 17 00:00:00 2001 From: Nidhoegger Date: Mon, 6 Jul 2015 10:51:06 +0200 Subject: [PATCH 1/6] Fixed Issue #1054. Buttons in Feedback panel will only get focused, if the MageFrame has the focus thus preventing focus theft from other programs. --- .../src/main/java/mage/client/game/FeedbackPanel.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java b/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java index 778c6ae58c..0190ec139b 100644 --- a/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java @@ -201,9 +201,12 @@ public class FeedbackPanel extends javax.swing.JPanel { } // Issue 256: Chat+Feedback panel: request focus prevents players from chatting - private void requestFocusIfPossible() { + // Issue #1054: XMage steals window focus whenever the screen updates + private void requestFocusIfPossible() { boolean requestFocusAllowed = true; - if (connectedChatPanel != null && connectedChatPanel.getTxtMessageInputComponent() != null) { + if (MageFrame.getInstance().getFocusOwner() == null) { + requestFocusAllowed = false; + } else if (connectedChatPanel != null && connectedChatPanel.getTxtMessageInputComponent() != null) { if (connectedChatPanel.getTxtMessageInputComponent().hasFocus()) { requestFocusAllowed = false; } From c942592c3b72ce4ec6e4eceead03192e19762fdf Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 6 Jul 2015 13:36:23 +0200 Subject: [PATCH 2/6] * Dash - Fixed that the creature was returned to hand from dash also if it left battlefield before. --- .../sets/returntoravnica/AngelOfSerenity.java | 11 ++-- .../sets/returntoravnica/UltimatePrice.java | 2 - .../cards/abilities/keywords/DashTest.java | 62 +++++++++++++------ .../cards/abilities/keywords/RenownTest.java | 27 ++++++++ .../mage/abilities/keyword/DashAbility.java | 44 ++++++++++--- 5 files changed, 112 insertions(+), 34 deletions(-) diff --git a/Mage.Sets/src/mage/sets/returntoravnica/AngelOfSerenity.java b/Mage.Sets/src/mage/sets/returntoravnica/AngelOfSerenity.java index e59ab2b006..06879b41d5 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/AngelOfSerenity.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/AngelOfSerenity.java @@ -67,7 +67,6 @@ public class AngelOfSerenity extends CardImpl { this.power = new MageInt(5); this.toughness = new MageInt(6); - // Flying this.addAbility(FlyingAbility.getInstance()); @@ -75,7 +74,7 @@ public class AngelOfSerenity extends CardImpl { this.addAbility(new AngelOfSerenityTriggeredAbility()); // When Angel of Serenity leaves the battlefield, return the exiled cards to their owners' hands. - this.addAbility(new LeavesBattlefieldTriggeredAbility(new AngelOfSerenityLeaveEffect(), false )); + this.addAbility(new LeavesBattlefieldTriggeredAbility(new AngelOfSerenityLeaveEffect(), false)); } public AngelOfSerenity(final AngelOfSerenity card) { @@ -104,7 +103,7 @@ class AngelOfSerenityTriggeredAbility extends ZoneChangeTriggeredAbility { getTargets().clear(); FilterCreaturePermanent filter = new FilterCreaturePermanent("up to three other target creatures"); filter.add(new AnotherPredicate()); - TargetCreaturePermanent target1 = new TargetCreaturePermanent(0,3, filter, false); + TargetCreaturePermanent target1 = new TargetCreaturePermanent(0, 3, filter, false); game.getPlayer(getControllerId()).chooseTarget(Outcome.Exile, target1, this, game); if (target1.getTargets().size() > 0) { getTargets().add(target1); @@ -112,8 +111,8 @@ class AngelOfSerenityTriggeredAbility extends ZoneChangeTriggeredAbility { } int leftTargets = 3 - target1.getTargets().size(); if (leftTargets > 0) { - FilterCard filter2 = new FilterCreatureCard("up to " + leftTargets + " target creature card" + (leftTargets > 1?"s":"") +" from graveyards"); - TargetCardInGraveyard target2 = new TargetCardInGraveyard(0,leftTargets, filter2); + FilterCard filter2 = new FilterCreatureCard("up to " + leftTargets + " target creature card" + (leftTargets > 1 ? "s" : "") + " from graveyards"); + TargetCardInGraveyard target2 = new TargetCardInGraveyard(0, leftTargets, filter2); game.getPlayer(getControllerId()).chooseTarget(Outcome.Exile, target2, this, game); if (target2.getTargets().size() > 0) { getTargets().add(target2); @@ -160,7 +159,7 @@ class AngelOfSerenityEnterEffect extends OneShotEffect { } } - } else if (target instanceof TargetCardInGraveyard){ + } else if (target instanceof TargetCardInGraveyard) { for (UUID cardId : target.getTargets()) { Card card = game.getCard(cardId); if (card != null) { diff --git a/Mage.Sets/src/mage/sets/returntoravnica/UltimatePrice.java b/Mage.Sets/src/mage/sets/returntoravnica/UltimatePrice.java index 168ad1e8f7..2c2e3a4af0 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/UltimatePrice.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/UltimatePrice.java @@ -34,7 +34,6 @@ import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.MonocoloredPredicate; -import mage.target.Target; import mage.target.common.TargetCreaturePermanent; /** @@ -53,7 +52,6 @@ public class UltimatePrice extends CardImpl { super(ownerId, 82, "Ultimate Price", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{B}"); this.expansionSetCode = "RTR"; - // Destroy target monocolored creature. this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); this.getSpellAbility().addEffect(new DestroyTargetEffect()); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DashTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DashTest.java index 5305854a07..e5f3b6f016 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DashTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DashTest.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.keywords; import mage.constants.PhaseStep; @@ -40,26 +39,25 @@ import org.mage.test.serverside.base.CardTestPlayerBase; public class DashTest extends CardTestPlayerBase { /** - * 702.108. Dash - * 702.108a Dash represents three abilities: two static abilities that function while the card with dash is - * on the stack, one of which may create a delayed triggered ability, and a static ability that - * functions while the object with dash is on the battlefield. “Dash [cost]” means “You may cast - * this card by paying [cost] rather that its mana cost,” “If this spell’s dash cost was paid, return the - * permanent this spell becomes to its owner’s hand at the beginning of the next end step,” and “As - * long as this permanent’s dash cost was paid, it has haste.” Paying a card’s dash cost follows the - * rules for paying alternative costs in rules 601.2b and 601.2e–g. - * - */ - - /** - * Screamreach Brawler - * Creature — Orc Berserker 2/3, 2R (3) - * Dash {1}{R} (You may cast this spell for its dash cost. If you do, it - * gains haste, and it's returned from the battlefield to its owner's hand - * at the beginning of the next end step.) + * 702.108. Dash 702.108a Dash represents three abilities: two static + * abilities that function while the card with dash is on the stack, one of + * which may create a delayed triggered ability, and a static ability that + * functions while the object with dash is on the battlefield. “Dash [cost]” + * means “You may cast this card by paying [cost] rather that its mana + * cost,” “If this spell’s dash cost was paid, return the permanent this + * spell becomes to its owner’s hand at the beginning of the next end step,” + * and “As long as this permanent’s dash cost was paid, it has haste.” + * Paying a card’s dash cost follows the rules for paying alternative costs + * in rules 601.2b and 601.2e–g. + * + */ + /** + * Screamreach Brawler Creature — Orc Berserker 2/3, 2R (3) Dash {1}{R} (You + * may cast this spell for its dash cost. If you do, it gains haste, and + * it's returned from the battlefield to its owner's hand at the beginning + * of the next end step.) * */ - @Test public void testDash() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); @@ -96,4 +94,30 @@ public class DashTest extends CardTestPlayerBase { } + /** + * Also dash returns creatures to your hand at end of turn even if they died + * that turn. + */ + @Test + public void testDashedCreatureDiesInCombat() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.HAND, playerA, "Screamreach Brawler"); // 2/3 + + addCard(Zone.BATTLEFIELD, playerB, "Geist of the Moors", 1); // 3/1 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Screamreach Brawler"); + setChoice(playerA, "Yes"); + attack(1, playerA, "Screamreach Brawler"); + block(1, playerB, "Geist of the Moors", "Screamreach Brawler"); + + setStopAt(2, PhaseStep.UNTAP); + execute(); + + assertLife(playerB, 20); + assertPermanentCount(playerA, "Screamreach Brawler", 0); + assertHandCount(playerA, "Screamreach Brawler", 0); + assertGraveyardCount(playerA, "Screamreach Brawler", 1); + assertGraveyardCount(playerB, "Geist of the Moors", 1); + + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RenownTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RenownTest.java index 533c50b39b..16637cc14c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RenownTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RenownTest.java @@ -211,4 +211,31 @@ public class RenownTest extends CardTestPlayerBase { } + /** + * Ability doesn't trigger when renowned. ("Whenever an opponent casts a + * noncreature spell, if ~ is renowned, ~ deals 2 damage to that player.") + */ + @Test + public void testScabClanBerserker() { + // Renown 1 + // Whenever an opponent casts a noncreature spell, if Scab-Clan Berserker is renowned, Scab-Clan Berserker deals 2 damage to that player. + addCard(Zone.BATTLEFIELD, playerA, "Scab-Clan Berserker"); // 2/2 {1}{R}{R} + + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.HAND, playerB, "Lightning Bolt"); + + attack(3, playerA, "Scab-Clan Berserker"); // 1 damage + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", playerA); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + Permanent berserker = getPermanent("Scab-Clan Berserker", playerA); + Assert.assertEquals("has has renown", true, berserker.isRenown()); + assertPowerToughness(playerA, "Scab-Clan Berserker", 3, 3); + + assertLife(playerA, 17); // Lightning Bolt + assertLife(playerB, 16); // 2 from attack 2 from triggered ability + + } } diff --git a/Mage/src/mage/abilities/keyword/DashAbility.java b/Mage/src/mage/abilities/keyword/DashAbility.java index 4072e1a856..4a2d96170f 100644 --- a/Mage/src/mage/abilities/keyword/DashAbility.java +++ b/Mage/src/mage/abilities/keyword/DashAbility.java @@ -31,6 +31,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; import mage.abilities.common.EntersBattlefieldAbility; @@ -44,7 +45,7 @@ import mage.abilities.costs.Costs; import mage.abilities.costs.CostsImpl; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.cards.Card; @@ -76,12 +77,9 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts Ability ability = new EntersBattlefieldAbility( new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.Custom, false), DashedCondition.getInstance(), false, "", ""); - Effect effect = new ReturnToHandTargetEffect(); - effect.setText("return the dashed creature from the battlefield to its owner's hand"); - effect.setTargetPointer(new FixedTarget(card.getId())); - ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), false)); + ability.addEffect(new DashAddDelayedTriggeredAbilityEffect()); addSubAbility(ability); - + } public DashAbility(final DashAbility ability) { @@ -134,7 +132,7 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts this.resetDash(); for (AlternativeCost2 dashCost : alternativeSourceCosts) { if (dashCost.canPay(ability, sourceId, controllerId, game) - && player.chooseUse(Outcome.Benefit, new StringBuilder(KEYWORD).append(" the creature for ").append(dashCost.getText(true)).append(" ?").toString(), ability, game)) { + && player.chooseUse(Outcome.Benefit, KEYWORD + " the creature for " + dashCost.getText(true) + " ?", ability, game)) { activateDash(dashCost, game); ability.getManaCostsToPay().clear(); ability.getCosts().clear(); @@ -209,3 +207,35 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts return alterCosts; } } + +class DashAddDelayedTriggeredAbilityEffect extends OneShotEffect { + + public DashAddDelayedTriggeredAbilityEffect() { + super(Outcome.Benefit); + this.staticText = "return the dashed creature from the battlefield to its owner's hand"; + } + + public DashAddDelayedTriggeredAbilityEffect(final DashAddDelayedTriggeredAbilityEffect effect) { + super(effect); + } + + @Override + public DashAddDelayedTriggeredAbilityEffect copy() { + return new DashAddDelayedTriggeredAbilityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Effect effect = new ReturnToHandTargetEffect(); + effect.setText("return the dashed creature from the battlefield to its owner's hand"); + effect.setTargetPointer(new FixedTarget(source.getSourceId())); + // init target pointer now because the dashed creature will only be returned from current zone + effect.getTargetPointer().init(game, source); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game), game); + game.addDelayedTriggeredAbility(delayedAbility); + return false; + } +} From b45cdb9a556b59cc78b4969ab33bd53ed346c898 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 6 Jul 2015 13:51:55 +0200 Subject: [PATCH 3/6] * Mistmeadow Witch - Fixed the unexpected tap source cost. --- Mage.Sets/src/mage/sets/shadowmoor/MistmeadowWitch.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/shadowmoor/MistmeadowWitch.java b/Mage.Sets/src/mage/sets/shadowmoor/MistmeadowWitch.java index 0b85cca6af..f5acde0684 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/MistmeadowWitch.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/MistmeadowWitch.java @@ -36,7 +36,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; -import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnFromExileEffect; @@ -62,7 +61,6 @@ public class MistmeadowWitch extends CardImpl { // {2}{W}{U}: Exile target creature. Return that card to the battlefield under its owner's control at the beginning of the next end step. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MistmeadowWitchEffect(), new ManaCostsImpl("{2}{W}{U}")); - ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } From 1ee75e3e94b30b126791c6c02294200f71765b4b Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 6 Jul 2015 15:22:16 +0200 Subject: [PATCH 4/6] * Angel of Serenity - Fixed that the exiled cards did not return when the Angel left the battlefield (fixes #1083). --- .../sets/returntoravnica/AngelOfSerenity.java | 46 ++------- .../EnterLeaveBattlefieldExileTargetTest.java | 99 +++++++++++++++++++ ...ereTest.java => JourneyToNowhereTest.java} | 2 +- .../ReturnFromExileForSourceEffect.java | 98 +++++++++--------- Mage/src/mage/players/PlayerImpl.java | 6 ++ Utils/release/getting_implemented_cards.txt | 3 + 6 files changed, 168 insertions(+), 86 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/triggers/EnterLeaveBattlefieldExileTargetTest.java rename Mage.Tests/src/test/java/org/mage/test/cards/triggers/{JournexToNowhereTest.java => JourneyToNowhereTest.java} (96%) diff --git a/Mage.Sets/src/mage/sets/returntoravnica/AngelOfSerenity.java b/Mage.Sets/src/mage/sets/returntoravnica/AngelOfSerenity.java index 06879b41d5..d71d7c49ce 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/AngelOfSerenity.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/AngelOfSerenity.java @@ -29,10 +29,12 @@ package mage.sets.returntoravnica; import java.util.UUID; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.common.ZoneChangeTriggeredAbility; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnFromExileForSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.Card; import mage.cards.CardImpl; @@ -44,10 +46,10 @@ import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.AnotherPredicate; -import mage.game.ExileZone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInGraveyard; import mage.target.common.TargetCreaturePermanent; @@ -74,7 +76,7 @@ public class AngelOfSerenity extends CardImpl { this.addAbility(new AngelOfSerenityTriggeredAbility()); // When Angel of Serenity leaves the battlefield, return the exiled cards to their owners' hands. - this.addAbility(new LeavesBattlefieldTriggeredAbility(new AngelOfSerenityLeaveEffect(), false)); + this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.HAND, false, true), false)); } public AngelOfSerenity(final AngelOfSerenity card) { @@ -149,13 +151,16 @@ class AngelOfSerenityEnterEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { boolean result = true; - if (source.getTargets().size() > 0) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null && sourceObject != null && source.getTargets().size() > 0) { + UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); for (Target target : source.getTargets()) { if (target instanceof TargetCreaturePermanent) { for (UUID permanentId : target.getTargets()) { Permanent permanent = game.getPermanent(permanentId); if (permanent != null) { - result |= permanent.moveToExile(CardUtil.getCardExileZoneId(game, source), "Angel of Serenity", source.getSourceId(), game); + result |= controller.moveCardToExileWithInfo(permanent, exileZoneId, sourceObject.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true); } } @@ -163,7 +168,7 @@ class AngelOfSerenityEnterEffect extends OneShotEffect { for (UUID cardId : target.getTargets()) { Card card = game.getCard(cardId); if (card != null) { - result |= card.moveToExile(CardUtil.getCardExileZoneId(game, source), "Angel of Serenity", source.getSourceId(), game); + result |= controller.moveCardToExileWithInfo(card, exileZoneId, sourceObject.getIdName(), source.getSourceId(), game, Zone.GRAVEYARD, true); } } } @@ -172,34 +177,3 @@ class AngelOfSerenityEnterEffect extends OneShotEffect { return result; } } - -class AngelOfSerenityLeaveEffect extends OneShotEffect { - - public AngelOfSerenityLeaveEffect() { - super(Outcome.ReturnToHand); - this.staticText = "return the exiled cards to their owners' hands"; - } - - public AngelOfSerenityLeaveEffect(final AngelOfSerenityLeaveEffect effect) { - super(effect); - } - - @Override - public AngelOfSerenityLeaveEffect copy() { - return new AngelOfSerenityLeaveEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - ExileZone exZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); - if (exZone != null) { - for (Card card : exZone.getCards(game)) { - if (card != null) { - card.moveToZone(Zone.HAND, source.getSourceId(), game, false); - } - } - return true; - } - return false; - } -} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/EnterLeaveBattlefieldExileTargetTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/EnterLeaveBattlefieldExileTargetTest.java new file mode 100644 index 0000000000..a808896969 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/EnterLeaveBattlefieldExileTargetTest.java @@ -0,0 +1,99 @@ +/* + * 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; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class EnterLeaveBattlefieldExileTargetTest extends CardTestPlayerBase { + + @Test + public void testAngelOfSerenityExile() { + // Flying + // When Angel of Serenity enters the battlefield, you may exile up to three other target creatures from the battlefield and/or creature cards from graveyards. + // When Angel of Serenity leaves the battlefield, return the exiled cards to their owners' hands. + addCard(Zone.HAND, playerA, "Angel of Serenity"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel of Serenity"); + addTarget(playerA, "Silvercoat Lion^Pillarfield Ox"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Angel of Serenity", 1); + assertExileCount("Silvercoat Lion", 1); + assertExileCount("Pillarfield Ox", 1); + + } + + /** + * When Angel of Serenity entered the battlefield on my opponent's main + * phase it exiled 3 of my creatures (as it should), I cast Ultimate Price + * (destroy monocolored creature) on my next main phase and destroyed the + * Angel of Serenity. The log said the exiled cards were returned to my hand + * but they remained in exile indefinitely. + */ + @Test + public void testAngelOfSerenityExileReturn() { + // Flying + // When Angel of Serenity enters the battlefield, you may exile up to three other target creatures from the battlefield and/or creature cards from graveyards. + // When Angel of Serenity leaves the battlefield, return the exiled cards to their owners' hands. + addCard(Zone.HAND, playerA, "Angel of Serenity"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); + + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 1); + addCard(Zone.HAND, playerB, "Ultimate Price", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel of Serenity"); + addTarget(playerA, "Silvercoat Lion^Pillarfield Ox"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Ultimate Price", "Angel of Serenity"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, "Ultimate Price", 1); + assertGraveyardCount(playerA, "Angel of Serenity", 1); + assertHandCount(playerB, "Silvercoat Lion", 1); + assertHandCount(playerB, "Pillarfield Ox", 1); + assertExileCount(playerB, 0); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/JournexToNowhereTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/JourneyToNowhereTest.java similarity index 96% rename from Mage.Tests/src/test/java/org/mage/test/cards/triggers/JournexToNowhereTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/triggers/JourneyToNowhereTest.java index 9199ba892e..8cef82f899 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/JournexToNowhereTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/JourneyToNowhereTest.java @@ -37,7 +37,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * @author LeveX2 */ -public class JournexToNowhereTest extends CardTestPlayerBase { +public class JourneyToNowhereTest extends CardTestPlayerBase { /* Journey to Nowhere Enchantment {1}{W} diff --git a/Mage/src/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java b/Mage/src/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java index f1e2347565..181bb35f94 100644 --- a/Mage/src/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java @@ -1,35 +1,32 @@ /* -* 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. -*/ - + * 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 mage.abilities.effects.common; -import java.util.LinkedList; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; @@ -41,6 +38,7 @@ import static mage.constants.Zone.GRAVEYARD; import static mage.constants.Zone.HAND; import mage.game.ExileZone; import mage.game.Game; +import mage.game.permanent.PermanentToken; import mage.players.Player; import mage.util.CardUtil; @@ -55,7 +53,7 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect { private boolean previousZone; /** - * + * * @param zone Zone the card should return to */ public ReturnFromExileForSourceEffect(Zone zone) { @@ -65,12 +63,13 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect { public ReturnFromExileForSourceEffect(Zone zone, boolean tapped) { this(zone, tapped, true); } - + /** - * + * * @param zone * @param tapped - * @param previousZone if this is used from a dies leave battlefield or destroyed trigger, the exile zone is based on previous zone of the object + * @param previousZone if this is used from a dies leave battlefield or + * destroyed trigger, the exile zone is based on previous zone of the object */ public ReturnFromExileForSourceEffect(Zone zone, boolean tapped, boolean previousZone) { super(Outcome.PutCardInPlay); @@ -94,24 +93,25 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int zoneChangeCounter = source.getSourceObjectZoneChangeCounter() - (previousZone ? 1:0); - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter); - ExileZone exile = game.getExile().getExileZone(exileId); + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (sourceObject != null && controller != null) { + int zoneChangeCounter = source.getSourceObjectZoneChangeCounter(); + if (zoneChangeCounter > 0 && previousZone && !(sourceObject instanceof PermanentToken)) { + zoneChangeCounter--; + } + ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter)); if (exile != null) { // null is valid if source left battlefield before enters the battlefield effect resolved - LinkedList cards = new LinkedList<>(exile); - for (UUID cardId: cards) { - Card card = game.getCard(cardId); - if (card == null) { - return false; + if (returnToZone.equals(Zone.BATTLEFIELD)) { + for (Card card : exile.getCards(game)) { + Player owner = game.getPlayer(card.getOwnerId()); + if (owner != null) { + owner.putOntoBattlefieldWithInfo(card, game, Zone.EXILED, source.getSourceId()); + } } - if (!game.isSimulation()) { - game.informPlayers(controller.getLogName() + " moves " + card.getLogName() + " from exile to " + returnToZone.toString().toLowerCase()); - } - card.moveToZone(returnToZone, source.getSourceId(), game, tapped); + } else { + controller.moveCards(exile, Zone.EXILED, returnToZone, source, game); } - exile.clear(); } return true; } @@ -121,7 +121,7 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect { private void setText() { StringBuilder sb = new StringBuilder(); sb.append("return the exiled cards "); - switch(returnToZone) { + switch (returnToZone) { case BATTLEFIELD: sb.append("to the battlefield under its owner's control"); if (tapped) { diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 228cbb7de9..052ab43222 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -2914,6 +2914,12 @@ public abstract class PlayerImpl implements Player, Serializable { result |= moveCardToHandWithInfo(card, source == null ? null : source.getSourceId(), game, fromZone); } return result; + case BATTLEFIELD: + result = false; + for (Card card : cards) { + result |= putOntoBattlefieldWithInfo(card, game, fromZone, source == null ? null : source.getSourceId()); + } + return result; default: throw new UnsupportedOperationException("to Zone not supported yet"); } diff --git a/Utils/release/getting_implemented_cards.txt b/Utils/release/getting_implemented_cards.txt index 9fd9eceae2..9efee63672 100644 --- a/Utils/release/getting_implemented_cards.txt +++ b/Utils/release/getting_implemented_cards.txt @@ -146,6 +146,9 @@ git log 757b9ea99ec1f0ce46bb533f9f86f3473d122a60..HEAD --diff-filter=A --name-st since 1.4.2.v0 git log cd0cba6ec7d8799bb85247b7b4f5d545e170b093..HEAD --diff-filter=A --name-status | sed -ne "s/^A[^u]Mage.Sets\/src\/mage\/sets\///p" | sort > added_cards.txt +since 1.4.2.v1 +git log 0b26aaff6ec033a538179bf607b1c7a7736aedb2..HEAD --diff-filter=A --name-status | sed -ne "s/^A[^u]Mage.Sets\/src\/mage\/sets\///p" | sort > added_cards.txt + 3. Copy added_cards.txt to trunk\Utils folder 4. Run script: > perl extract_in_wiki_format.perl From d38910b81b7b9268148775f6845859a9b7bbef34 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 6 Jul 2015 16:49:24 +0200 Subject: [PATCH 5/6] * Liliana, Heretical Healer - Fixed that the Zombie token was not created if Liliana was exiled by her effect. --- .../magicorigins/LilianaHereticalHealer.java | 5 ++- .../abilities/keywords/TransformTest.java | 3 ++ ...ExileAndReturnTransformedSourceEffect.java | 43 ++++++++++++++----- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/Mage.Sets/src/mage/sets/magicorigins/LilianaHereticalHealer.java b/Mage.Sets/src/mage/sets/magicorigins/LilianaHereticalHealer.java index 7d7a1ee93f..a3a6bc156b 100644 --- a/Mage.Sets/src/mage/sets/magicorigins/LilianaHereticalHealer.java +++ b/Mage.Sets/src/mage/sets/magicorigins/LilianaHereticalHealer.java @@ -30,6 +30,7 @@ package mage.sets.magicorigins; import java.util.UUID; import mage.MageInt; import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.ExileAndReturnTransformedSourceEffect; import mage.abilities.keyword.LifelinkAbility; import mage.abilities.keyword.TransformAbility; @@ -42,6 +43,7 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.AnotherPredicate; import mage.filter.predicate.permanent.ControllerPredicate; import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.ZombieToken; /** * @@ -74,7 +76,8 @@ public class LilianaHereticalHealer extends CardImpl { this.addAbility(LifelinkAbility.getInstance()); // Whenever another nontoken creature you control dies, exile Liliana Heretical Healer, then return her to the battlefield transformed under her owner's control. If you do, put a 2/2 black Zombie creature token onto the battlefield. - this.addAbility(new DiesCreatureTriggeredAbility(new ExileAndReturnTransformedSourceEffect(ExileAndReturnTransformedSourceEffect.Gender.FEMAL), false, filter)); + this.addAbility(new DiesCreatureTriggeredAbility(new ExileAndReturnTransformedSourceEffect(ExileAndReturnTransformedSourceEffect.Gender.FEMAL, + new CreateTokenEffect(new ZombieToken(expansionSetCode))), false, filter)); } public LilianaHereticalHealer(final LilianaHereticalHealer card) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java index 8ed4b75165..eb3cf1762d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java @@ -87,6 +87,9 @@ public class TransformTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Liliana, Heretical Healer", 0); assertPermanentCount(playerA, "Liliana, Defiant Necromancer", 1); assertCounterCount("Liliana, Defiant Necromancer", CounterType.LOYALTY, 3); + + assertPermanentCount(playerA, "Zombie", 1); + } } diff --git a/Mage/src/mage/abilities/effects/common/ExileAndReturnTransformedSourceEffect.java b/Mage/src/mage/abilities/effects/common/ExileAndReturnTransformedSourceEffect.java index 3e6bd508bb..7e328a9b8b 100644 --- a/Mage/src/mage/abilities/effects/common/ExileAndReturnTransformedSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/ExileAndReturnTransformedSourceEffect.java @@ -7,6 +7,8 @@ package mage.abilities.effects.common; import mage.MageObject; import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.TransformAbility; import mage.cards.Card; @@ -19,26 +21,40 @@ import mage.players.Player; * * @author LevelX2 */ - public class ExileAndReturnTransformedSourceEffect extends OneShotEffect { - - public static enum Gender { MALE, FEMAL }; - + + public static enum Gender { + + MALE, FEMAL + }; + + protected Effect additionalEffect; + public ExileAndReturnTransformedSourceEffect(Gender gender) { - super(Outcome.Benefit); - this.staticText = "exile {this}, then return " + (gender.equals(Gender.MALE) ? "him":"her") - + " to the battlefield transformed under" + (gender.equals(Gender.MALE) ? "his":"her")+ " owner's control"; + this(gender, null); } - + + /** + * @param gender + * @param additionalEffect that effect is applies as source is exiled + */ + public ExileAndReturnTransformedSourceEffect(Gender gender, Effect additionalEffect) { + super(Outcome.Benefit); + this.additionalEffect = additionalEffect; + this.staticText = "exile {this}, then return " + (gender.equals(Gender.MALE) ? "him" : "her") + + " to the battlefield transformed under" + (gender.equals(Gender.MALE) ? "his" : "her") + " owner's control"; + } + public ExileAndReturnTransformedSourceEffect(final ExileAndReturnTransformedSourceEffect effect) { super(effect); + this.additionalEffect = effect.additionalEffect; } - + @Override public ExileAndReturnTransformedSourceEffect copy() { return new ExileAndReturnTransformedSourceEffect(this); } - + @Override public boolean apply(Game game, Ability source) { MageObject sourceObject = source.getSourceObjectIfItStillExists(game); @@ -48,6 +64,13 @@ public class ExileAndReturnTransformedSourceEffect extends OneShotEffect { if (controller.moveCards(card, Zone.BATTLEFIELD, Zone.EXILED, source, game)) { game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), Boolean.TRUE); controller.putOntoBattlefieldWithInfo(card, game, Zone.EXILED, source.getSourceId()); + if (additionalEffect != null) { + if (additionalEffect instanceof ContinuousEffect) { + game.addEffect((ContinuousEffect) additionalEffect, source); + } else { + additionalEffect.apply(game, source); + } + } } } return true; From 7bb93fbb25adaa1e5cae4c558305ff83697873ba Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 6 Jul 2015 17:23:47 +0200 Subject: [PATCH 6/6] * Willbreaker - Fixed that it didn't trigger when targeted by abilities. --- .../mage/sets/magicorigins/Willbreaker.java | 4 +- .../sets/newphyrexia/BlindingSouleater.java | 9 +-- .../cards/triggers/BecomesTheTargetTest.java | 71 +++++++++++++++++++ 3 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/triggers/BecomesTheTargetTest.java diff --git a/Mage.Sets/src/mage/sets/magicorigins/Willbreaker.java b/Mage.Sets/src/mage/sets/magicorigins/Willbreaker.java index c6dcbe34c6..0480e397f3 100644 --- a/Mage.Sets/src/mage/sets/magicorigins/Willbreaker.java +++ b/Mage.Sets/src/mage/sets/magicorigins/Willbreaker.java @@ -43,7 +43,6 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; -import mage.game.stack.StackObject; import mage.players.Player; import mage.target.targetpointer.FixedTarget; @@ -96,8 +95,7 @@ class WillbreakerTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (getControllerId().equals(event.getPlayerId())) { Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.getCardType().contains(CardType.CREATURE) - && StackObject.class.isInstance(game.getObject(event.getSourceId()))) { + if (permanent != null && permanent.getCardType().contains(CardType.CREATURE)) { Player controller = game.getPlayer(getControllerId()); if (controller != null && controller.hasOpponent(permanent.getControllerId(), game)) { getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId())); diff --git a/Mage.Sets/src/mage/sets/newphyrexia/BlindingSouleater.java b/Mage.Sets/src/mage/sets/newphyrexia/BlindingSouleater.java index 34cd7866f8..97edb20189 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/BlindingSouleater.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/BlindingSouleater.java @@ -28,16 +28,16 @@ package mage.sets.newphyrexia; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.ColoredManaSymbol; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.PhyrexianManaCost; import mage.abilities.effects.common.TapTargetEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.target.common.TargetCreaturePermanent; /** @@ -54,6 +54,7 @@ public class BlindingSouleater extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(3); + // {WP},{T}: Tap target creature. ( can be paid with either or 2 life.) SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TapTargetEffect(), new PhyrexianManaCost(ColoredManaSymbol.W)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BecomesTheTargetTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BecomesTheTargetTest.java new file mode 100644 index 0000000000..a6bb72f405 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BecomesTheTargetTest.java @@ -0,0 +1,71 @@ +/* + * 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; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class BecomesTheTargetTest extends CardTestPlayerBase { + + /** + * Willbreaker is not working when an ability is targeting the opponet's + * creature. Only spells. + * + */ + @Test + public void testWillbreakerAbility() { + // Whenever a creature an opponent controls becomes the target of a spell or ability you control, gain control of that creature for as long as you control Willbreaker. + addCard(Zone.BATTLEFIELD, playerB, "Willbreaker", 1); + addCard(Zone.BATTLEFIELD, playerB, "Blinding Souleater", 1); + addCard(Zone.BATTLEFIELD, playerB, "Plains", 1); + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{WP},{T}: Tap target creature", "Silvercoat Lion"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertPermanentCount(playerA, "Silvercoat Lion", 0); + assertTapped("Silvercoat Lion", true); + + assertPermanentCount(playerB, "Silvercoat Lion", 1); + assertPermanentCount(playerB, "Willbreaker", 1); + assertPermanentCount(playerB, "Blinding Souleater", 1); + + } +}