From 821398211b02ca20e681cd46e762afc2c3e5b054 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 17 Aug 2014 01:12:41 +0200 Subject: [PATCH] * Counterbalance, Hisako, Minamp Sensai - Fixed that converted mana comparison of spilt cards did not work correctly. --- .../HisokaMinamoSensei.java | 12 +-- .../mage/sets/coldsnap/Counterbalance.java | 5 +- .../abilities/keywords/TransmuteTest.java | 87 +++++++++++++++++++ .../counterspell/CounterbalanceTest.java | 33 +++++++ .../condition/common/MyTurnCondition.java | 2 +- .../abilities/keyword/TransmuteAbility.java | 14 ++- .../filter/predicate/IntComparePredicate.java | 1 + Mage/src/mage/util/CardUtil.java | 32 +++++++ 8 files changed, 177 insertions(+), 9 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransmuteTest.java diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/HisokaMinamoSensei.java b/Mage.Sets/src/mage/sets/championsofkamigawa/HisokaMinamoSensei.java index f7c9995c620..6157900979c 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/HisokaMinamoSensei.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/HisokaMinamoSensei.java @@ -48,6 +48,7 @@ import mage.game.stack.Spell; import mage.players.Player; import mage.target.TargetSpell; import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; /** * @author LevelX @@ -85,7 +86,7 @@ public class HisokaMinamoSensei extends CardImpl { class HisokaMinamoSenseiDiscardTargetCost extends CostImpl { - protected int convertedManaCosts = 0; + protected Card card = null; public HisokaMinamoSenseiDiscardTargetCost(TargetCardInHand target) { this.addTarget(target); @@ -101,11 +102,10 @@ class HisokaMinamoSenseiDiscardTargetCost extends CostImpl { if (targets.choose(Outcome.Discard, controllerId, sourceId, game)) { Player player = game.getPlayer(controllerId); for (UUID targetId: targets.get(0).getTargets()) { - Card card = player.getHand().get(targetId, game); + card = player.getHand().get(targetId, game); if (card == null) { return false; } - convertedManaCosts = card.getManaCost().convertedManaCost(); paid |= player.discard(card, null, game); } @@ -123,8 +123,8 @@ class HisokaMinamoSenseiDiscardTargetCost extends CostImpl { return new HisokaMinamoSenseiDiscardTargetCost(this); } - public int getConvertedCosts() { - return convertedManaCosts; + public Card getDiscardedCard() { + return card; } } @@ -144,7 +144,7 @@ class HisokaMinamoSenseiCounterEffect extends OneShotEffect { Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); if (spell != null) { HisokaMinamoSenseiDiscardTargetCost cost = (HisokaMinamoSenseiDiscardTargetCost) source.getCosts().get(0); - if (cost != null && cost.getConvertedCosts() == spell.getConvertedManaCost()) { + if (cost != null && CardUtil.convertedManaCostsIsEqual(cost.getDiscardedCard(), spell)) { return game.getStack().counter(targetPointer.getFirst(game, source), source.getSourceId(), game); } } diff --git a/Mage.Sets/src/mage/sets/coldsnap/Counterbalance.java b/Mage.Sets/src/mage/sets/coldsnap/Counterbalance.java index de1b4dbc076..2e9437a4e82 100644 --- a/Mage.Sets/src/mage/sets/coldsnap/Counterbalance.java +++ b/Mage.Sets/src/mage/sets/coldsnap/Counterbalance.java @@ -44,6 +44,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.players.Player; +import mage.util.CardUtil; /** * @@ -99,7 +100,7 @@ class CounterbalanceEffect extends OneShotEffect { CardsImpl cards = new CardsImpl(); cards.add(topcard); controller.revealCards(sourcePermanent.getName(), cards, game); - if (topcard.getManaCost().convertedManaCost() == spell.getConvertedManaCost()) { + if (CardUtil.convertedManaCostsIsEqual(topcard, spell)) { return game.getStack().counter(spell.getId(), source.getSourceId(), game); } } @@ -108,4 +109,6 @@ class CounterbalanceEffect extends OneShotEffect { } return false; } + + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransmuteTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransmuteTest.java new file mode 100644 index 00000000000..0720c51cbe1 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransmuteTest.java @@ -0,0 +1,87 @@ +/* + * 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.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * 702.52. Transmute + * + * 702.52a Transmute is an activated ability that functions only while the card with transmute is + * in a player’s hand. “Transmute [cost]” means “[Cost], Discard this card: Search your library for + * a card with the same converted mana cost as the discarded card, reveal that card, and put it into + * your hand. Then shuffle your library. Play this ability only any time you could play a sorcery.” + * + * 702.52b Although the transmute ability is playable only if the card is in a player’s hand, it + * continues to exist while the object is in play and in all other zones. Therefore objects with + * transmute will be affected by effects that depend on objects having one or more activated abilities. + * + * @author LevelX2 + */ + +public class TransmuteTest extends CardTestPlayerBase{ + + @Test + public void searchSimpleOneManaCmcSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.HAND, playerA, "Dizzy Spell"); + + addCard(Zone.LIBRARY, playerA, "Lightning Bolt"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Transmute {1}{U}{U} ({1}{U}{U}, Discard this card: Search your library for a card with the same converted mana cost as this card, reveal it, and put it into your hand. Then shuffle your library. Transmute only as a sorcery.)"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Dizzy Spell", 1); + assertHandCount(playerA, "Lightning Bolt", 1); + } + + @Test + public void searchSplittedCardOneManaCmcSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.HAND, playerA, "Dizzy Spell"); + + addCard(Zone.LIBRARY, playerA, "Wear // Tear"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Transmute {1}{U}{U} ({1}{U}{U}, Discard this card: Search your library for a card with the same converted mana cost as this card, reveal it, and put it into your hand. Then shuffle your library. Transmute only as a sorcery.)"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Dizzy Spell", 1); + assertHandCount(playerA, "Wear", 1); // Filter search can only search for one side of a split card + assertHandCount(playerA, "Tear", 1); // Filter search can only search for one side of a split card + } + +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/CounterbalanceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/CounterbalanceTest.java index 0f350df73a3..47c061f3fc7 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/CounterbalanceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/CounterbalanceTest.java @@ -76,4 +76,37 @@ public class CounterbalanceTest extends CardTestPlayerBase { } + /** + * Test that if the top cardis a split card, both casting costs of the split cards + * count to counter the spell. If one of the split cards halfes has the equal casting + * cost, the spell is countered. + * + */ + + @Test + public void testSplitCard() { + addCard(Zone.HAND, playerA, "Typhoid Rats"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Counterbalance"); + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + + addCard(Zone.LIBRARY, playerB, "Wear // Tear"); // CMC 2 and 1 + skipInitShuffling(); // so the set to top card stays at top + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Typhoid Rats"); + setChoice(playerB, "Yes"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertPermanentCount(playerA, "Typhoid Rats", 0); + assertGraveyardCount(playerA, "Typhoid Rats", 1); + assertGraveyardCount(playerA, 1); + assertGraveyardCount(playerB, 0); + + } } diff --git a/Mage/src/mage/abilities/condition/common/MyTurnCondition.java b/Mage/src/mage/abilities/condition/common/MyTurnCondition.java index 7f7838f2cd4..fbe727894dc 100644 --- a/Mage/src/mage/abilities/condition/common/MyTurnCondition.java +++ b/Mage/src/mage/abilities/condition/common/MyTurnCondition.java @@ -32,7 +32,7 @@ import mage.abilities.condition.Condition; import mage.game.Game; public class MyTurnCondition implements Condition { - private static MyTurnCondition fInstance = new MyTurnCondition(); + private final static MyTurnCondition fInstance = new MyTurnCondition(); public static Condition getInstance() { return fInstance; diff --git a/Mage/src/mage/abilities/keyword/TransmuteAbility.java b/Mage/src/mage/abilities/keyword/TransmuteAbility.java index b268d2ef1ba..156a9dc8848 100644 --- a/Mage/src/mage/abilities/keyword/TransmuteAbility.java +++ b/Mage/src/mage/abilities/keyword/TransmuteAbility.java @@ -21,6 +21,18 @@ import java.util.UUID; import mage.constants.TimingRule; /** + * + * 702.52. Transmute + * + * 702.52a Transmute is an activated ability that functions only while the card with transmute is + * in a player’s hand. “Transmute [cost]” means “[Cost], Discard this card: Search your library for + * a card with the same converted mana cost as the discarded card, reveal that card, and put it into + * your hand. Then shuffle your library. Play this ability only any time you could play a sorcery.” + * + * 702.52b Although the transmute ability is playable only if the card is in a player’s hand, it + * continues to exist while the object is in play and in all other zones. Therefore objects with + * transmute will be affected by effects that depend on objects having one or more activated abilities. + * * @author Loki */ public class TransmuteAbility extends SimpleActivatedAbility { @@ -72,7 +84,7 @@ class TransmuteEffect extends OneShotEffect { for (UUID cardId : target.getTargets()) { Card card = player.getLibrary().remove(cardId, game); if (card != null) { - card.moveToZone(Zone.HAND, source.getSourceId(), game, false); + player.moveCardToHandWithInfo(card, source.getSourceId(), game, Zone.LIBRARY); revealed.add(card); } } diff --git a/Mage/src/mage/filter/predicate/IntComparePredicate.java b/Mage/src/mage/filter/predicate/IntComparePredicate.java index 961c60e88dd..e9229bbd2da 100644 --- a/Mage/src/mage/filter/predicate/IntComparePredicate.java +++ b/Mage/src/mage/filter/predicate/IntComparePredicate.java @@ -34,6 +34,7 @@ import mage.game.Game; /** * * @author North + * @param */ public abstract class IntComparePredicate implements Predicate { diff --git a/Mage/src/mage/util/CardUtil.java b/Mage/src/mage/util/CardUtil.java index 55c17cbada0..c448a9f2161 100644 --- a/Mage/src/mage/util/CardUtil.java +++ b/Mage/src/mage/util/CardUtil.java @@ -28,7 +28,9 @@ package mage.util; +import java.util.HashSet; import java.util.Iterator; +import java.util.Set; import java.util.UUID; import mage.MageObject; @@ -46,9 +48,11 @@ import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.VariableManaCost; import mage.cards.Card; +import mage.cards.SplitCard; import mage.constants.CardType; import mage.game.Game; import mage.game.permanent.token.Token; +import mage.game.stack.Spell; import mage.util.functions.CopyFunction; import mage.util.functions.CopyTokenFunction; @@ -515,4 +519,32 @@ public class CardUtil { public static String addToolTipMarkTags(String text) { return "" + text + ""; } + + public static boolean convertedManaCostsIsEqual(MageObject object1, MageObject object2) { + Set cmcObject1 = getCMC(object1); + Set cmcObject2 = getCMC(object2); + for (Integer integer :cmcObject1) { + if (cmcObject2.contains(integer)) { + return true; + } + } + return false; + } + + public static Set getCMC(MageObject object) { + Set cmcObject = new HashSet<>(); + if (object instanceof Card) { + Card card = (Card) object; + if (card instanceof SplitCard) { + SplitCard splitCard = (SplitCard) card; + cmcObject.add(splitCard.getLeftHalfCard().getManaCost().convertedManaCost()); + cmcObject.add(splitCard.getRightHalfCard().getManaCost().convertedManaCost()); + } else { + cmcObject.add(card.getManaCost().convertedManaCost()); + } + } else if (object instanceof Spell) { + cmcObject.add(((Spell)object).getConvertedManaCost()); + } + return cmcObject; + } }