From bf21de745a7f9f60f8610a94d23644cacb863c94 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 1 Jul 2015 13:37:32 +0200 Subject: [PATCH 1/4] * Fixed a bug of check if two objects sharing a colors were also to colorless objects did return a positive result (e.g. caused Dream Halls to cast Artifacts by discarding lands). --- .../src/mage/sets/stronghold/DreamHalls.java | 5 +- .../src/mage/sets/urzassaga/Persecute.java | 9 +- .../cards/cost/alternate/OmniscienceTest.java | 35 +- .../UseAlternateSourceCostsTets.java | 104 ++++++ .../base/impl/CardTestPlayerAPIImpl.java | 326 ++++++++++-------- Mage/src/mage/ObjectColor.java | 131 +++---- Mage/src/mage/players/PlayerImpl.java | 16 + 7 files changed, 388 insertions(+), 238 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTets.java diff --git a/Mage.Sets/src/mage/sets/stronghold/DreamHalls.java b/Mage.Sets/src/mage/sets/stronghold/DreamHalls.java index 317db0e018..e0a0b4c45f 100644 --- a/Mage.Sets/src/mage/sets/stronghold/DreamHalls.java +++ b/Mage.Sets/src/mage/sets/stronghold/DreamHalls.java @@ -58,7 +58,6 @@ public class DreamHalls extends CardImpl { super(ownerId, 28, "Dream Halls", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}{U}"); this.expansionSetCode = "STH"; - // Rather than pay the mana cost for a spell, its controller may discard a card that shares a color with that spell. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DreamHallsEffect())); } @@ -102,13 +101,13 @@ class DreamHallsEffect extends ContinuousEffectImpl { public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - for (UUID playerId: controller.getInRange()) { + for (UUID playerId : controller.getInRange()) { Player player = game.getPlayer(playerId); if (player != null) { player.getAlternativeSourceCosts().add(alternativeCastingCostAbility); } } - + return true; } return false; diff --git a/Mage.Sets/src/mage/sets/urzassaga/Persecute.java b/Mage.Sets/src/mage/sets/urzassaga/Persecute.java index 5c83ad2eea..82d8dfe6f5 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/Persecute.java +++ b/Mage.Sets/src/mage/sets/urzassaga/Persecute.java @@ -29,6 +29,7 @@ package mage.sets.urzassaga; import java.util.Set; import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; @@ -52,7 +53,6 @@ public class Persecute extends CardImpl { super(ownerId, 146, "Persecute", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{2}{B}{B}"); this.expansionSetCode = "USG"; - // Choose a color. Target player reveals his or her hand and discards all cards of that color. this.getSpellAbility().addEffect(new PersecuteEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); @@ -88,19 +88,20 @@ class PersecuteEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getFirstTarget()); - if (controller != null) { + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null && sourceObject != null) { ChoiceColor choice = new ChoiceColor(); while (!choice.isChosen()) { controller.choose(outcome, choice, game); if (!controller.isInGame()) { return false; } - } + } if (choice.getColor() == null) { return false; } Cards hand = controller.getHand(); - controller.revealCards("Persecute", hand, game); + controller.revealCards(sourceObject.getIdName(), hand, game); Set cards = hand.getCards(game); for (Card card : cards) { if (card != null && card.getColor(game).shares(choice.getColor())) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/OmniscienceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/OmniscienceTest.java index 941a069618..02ab0f8a23 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/OmniscienceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/OmniscienceTest.java @@ -2,44 +2,43 @@ package org.mage.test.cards.cost.alternate; import mage.constants.PhaseStep; import mage.constants.Zone; - import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; public class OmniscienceTest extends CardTestPlayerBase { - - @Test - public void testSpellNoCost() { + + @Test + public void testSpellNoCost() { + // You may cast nonland cards from your hand without paying their mana costs. addCard(Zone.BATTLEFIELD, playerA, "Omniscience", 1); - + addCard(Zone.HAND, playerA, "Gray Ogre", 1); - + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gray Ogre"); setStopAt(1, PhaseStep.END_TURN); execute(); - + //Gray Ogre is cast because it is free assertPermanentCount(playerA, "Gray Ogre", 1); - } - - @Test - public void testSpellHasCostIfCastFromGraveyard() { + } + + @Test + public void testSpellHasCostIfCastFromGraveyard() { + // You may cast nonland cards from your hand without paying their mana costs. addCard(Zone.BATTLEFIELD, playerA, "Omniscience", 1); - + addCard(Zone.BATTLEFIELD, playerA, "Haakon, Stromgald Scourge", 1); - + addCard(Zone.GRAVEYARD, playerA, "Knight of the White Orchid", 1); - + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Knight of the White Orchid"); setStopAt(1, PhaseStep.END_TURN); execute(); - + //Knight of the White Orchid was not cast due to lack of mana assertPermanentCount(playerA, "Knight of the White Orchid", 0); - } - - + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTets.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTets.java new file mode 100644 index 0000000000..c33fdc961e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTets.java @@ -0,0 +1,104 @@ +/* + * 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.cost.alternate; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class UseAlternateSourceCostsTets extends CardTestPlayerBase { + + @Test + public void DreamHallsCastColoredSpell() { + // Rather than pay the mana cost for a spell, its controller may discard a card that shares a color with that spell. + addCard(Zone.BATTLEFIELD, playerA, "Dream Halls", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); // Add the mountains so the spell is included in teh available spells + + addCard(Zone.HAND, playerA, "Gray Ogre", 1); // Creature 3/1 + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gray Ogre"); // Cast Orgre by discarding the Lightning Bolt + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + //Gray Ogre is cast with the discard + assertPermanentCount(playerA, "Gray Ogre", 1); + assertGraveyardCount(playerA, "Lightning Bolt", 1); + assertTapped("Mountain", false); + } + + @Test + public void DreamHallsCantCastColorlessSpell() { + // Rather than pay the mana cost for a spell, its controller may discard a card that shares a color with that spell. + addCard(Zone.BATTLEFIELD, playerA, "Dream Halls", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); // Add the mountains so the spell is included in teh available spells + + addCard(Zone.HAND, playerA, "Juggernaut", 1); // Creature 5/3 - {4} + addCard(Zone.HAND, playerA, "Haunted Plate Mail", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Juggernaut"); // Cast Juggernaut by discarding Haunted Plate Mail may not work + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Haunted Plate Mail", 0); + assertTapped("Mountain", true); + //Juggernaut is not cast by alternate casting costs + assertPermanentCount(playerA, "Juggernaut", 1); + } + + @Test + public void DreamHallsCastWithFutureSight() { + // Rather than pay the mana cost for a spell, its controller may discard a card that shares a color with that spell. + addCard(Zone.BATTLEFIELD, playerA, "Dream Halls", 1); + // Play with the top card of your library revealed. + // You may play the top card of your library. + addCard(Zone.BATTLEFIELD, playerA, "Future Sight", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); // Add the mountains so the spell is included in teh available spells + + addCard(Zone.LIBRARY, playerA, "Gray Ogre", 1); // Creature 3/1 + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + skipInitShuffling(); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gray Ogre"); // Cast Orgre by discarding the Lightning Bolt + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertTapped("Mountain", false); + //Gray Ogre is cast with the discard + assertPermanentCount(playerA, "Gray Ogre", 1); + assertGraveyardCount(playerA, "Lightning Bolt", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index 8b910600cb..331f42e4fe 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -41,13 +41,14 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement // Defines the constant if for activate ability is not target but a ability on the stack to define public static final String NO_TARGET = "NO_TARGET"; - + protected GameOptions gameOptions; - + protected String deckNameA; protected String deckNameB; - + protected enum ExpectedType { + TURN_NUMBER, RESULT, LIFE, @@ -55,14 +56,15 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement GRAVEYARD, UNKNOWN } - + static { // CardScanner.scanned = true; CardScanner.scan(); } /** - * Default game initialization params for red player (that plays with Mountains) + * Default game initialization params for red player (that plays with + * Mountains) */ @Override public void useRedDefault() { @@ -88,7 +90,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } /** - * Default game initialization params for white player (that plays with Plains) + * Default game initialization params for white player (that plays with + * Plains) */ public void useWhiteDefault() { // *** ComputerA *** @@ -103,7 +106,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement removeAllCardsFromLibrary(playerB); addCard(Zone.LIBRARY, playerB, "Plains", 10); } - + @Before public void reset() throws GameException, FileNotFoundException { if (currentGame != null) { @@ -120,7 +123,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement stopAtStep = PhaseStep.UNTAP; for (Player player : currentGame.getPlayers().values()) { - TestPlayer testPlayer = (TestPlayer)player; + TestPlayer testPlayer = (TestPlayer) player; getCommands(testPlayer).clear(); getLibraryCards(testPlayer).clear(); getHandCards(testPlayer).clear(); @@ -132,11 +135,11 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } abstract protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException; - + protected TestPlayer createPlayer(Game game, TestPlayer player, String name) throws GameException { return createPlayer(game, player, name, "RB Aggro.dck"); } - + protected TestPlayer createPlayer(Game game, TestPlayer player, String name, String deckName) throws GameException { player = createNewPlayer(name); player.setTestMode(true); @@ -147,7 +150,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size()); } game.loadCards(deck.getCards(), player.getId()); - game.loadCards(deck.getSideboard(), player.getId()); + game.loadCards(deck.getSideboard(), player.getId()); game.addPlayer(player, deck); return player; @@ -156,7 +159,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement /** * Starts testing card by starting current game. * - * @throws IllegalStateException In case game wasn't created previously. Use {@link #load} method to initialize the game. + * @throws IllegalStateException In case game wasn't created previously. Use + * {@link #load} method to initialize the game. */ public void execute() throws IllegalStateException { if (currentGame == null || activePlayer == null) { @@ -164,7 +168,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } for (Player player : currentGame.getPlayers().values()) { - TestPlayer testPlayer = (TestPlayer)player; + TestPlayer testPlayer = (TestPlayer) player; currentGame.cheat(player.getId(), getCommands(testPlayer)); currentGame.cheat(player.getId(), getLibraryCards(testPlayer), getHandCards(testPlayer), getBattlefieldCards(testPlayer), getGraveCards(testPlayer)); @@ -182,13 +186,11 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement logger.info("Test has been executed. Execution time: " + (t2 - t1) / 1000000 + " ms"); } - - - + protected TestPlayer createNewPlayer(String playerName) { return createPlayer(playerName); } - + protected Player getPlayerFromName(String playerName, String line) { Player player = null; switch (playerName) { @@ -209,10 +211,11 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } return player; } - + /** - * Removes all cards from player's library from the game. - * Usually this should be used once before initialization to form the library in certain order. + * Removes all cards from player's library from the game. Usually this + * should be used once before initialization to form the library in certain + * order. * * @param player {@link Player} to remove all library cards from. */ @@ -221,8 +224,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } /** - * Removes all cards from player's hand from the game. - * Usually this should be used once before initialization to set the players hand. + * Removes all cards from player's hand from the game. Usually this should + * be used once before initialization to set the players hand. * * @param player {@link Player} to remove all cards from hand. */ @@ -234,7 +237,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * Add a card to specified zone of specified player. * * @param gameZone {@link mage.constants.Zone} to add cards to. - * @param player {@link Player} to add cards for. Use either playerA or playerB. + * @param player {@link Player} to add cards for. Use either playerA or + * playerB. * @param cardName Card name in string format. */ public void addCard(Zone gameZone, TestPlayer player, String cardName) { @@ -245,9 +249,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * Add any amount of cards to specified zone of specified player. * * @param gameZone {@link mage.constants.Zone} to add cards to. - * @param player {@link Player} to add cards for. Use either playerA or playerB. + * @param player {@link Player} to add cards for. Use either playerA or + * playerB. * @param cardName Card name in string format. - * @param count Amount of cards to be added. + * @param count Amount of cards to be added. */ public void addCard(Zone gameZone, TestPlayer player, String cardName, int count) { addCard(gameZone, player, cardName, count, false); @@ -257,11 +262,13 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * Add any amount of cards to specified zone of specified player. * * @param gameZone {@link mage.constants.Zone} to add cards to. - * @param player {@link Player} to add cards for. Use either playerA or playerB. + * @param player {@link Player} to add cards for. Use either playerA or + * playerB. * @param cardName Card name in string format. - * @param count Amount of cards to be added. - * @param tapped In case gameZone is Battlefield, determines whether permanent should be tapped. - * In case gameZone is other than Battlefield, {@link IllegalArgumentException} is thrown + * @param count Amount of cards to be added. + * @param tapped In case gameZone is Battlefield, determines whether + * permanent should be tapped. In case gameZone is other than Battlefield, + * {@link IllegalArgumentException} is thrown */ public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) { @@ -315,7 +322,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * Set player's initial life count. * * @param player {@link Player} to set life count for. - * @param life Life count to set. + * @param life Life count to set. */ public void setLife(TestPlayer player, int life) { getCommands(player).put(Zone.OUTSIDE, "life:" + String.valueOf(life)); @@ -323,6 +330,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement /** * Define turn number to stop the game on. + * * @param turn */ @Override @@ -332,8 +340,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } /** - * Define turn number and step to stop the game on. - * The game stops after executing the step + * Define turn number and step to stop the game on. The game stops after + * executing the step + * * @param turn * @param step */ @@ -389,7 +398,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * Assert player's life count after test execution. * * @param player {@link Player} to get life for comparison. - * @param life Expected player's life to compare with. + * @param life Expected player's life to compare with. */ @Override public void assertLife(Player player, int life) throws AssertionError { @@ -400,19 +409,20 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement /** * Assert creature's power and toughness by card name. *

- * Throws {@link AssertionError} in the following cases: - * 1. no such player - * 2. no such creature under player's control - * 3. depending on comparison scope: - * 3a. any: no creature under player's control with the specified p\t params - * 3b. all: there is at least one creature with the cardName with the different p\t params + * Throws {@link AssertionError} in the following cases: 1. no such player + * 2. no such creature under player's control 3. depending on comparison + * scope: 3a. any: no creature under player's control with the specified p\t + * params 3b. all: there is at least one creature with the cardName with the + * different p\t params * - * @param player {@link Player} to get creatures for comparison. - * @param cardName Card name to compare with. - * @param power Expected power to compare with. + * @param player {@link Player} to get creatures for comparison. + * @param cardName Card name to compare with. + * @param power Expected power to compare with. * @param toughness Expected toughness to compare with. - * @param scope {@link mage.filter.Filter.ComparisonScope} Use ANY, if you want "at least one creature with given name should have specified p\t" - * Use ALL, if you want "all creature with gived name should have specified p\t" + * @param scope {@link mage.filter.Filter.ComparisonScope} Use ANY, if you + * want "at least one creature with given name should have specified p\t" + * Use ALL, if you want "all creature with gived name should have specified + * p\t" */ @Override public void assertPowerToughness(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope) @@ -442,18 +452,19 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } } - Assert.assertTrue("There is no such permanent under player's control, player=" + player.getName() + - ", cardName=" + cardName, count > 0); + Assert.assertTrue("There is no such permanent under player's control, player=" + player.getName() + + ", cardName=" + cardName, count > 0); if (scope.equals(Filter.ComparisonScope.Any)) { - Assert.assertTrue("There is no such creature under player's control with specified power&toughness, player=" + player.getName() + - ", cardName=" + cardName + " (found similar: " + found + ", one of them: power=" + foundPower + " toughness=" + foundToughness + ")", fit > 0); + Assert.assertTrue("There is no such creature under player's control with specified power&toughness, player=" + player.getName() + + ", cardName=" + cardName + " (found similar: " + found + ", one of them: power=" + foundPower + " toughness=" + foundToughness + ")", fit > 0); } } /** - * See {@link #assertPowerToughness(mage.players.Player, String, int, int, mage.filter.Filter.ComparisonScope)} - * + * See + * {@link #assertPowerToughness(mage.players.Player, String, int, int, mage.filter.Filter.ComparisonScope)} + * * @param player * @param cardName * @param power @@ -478,15 +489,15 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } } - Assert.assertNotNull("There is no such permanent under player's control, player=" + player.getName() + - ", cardName=" + cardName, found); + Assert.assertNotNull("There is no such permanent under player's control, player=" + player.getName() + + ", cardName=" + cardName, found); - Assert.assertTrue("There is more than one such permanent under player's control, player=" + player.getName() + - ", cardName=" + cardName, count == 1); + Assert.assertTrue("There is more than one such permanent under player's control, player=" + player.getName() + + ", cardName=" + cardName, count == 1); for (Ability ability : abilities) { - Assert.assertTrue("No such ability=" + ability.toString() + ", player=" + player.getName() + - ", cardName" + cardName, found.getAbilities().contains(ability)); + Assert.assertTrue("No such ability=" + ability.toString() + ", player=" + player.getName() + + ", cardName" + cardName, found.getAbilities().contains(ability)); } } @@ -495,7 +506,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param player * @param cardName * @param ability - * @param flag true if creature should contain ability, false if it should NOT contain it instead + * @param flag true if creature should contain ability, false if it should + * NOT contain it instead * @throws AssertionError */ public void assertAbility(Player player, String cardName, Ability ability, boolean flag) throws AssertionError { @@ -508,18 +520,18 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } } - Assert.assertNotNull("There is no such permanent under player's control, player=" + player.getName() + - ", cardName=" + cardName, found); + Assert.assertNotNull("There is no such permanent under player's control, player=" + player.getName() + + ", cardName=" + cardName, found); - Assert.assertTrue("There is more than one such permanent under player's control, player=" + player.getName() + - ", cardName=" + cardName, count == 1); + Assert.assertTrue("There is more than one such permanent under player's control, player=" + player.getName() + + ", cardName=" + cardName, count == 1); if (flag) { - Assert.assertTrue("No such ability=" + ability.toString() + ", player=" + player.getName() + - ", cardName" + cardName, found.getAbilities().containsRule(ability)); + Assert.assertTrue("No such ability=" + ability.toString() + ", player=" + player.getName() + + ", cardName" + cardName, found.getAbilities().containsRule(ability)); } else { - Assert.assertFalse("Card shouldn't have such ability=" + ability.toString() + ", player=" + player.getName() + - ", cardName" + cardName, found.getAbilities().containsRule(ability)); + Assert.assertFalse("Card shouldn't have such ability=" + ability.toString() + ", player=" + player.getName() + + ", cardName" + cardName, found.getAbilities().containsRule(ability)); } } @@ -527,7 +539,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * Assert permanent count under player's control. * * @param player {@link Player} which permanents should be counted. - * @param count Expected count. + * @param count Expected count. */ @Override public void assertPermanentCount(Player player, int count) throws AssertionError { @@ -543,9 +555,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement /** * Assert permanent count under player's control. * - * @param player {@link Player} which permanents should be counted. + * @param player {@link Player} which permanents should be counted. * @param cardName Name of the cards that should be counted. - * @param count Expected count. + * @param count Expected count. */ @Override public void assertPermanentCount(Player player, String cardName, int count) throws AssertionError { @@ -580,9 +592,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement /** * Assert counter count on a permanent * - * @param cardName Name of the cards that should be counted. - * @param type Type of the counter that should be counted. - * @param count Expected count. + * @param cardName Name of the cards that should be counted. + * @param type Type of the counter that should be counted. + * @param count Expected count. */ public void assertCounterCount(String cardName, CounterType type, int count) throws AssertionError { Permanent found = null; @@ -595,35 +607,36 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement Assert.assertNotNull("There is no such permanent on the battlefield, cardName=" + cardName, found); Assert.assertEquals("(Battlefield) Counter counts are not equal (" + cardName + ":" + type + ")", count, found.getCounters().getCount(type)); } + /** - * Assert counter count on a card in exile + * Assert counter count on a card in exile * - * @param cardName Name of the cards that should be counted. - * @param type Type of the counter that should be counted. - * @param count Expected count. + * @param cardName Name of the cards that should be counted. + * @param type Type of the counter that should be counted. + * @param count Expected count. */ public void assertCounterOnExiledCardCount(String cardName, CounterType type, int count) throws AssertionError { Card found = null; if (found == null) { - for (Card card : currentGame.getExile().getAllCards(currentGame)) { - if (card.getName().equals(cardName)) { - found = card; - break; + for (Card card : currentGame.getExile().getAllCards(currentGame)) { + if (card.getName().equals(cardName)) { + found = card; + break; + } } - } - + } Assert.assertNotNull("There is no such card in the exile, cardName=" + cardName, found); Assert.assertEquals("(Exile) Counter counts are not equal (" + cardName + ":" + type + ")", count, found.getCounters(currentGame).getCount(type)); } - + /** * Assert counter count on a player * - * @param player The player whos counters should be counted. - * @param type Type of the counter that should be counted. - * @param count Expected count. + * @param player The player whos counters should be counted. + * @param type Type of the counter that should be counted. + * @param count Expected count. */ public void assertCounterCount(Player player, CounterType type, int count) throws AssertionError { Assert.assertEquals("(Battlefield) Counter counts are not equal (" + player.getName() + ":" + type + ")", count, player.getCounters().getCount(type)); @@ -632,9 +645,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement /** * Assert whether a permanent is a specified type or not * - * @param cardName Name of the permanent that should be checked. - * @param type A type to test for - * @param flag true if creature should have type, false if it should not + * @param cardName Name of the permanent that should be checked. + * @param type A type to test for + * @param flag true if creature should have type, false if it should not */ public void assertType(String cardName, CardType type, boolean flag) throws AssertionError { Permanent found = null; @@ -654,9 +667,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement /** * Assert whether a permanent is a specified type * - * @param cardName Name of the permanent that should be checked. - * @param type A type to test for - * @param subType a subtype to test for + * @param cardName Name of the permanent that should be checked. + * @param type A type to test for + * @param subType a subtype to test for */ public void assertType(String cardName, CardType type, String subType) throws AssertionError { Permanent found = null; @@ -677,14 +690,19 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement /** * Assert whether a permanent is tapped or not * - * @param cardName Name of the permanent that should be checked. - * @param tapped Whether the permanent is tapped or not + * @param cardName Name of the permanent that should be checked. + * @param tapped Whether the permanent is tapped or not */ public void assertTapped(String cardName, boolean tapped) throws AssertionError { Permanent found = null; for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents()) { if (permanent.getName().equals(cardName)) { - found = permanent; + if (found == null) { + found = permanent; + } else if (tapped != found.isTapped()) { // try to find a not correct permanent + found = permanent; + break; + } } } @@ -696,8 +714,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement /** * Assert whether a permanent is attacking or not * - * @param cardName Name of the permanent that should be checked. - * @param attacking Whether the permanent is attacking or not + * @param cardName Name of the permanent that should be checked. + * @param attacking Whether the permanent is attacking or not */ public void assertAttacking(String cardName, boolean attacking) throws AssertionError { Permanent found = null; @@ -715,8 +733,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement /** * Assert card count in player's hand. * - * @param player {@link Player} who's hand should be counted. - * @param count Expected count. + * @param player {@link Player} who's hand should be counted. + * @param count Expected count. */ public void assertHandCount(Player player, int count) throws AssertionError { int actual = currentGame.getPlayer(player.getId()).getHand().size(); @@ -726,9 +744,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement /** * Assert card count in player's hand. * - * @param player {@link Player} who's hand should be counted. - * @param cardName Name of the cards that should be counted. - * @param count Expected count. + * @param player {@link Player} who's hand should be counted. + * @param cardName Name of the cards that should be counted. + * @param count Expected count. */ public void assertHandCount(Player player, String cardName, int count) throws AssertionError { FilterCard filter = new FilterCard(); @@ -740,8 +758,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement /** * Assert card count in player's graveyard. * - * @param player {@link Player} who's graveyard should be counted. - * @param count Expected count. + * @param player {@link Player} who's graveyard should be counted. + * @param count Expected count. */ public void assertGraveyardCount(Player player, int count) throws AssertionError { int actual = currentGame.getPlayer(player.getId()).getGraveyard().size(); @@ -751,12 +769,12 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement /** * Assert card count in exile. * - * @param cardName Name of the cards that should be counted. - * @param count Expected count. + * @param cardName Name of the cards that should be counted. + * @param count Expected count. */ public void assertExileCount(String cardName, int count) throws AssertionError { int actualCount = 0; - for (ExileZone exile: currentGame.getExile().getExileZones()) { + for (ExileZone exile : currentGame.getExile().getExileZones()) { for (Card card : exile.getCards(currentGame)) { if (card.getName().equals(cardName)) { actualCount++; @@ -770,12 +788,12 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement /** * Assert card count in exile by owner. * - * @param owner player that owns the cards. - * @param count Expected count. + * @param owner player that owns the cards. + * @param count Expected count. */ public void assertExileCount(Player owner, int count) throws AssertionError { int actualCount = 0; - for (ExileZone exile: currentGame.getExile().getExileZones()) { + for (ExileZone exile : currentGame.getExile().getExileZones()) { for (Card card : exile.getCards(currentGame)) { if (card.getOwnerId().equals(owner.getId())) { actualCount++; @@ -789,9 +807,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement /** * Assert card count in player's graveyard. * - * @param player {@link Player} who's graveyard should be counted. + * @param player {@link Player} who's graveyard should be counted. * @param cardName Name of the cards that should be counted. - * @param count Expected count. + * @param count Expected count. */ public void assertGraveyardCount(Player player, String cardName, int count) throws AssertionError { int actualCount = 0; @@ -805,7 +823,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } /** - * Asserts added actions count. Usefull to make sure that all actions were executed. + * Asserts added actions count. Usefull to make sure that all actions were + * executed. + * * @param player * @param count */ @@ -850,38 +870,42 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } /** - * + * * @param turnNum * @param step * @param player * @param cardName - * @param targetName for modes you can add "mode=3" before target name, multiple targets can be seperated by ^ + * @param targetName for modes you can add "mode=3" before target name, + * multiple targets can be seperated by ^ */ public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName) { player.addAction(turnNum, step, "activate:Cast " + cardName + "$target=" + targetName); } public enum StackClause { + WHILE_ON_STACK, WHILE_NOT_ON_STACK; } /** - * Spell will only be cast, if a spell / ability with the given name is on the stack - * + * Spell will only be cast, if a spell / ability with the given name is on + * the stack + * * @param turnNum * @param step * @param player * @param cardName * @param targetName - * @param spellOnStack + * @param spellOnStack */ public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName, String spellOnStack) { castSpell(turnNum, step, player, cardName, targetName, spellOnStack, StackClause.WHILE_ON_STACK); } /** - * Spell will only be cast, if a spell / ability with the given name IS or IS NOT on the stack + * Spell will only be cast, if a spell / ability with the given name IS or + * IS NOT on the stack * * @param turnNum * @param step @@ -927,18 +951,19 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } /** - * + * * @param turnNum * @param step * @param player * @param ability - * @param targetName if not target has to be defined use the constant NO_TARGET - * @param spellOnStack + * @param targetName if not target has to be defined use the constant + * NO_TARGET + * @param spellOnStack */ public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, String targetName, String spellOnStack) { StringBuilder sb = new StringBuilder("activate:").append(ability); if (targetName != null && !targetName.isEmpty()) { - sb.append("$target=" ).append(targetName); + sb.append("$target=").append(targetName); } if (spellOnStack != null && !spellOnStack.isEmpty()) { sb.append("$spellOnStack=").append(spellOnStack); @@ -951,11 +976,11 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } public void attack(int turnNum, TestPlayer player, String attacker) { - player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, "attack:"+attacker); + player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, "attack:" + attacker); } - + public void attack(int turnNum, TestPlayer player, String attacker, TestPlayer defendingPlayer) { - player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, "attack:"+attacker+"$defendingPlayer="+defendingPlayer.getName()); + player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, "attack:" + attacker + "$defendingPlayer=" + defendingPlayer.getName()); } public void attack(int turnNum, TestPlayer player, String attacker, String planeswalker) { @@ -963,13 +988,13 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } public void block(int turnNum, TestPlayer player, String blocker, String attacker) { - player.addAction(turnNum, PhaseStep.DECLARE_BLOCKERS, "block:"+blocker+"$"+attacker); + player.addAction(turnNum, PhaseStep.DECLARE_BLOCKERS, "block:" + blocker + "$" + attacker); } /** - * For use choices set "Yes" or "No" the the choice string. - * For X values set "X=[xValue]" example: for X=3 set choice string to "X=3". - * + * For use choices set "Yes" or "No" the the choice string. For X values set + * "X=[xValue]" example: for X=3 set choice string to "X=3". + * * @param player * @param choice */ @@ -979,9 +1004,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement /** * Set the modes for modal spells - * + * * @param player - * @param choice starting with "1" for mode 1, "2" for mode 2 and so on (to set multiple modes call the command multiple times) + * @param choice starting with "1" for mode 1, "2" for mode 2 and so on (to + * set multiple modes call the command multiple times) */ public void setModeChoice(TestPlayer player, String choice) { player.addModeChoice(choice); @@ -991,39 +1017,39 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * Set target permanents * * @param player - * @param target you can add multiple targets by seperating them by the "^" character - * e.g. "creatureName1^creatureName2" - * you can qualify the target additional by setcode - * e.g. "creatureName-M15" - * you can add [no copy] to the end of the target name to prohibite targets that are copied - * you can add [only copy] to the end of the target name to allow only targets that are copies + * @param target you can add multiple targets by seperating them by the "^" + * character e.g. "creatureName1^creatureName2" you can qualify the target + * additional by setcode e.g. "creatureName-M15" you can add [no copy] to + * the end of the target name to prohibite targets that are copied you can + * add [only copy] to the end of the target name to allow only targets that + * are copies */ public void addTarget(TestPlayer player, String target) { player.addTarget(target); } - + /** * Sets a player as target - * + * * @param player - * @param targetPlayer + * @param targetPlayer */ public void addTarget(TestPlayer player, TestPlayer targetPlayer) { - player.addTarget("targetPlayer="+targetPlayer.getName()); + player.addTarget("targetPlayer=" + targetPlayer.getName()); } - + public void setDecknamePlayerA(String deckname) { deckNameA = deckname; } - + public void setDecknamePlayerB(String deckname) { deckNameB = deckname; } - + protected void skipInitShuffling() { gameOptions.skipInitShuffling = true; } - + protected ExpectedType getExpectedType(String line) { if (line.startsWith("turn:")) { return ExpectedType.TURN_NUMBER; @@ -1042,7 +1068,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } return ExpectedType.UNKNOWN; } - + protected String getStringParam(String line, int index) { String[] params = line.split(":"); if (index > params.length - 1) { @@ -1050,7 +1076,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } return params[index]; } - + protected void checkPermanentPT(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope) { if (currentGame == null) { throw new IllegalStateException("Current game is null"); @@ -1066,7 +1092,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement break; } } - } + } protected int getIntParam(String line, int index) { String[] params = line.split(":"); @@ -1074,6 +1100,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement throw new IllegalArgumentException("Not correct line: " + line); } return Integer.parseInt(params[index]); - } - + } + } diff --git a/Mage/src/mage/ObjectColor.java b/Mage/src/mage/ObjectColor.java index 812e45f187..c34ea4269d 100644 --- a/Mage/src/mage/ObjectColor.java +++ b/Mage/src/mage/ObjectColor.java @@ -1,31 +1,30 @@ /* -* 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; import java.io.Serializable; @@ -50,26 +49,27 @@ public class ObjectColor implements Serializable, Copyable, Compara private boolean red; private boolean green; - public ObjectColor() {} + public ObjectColor() { + } public ObjectColor(String color) { for (int i = 0; i < color.length(); i++) { switch (color.charAt(i)) { - case 'W': - white = true; - break; - case 'U': - blue = true; - break; - case 'B': - black = true; - break; - case 'R': - red = true; - break; - case 'G': - green = true; - break; + case 'W': + white = true; + break; + case 'U': + blue = true; + break; + case 'B': + black = true; + break; + case 'R': + red = true; + break; + case 'G': + green = true; + break; } } } @@ -157,30 +157,39 @@ public class ObjectColor implements Serializable, Copyable, Compara public boolean isWhite() { return white; } + public void setWhite(boolean white) { this.white = white; } + public boolean isBlue() { return blue; } + public void setBlue(boolean blue) { this.blue = blue; } + public boolean isBlack() { return black; } + public void setBlack(boolean black) { this.black = black; } + public boolean isRed() { return red; } + public void setRed(boolean red) { this.red = red; } + public boolean isGreen() { return green; } + public void setGreen(boolean green) { this.green = green; } @@ -287,14 +296,10 @@ public class ObjectColor implements Serializable, Copyable, Compara } public boolean shares(ObjectColor color) { - if (this == color) { - return true; - } - if (!hasColor() && !color.hasColor()) { - return true; - } - return color.white && white || color.blue && blue || color.black && black || - color.red && red || color.green && green; + // 105.4. [...] “Multicolored” is not a color. Neither is “colorless.” + return !color.isColorless() + && (color.white && white || color.blue && blue || color.black && black + || color.red && red || color.green && green); } @Override @@ -309,32 +314,32 @@ public class ObjectColor implements Serializable, Copyable, Compara if (this.isMulticolored()) { o1 = 6; - } else if(this.isColorless()) { + } else if (this.isColorless()) { o1 = 0; - } else if(this.isBlack()) { + } else if (this.isBlack()) { o1 = 1; - } else if(this.isBlue()) { + } else if (this.isBlue()) { o1 = 2; - } else if(this.isGreen()) { + } else if (this.isGreen()) { o1 = 3; - } else if(this.isRed()) { + } else if (this.isRed()) { o1 = 4; - } else if(this.isWhite()) { + } else if (this.isWhite()) { o1 = 5; } if (o.isMulticolored()) { o2 = 6; - } else if(o.isColorless()) { + } else if (o.isColorless()) { o2 = 0; - } else if(o.isBlack()) { + } else if (o.isBlack()) { o2 = 1; - } else if(o.isBlue()) { + } else if (o.isBlue()) { o2 = 2; - } else if(o.isGreen()) { + } else if (o.isGreen()) { o2 = 3; - } else if(o.isRed()) { + } else if (o.isRed()) { o2 = 4; - } else if(o.isWhite()) { + } else if (o.isWhite()) { o2 = 5; } return o1 - o2; diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 4fcd9df58f..6b8185d257 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -2459,6 +2459,22 @@ public abstract class PlayerImpl implements Player, Serializable { } } } + // check if it's possible to play the top card of a library + for (UUID playerInRangeId : game.getState().getPlayersInRange(getId(), game)) { + Player player = game.getPlayer(playerInRangeId); + if (player != null) { + if (player.isTopCardRevealed() && player.getLibrary().size() > 0) { + Card card = player.getLibrary().getFromTop(game); + if (game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, getId(), game)) { + for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.HAND)) { + if (ability instanceof SpellAbility || ability instanceof PlayLandAbility) { + playable.add(ability); + } + } + } + } + } + } // eliminate duplicate activated abilities Map playableActivated = new HashMap<>(); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) { From 9059f7a1e4fb771a5654661c8362e80450f41cc8 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 1 Jul 2015 13:40:52 +0200 Subject: [PATCH 2/4] * Fixed a bug of check if two objects sharing a colors were also to colorless objects did return a positive result (e.g. caused Dream Halls to cast Artifacts by discarding lands). --- ...ateSourceCostsTets.java => UseAlternateSourceCostsTest.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/{UseAlternateSourceCostsTets.java => UseAlternateSourceCostsTest.java} (96%) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTets.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTest.java similarity index 96% rename from Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTets.java rename to Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTest.java index c33fdc961e..125f37f8fd 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTets.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTest.java @@ -36,7 +36,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * * @author LevelX2 */ -public class UseAlternateSourceCostsTets extends CardTestPlayerBase { +public class UseAlternateSourceCostsTest extends CardTestPlayerBase { @Test public void DreamHallsCastColoredSpell() { From 30f22a092c0a4627f138c879f55842c13d21911d Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 1 Jul 2015 17:19:08 +0200 Subject: [PATCH 3/4] * Fixed a missing set of new SetTargetPointer parameter. --- Mage.Sets/src/mage/sets/timespiral/DeepSeaKraken.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/timespiral/DeepSeaKraken.java b/Mage.Sets/src/mage/sets/timespiral/DeepSeaKraken.java index 5df16776da..ac50ef010e 100644 --- a/Mage.Sets/src/mage/sets/timespiral/DeepSeaKraken.java +++ b/Mage.Sets/src/mage/sets/timespiral/DeepSeaKraken.java @@ -34,11 +34,12 @@ import mage.abilities.condition.common.SuspendedCondition; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; -import mage.abilities.keyword.SuspendAbility; import mage.abilities.keyword.CantBeBlockedSourceAbility; +import mage.abilities.keyword.SuspendAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; +import mage.constants.SetTargetPointer; import mage.constants.TargetController; import mage.constants.Zone; import mage.counters.CounterType; @@ -52,6 +53,7 @@ import mage.filter.predicate.permanent.ControllerPredicate; public class DeepSeaKraken extends CardImpl { private static final FilterSpell filter = new FilterSpell("an opponent casts"); + static { filter.add(new ControllerPredicate(TargetController.OPPONENT)); } @@ -70,7 +72,7 @@ public class DeepSeaKraken extends CardImpl { this.addAbility(new SuspendAbility(9, new ManaCostsImpl("{2}{U}"), this)); // Whenever an opponent casts a spell, if Deep-Sea Kraken is suspended, remove a time counter from it. this.addAbility(new ConditionalTriggeredAbility( - new SpellCastAllTriggeredAbility(Zone.EXILED, new RemoveCounterSourceEffect(CounterType.TIME.createInstance()), filter, false, null), SuspendedCondition.getInstance(), + new SpellCastAllTriggeredAbility(Zone.EXILED, new RemoveCounterSourceEffect(CounterType.TIME.createInstance()), filter, false, SetTargetPointer.NONE), SuspendedCondition.getInstance(), "Whenever an opponent casts a spell, if Deep-Sea Kraken is suspended, remove a time counter from it.", false)); } From 7a1a0412af0e7c41941b2356298172affe624eb4 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 1 Jul 2015 17:54:58 +0200 Subject: [PATCH 4/4] * Fixed that creatures forced to attack were wrongly also orced to attack if they had to pay a cost to attack (fixes #1036 fixes #593) . --- .../sets/bornofthegods/FloodtideSerpent.java | 28 ++-- .../championsofkamigawa/GhostlyPrison.java | 2 +- .../dragonsoftarkir/QalSismaBehemoth.java | 76 +---------- .../sets/journeyintonyx/OppressiveRays.java | 95 ++----------- .../src/mage/sets/newphyrexia/NornsAnnex.java | 65 +-------- .../src/mage/sets/ravnica/BlazingArchon.java | 39 +----- .../sets/returntoravnica/SphereOfSafety.java | 80 +++-------- .../sets/saviorsofkamigawa/Reverence.java | 48 ++----- .../mage/sets/scourge/FormOfTheDragon.java | 48 ++----- .../src/mage/sets/tempest/Propaganda.java | 2 +- .../mage/sets/tenthedition/WindbornMuse.java | 62 +-------- .../src/mage/sets/visions/ElephantGrass.java | 126 +++--------------- .../mage/sets/visions/PhyrexianMarauder.java | 69 ++++------ .../abilities/effects/ContinuousEffects.java | 3 +- .../effects/PayCostToAttackBlockEffect.java | 2 + .../PayCostToAttackBlockEffectImpl.java | 30 ++++- ...ntAttackBlockUnlessPaysAttachedEffect.java | 77 +++++++++++ ...CantAttackBlockUnlessPaysSourceEffect.java | 78 +++++++++++ .../common/combat/CantAttackYouAllEffect.java | 76 +++++++++++ .../CantAttackYouUnlessPayManaAllEffect.java | 84 ++++++++++++ .../CantAttackYouUnlessPayManaAllEffect.java | 41 ------ Mage/src/mage/game/combat/Combat.java | 1 + 22 files changed, 473 insertions(+), 659 deletions(-) create mode 100644 Mage/src/mage/abilities/effects/common/combat/CantAttackBlockUnlessPaysAttachedEffect.java create mode 100644 Mage/src/mage/abilities/effects/common/combat/CantAttackBlockUnlessPaysSourceEffect.java create mode 100644 Mage/src/mage/abilities/effects/common/combat/CantAttackYouAllEffect.java create mode 100644 Mage/src/mage/abilities/effects/common/combat/CantAttackYouUnlessPayManaAllEffect.java delete mode 100644 Mage/src/mage/abilities/effects/common/replacement/CantAttackYouUnlessPayManaAllEffect.java diff --git a/Mage.Sets/src/mage/sets/bornofthegods/FloodtideSerpent.java b/Mage.Sets/src/mage/sets/bornofthegods/FloodtideSerpent.java index 962b63f528..0fcebf1fd7 100644 --- a/Mage.Sets/src/mage/sets/bornofthegods/FloodtideSerpent.java +++ b/Mage.Sets/src/mage/sets/bornofthegods/FloodtideSerpent.java @@ -32,7 +32,9 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.ReturnToHandTargetPermanentCost; +import mage.abilities.effects.PayCostToAttackBlockEffectImpl; import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.combat.CantAttackBlockUnlessPaysSourceEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; @@ -53,6 +55,12 @@ import mage.target.common.TargetControlledPermanent; */ public class FloodtideSerpent extends CardImpl { + private static final FilterControlledPermanent filter = new FilterControlledPermanent("an enchantment you control"); + + static { + filter.add(new CardTypePredicate(CardType.ENCHANTMENT)); + } + public FloodtideSerpent(UUID ownerId) { super(ownerId, 41, "Floodtide Serpent", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{4}{U}"); this.expansionSetCode = "BNG"; @@ -62,8 +70,8 @@ public class FloodtideSerpent extends CardImpl { this.toughness = new MageInt(4); // Floodtide Serpent can't attack unless you return an enchantment you control to its owner's hand (This cost is paid as attackers are declared.) - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new FloodtideSerpentReplacementEffect())); - + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackBlockUnlessPaysSourceEffect( + new ReturnToHandTargetPermanentCost(new TargetControlledPermanent(filter)), PayCostToAttackBlockEffectImpl.RestrictType.ATTACK))); } @@ -85,24 +93,23 @@ class FloodtideSerpentReplacementEffect extends ReplacementEffectImpl { filter.add(new CardTypePredicate(CardType.ENCHANTMENT)); } - FloodtideSerpentReplacementEffect ( ) { + FloodtideSerpentReplacementEffect() { super(Duration.WhileOnBattlefield, Outcome.Neutral); staticText = "{this} can't attack unless you return an enchantment you control to its owner's hand (This cost is paid as attackers are declared.)"; } - FloodtideSerpentReplacementEffect ( FloodtideSerpentReplacementEffect effect ) { + FloodtideSerpentReplacementEffect(FloodtideSerpentReplacementEffect effect) { super(effect); } @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player player = game.getPlayer(event.getPlayerId()); - if ( player != null ) { + if (player != null) { ReturnToHandTargetPermanentCost attackCost = new ReturnToHandTargetPermanentCost(new TargetControlledPermanent(filter)); - if ( attackCost.canPay(source, source.getSourceId(), event.getPlayerId(), game) && - player.chooseUse(Outcome.Neutral, "Return an enchantment you control to hand to attack?", source, game) ) - { - if (attackCost.pay(source, game, source.getSourceId(), event.getPlayerId(), true) ) { + if (attackCost.canPay(source, source.getSourceId(), event.getPlayerId(), game) + && player.chooseUse(Outcome.Neutral, "Return an enchantment you control to hand to attack?", source, game)) { + if (attackCost.pay(source, game, source.getSourceId(), event.getPlayerId(), true)) { return false; } } @@ -111,11 +118,10 @@ class FloodtideSerpentReplacementEffect extends ReplacementEffectImpl { return false; } - @Override + @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == EventType.DECLARE_ATTACKER; } - @Override public boolean applies(GameEvent event, Ability source, Game game) { diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/GhostlyPrison.java b/Mage.Sets/src/mage/sets/championsofkamigawa/GhostlyPrison.java index 120e33b61d..fa36b4ee34 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/GhostlyPrison.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/GhostlyPrison.java @@ -32,7 +32,7 @@ import java.util.UUID; import mage.constants.*; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.replacement.CantAttackYouUnlessPayManaAllEffect; +import mage.abilities.effects.common.combat.CantAttackYouUnlessPayManaAllEffect; import mage.cards.CardImpl; /** diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/QalSismaBehemoth.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/QalSismaBehemoth.java index b32f307659..9f43bdd2a1 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/QalSismaBehemoth.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/QalSismaBehemoth.java @@ -29,21 +29,14 @@ package mage.sets.dragonsoftarkir; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.PayCostToAttackBlockEffectImpl; +import mage.abilities.effects.common.combat.CantAttackBlockUnlessPaysSourceEffect; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import static mage.game.events.GameEvent.EventType.DECLARE_ATTACKER; -import static mage.game.events.GameEvent.EventType.DECLARE_BLOCKER; -import mage.players.Player; /** * @@ -60,7 +53,7 @@ public class QalSismaBehemoth extends CardImpl { this.toughness = new MageInt(5); // Qal Sisma Behemoth can't attack or block unless you pay {2}. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new QalSismaBehemothEffect() )); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackBlockUnlessPaysSourceEffect(new ManaCostsImpl("{2}"), PayCostToAttackBlockEffectImpl.RestrictType.ATTACK_AND_BLOCK))); } @@ -73,66 +66,3 @@ public class QalSismaBehemoth extends CardImpl { return new QalSismaBehemoth(this); } } - -class QalSismaBehemothEffect extends ReplacementEffectImpl { - - private static final String effectText = "{this} can't attack or block unless you pay {2}"; - - QalSismaBehemothEffect ( ) { - super(Duration.WhileOnBattlefield, Outcome.Neutral); - staticText = effectText; - } - - QalSismaBehemothEffect ( QalSismaBehemothEffect effect ) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Player player = game.getPlayer(event.getPlayerId()); - if (player != null) { - String chooseText; - if (event.getType().equals(GameEvent.EventType.DECLARE_ATTACKER)) { - chooseText = "Pay {2} to attack?"; - } else { - chooseText = "Pay {2} to block?"; - } - ManaCostsImpl attackBlockTax = new ManaCostsImpl("{2}"); - if (attackBlockTax.canPay(source, source.getSourceId(), event.getPlayerId(), game) - && player.chooseUse(Outcome.Neutral, chooseText, source, game)) { - if (attackBlockTax.payOrRollback(source, game, source.getSourceId(), event.getPlayerId())) { - return false; - } - } - return true; - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - switch(event.getType()) { - case DECLARE_ATTACKER: - case DECLARE_BLOCKER: - return true; - default: - return false; - } - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return event.getSourceId().equals(source.getSourceId()); - } - - @Override - public QalSismaBehemothEffect copy() { - return new QalSismaBehemothEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/OppressiveRays.java b/Mage.Sets/src/mage/sets/journeyintonyx/OppressiveRays.java index 22e1057471..5b4dd45644 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/OppressiveRays.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/OppressiveRays.java @@ -33,11 +33,12 @@ import mage.abilities.ActivatedAbility; import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; -import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.CantAttackBlockUnlessPaysAttachedEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; +import mage.constants.AttachmentType; import mage.constants.CardType; import mage.constants.CostModificationType; import mage.constants.Duration; @@ -45,9 +46,7 @@ import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; import mage.util.CardUtil; @@ -63,7 +62,6 @@ public class OppressiveRays extends CardImpl { this.expansionSetCode = "JOU"; this.subtype.add("Aura"); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); @@ -72,9 +70,10 @@ public class OppressiveRays extends CardImpl { this.addAbility(ability); // Enchanted creature can't attack or block unless its controller pays 3. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new OppressiveRaysEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackBlockUnlessPaysAttachedEffect(new ManaCostsImpl<>("{3}"), AttachmentType.AURA))); + // Activated abilities of enchanted creature cost {3} more to activate. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new OppressiveRaysCostModificationEffect() )); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new OppressiveRaysCostModificationEffect())); } public OppressiveRays(final OppressiveRays card) { @@ -87,81 +86,9 @@ public class OppressiveRays extends CardImpl { } } - -class OppressiveRaysEffect extends ReplacementEffectImpl { - - private static final String effectText = "Enchanted creature can't attack or block unless its controller pays {3}"; - - OppressiveRaysEffect ( ) { - super(Duration.WhileOnBattlefield, Outcome.Neutral); - staticText = effectText; - } - - OppressiveRaysEffect ( OppressiveRaysEffect effect ) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Player player = game.getPlayer(event.getPlayerId()); - if (player != null) { - String chooseText; - if (event.getType().equals(GameEvent.EventType.DECLARE_ATTACKER)) { - chooseText = "Pay {3} to attack?"; - } else { - chooseText = "Pay {3} to block?"; - } - ManaCostsImpl attackBlockTax = new ManaCostsImpl("{3}"); - if (attackBlockTax.canPay(source, source.getSourceId(), event.getPlayerId(), game) - && player.chooseUse(Outcome.Neutral, chooseText, source, game)) { - if (attackBlockTax.payOrRollback(source, game, source.getSourceId(), event.getPlayerId())) { - return false; - } - } - return true; - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - switch(event.getType()) { - case DECLARE_ATTACKER: - case DECLARE_BLOCKER: - return true; - default: - return false; - } - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType().equals(GameEvent.EventType.DECLARE_ATTACKER)) { - Permanent attacker = game.getPermanent(event.getSourceId()); - return attacker != null && attacker.getAttachments().contains(source.getSourceId()); - } - if (event.getType().equals(GameEvent.EventType.DECLARE_BLOCKER)) { - Permanent blocker = game.getPermanent(event.getSourceId()); - return blocker != null && blocker.getAttachments().contains(source.getSourceId()); - } - return false; - } - - @Override - public OppressiveRaysEffect copy() { - return new OppressiveRaysEffect(this); - } - -} - class OppressiveRaysCostModificationEffect extends CostModificationEffectImpl { - OppressiveRaysCostModificationEffect ( ) { + OppressiveRaysCostModificationEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST); staticText = "Activated abilities of enchanted creature cost {3} more to activate"; } @@ -180,10 +107,10 @@ class OppressiveRaysCostModificationEffect extends CostModificationEffectImpl { public boolean applies(Ability abilityToModify, Ability source, Game game) { Permanent creature = game.getPermanent(abilityToModify.getSourceId()); if (creature != null && creature.getAttachments().contains(source.getSourceId())) { - if (abilityToModify instanceof ActivatedAbility - && !(abilityToModify instanceof SpellAbility)) { - return true; - } + if (abilityToModify instanceof ActivatedAbility + && !(abilityToModify instanceof SpellAbility)) { + return true; + } } return false; } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/NornsAnnex.java b/Mage.Sets/src/mage/sets/newphyrexia/NornsAnnex.java index 6c5c07386a..456f4df8a3 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/NornsAnnex.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/NornsAnnex.java @@ -28,20 +28,13 @@ package mage.sets.newphyrexia; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.combat.CantAttackYouUnlessPayManaAllEffect; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; /** * @author Loki @@ -53,8 +46,8 @@ public class NornsAnnex extends CardImpl { this.expansionSetCode = "NPH"; // {WP} ({WP} can be paid with either or 2 life.) - // Creatures can't attack you or a planeswalker you control unless their controller pays for each of those creatures. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new NornsAnnexReplacementEffect())); + // Creatures can't attack you or a planeswalker you control unless their controller pays {WP} for each of those creatures. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackYouUnlessPayManaAllEffect(new ManaCostsImpl<>("{WP}"), true))); } public NornsAnnex(final NornsAnnex card) { @@ -67,55 +60,3 @@ public class NornsAnnex extends CardImpl { } } - -class NornsAnnexReplacementEffect extends ReplacementEffectImpl { - - private static final String effectText = "Creatures can't attack you or a planeswalker you control unless their controller pays {WP} for each creature he or she controls that's attacking you"; - - NornsAnnexReplacementEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = effectText; - } - - NornsAnnexReplacementEffect(NornsAnnexReplacementEffect effect) { - super(effect); - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARE_ATTACKER; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getTargetId().equals(source.getControllerId())) { - return true; - } - // planeswalker - Permanent permanent = game.getPermanent(event.getTargetId()); - return permanent != null && permanent.getControllerId().equals(source.getControllerId()) - && permanent.getCardType().contains(CardType.PLANESWALKER); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Player player = game.getPlayer(event.getPlayerId()); - if (player != null) { - ManaCostsImpl propagandaTax = new ManaCostsImpl("{WP}"); - if (propagandaTax.canPay(source, source.getSourceId(), event.getPlayerId(), game) - && player.chooseUse(Outcome.Benefit, "Pay to declare attacker?", source, game)) { - if (propagandaTax.payOrRollback(source, game, source.getSourceId(), event.getPlayerId())) { - return false; - } - } - return true; - } - return false; - } - - @Override - public NornsAnnexReplacementEffect copy() { - return new NornsAnnexReplacementEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/sets/ravnica/BlazingArchon.java b/Mage.Sets/src/mage/sets/ravnica/BlazingArchon.java index 27a153c098..41fd4e3f54 100644 --- a/Mage.Sets/src/mage/sets/ravnica/BlazingArchon.java +++ b/Mage.Sets/src/mage/sets/ravnica/BlazingArchon.java @@ -29,18 +29,14 @@ package mage.sets.ravnica; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.RestrictionEffect; +import mage.abilities.effects.common.combat.CantAttackYouAllEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; /** * @@ -57,10 +53,10 @@ public class BlazingArchon extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - + // Creatures can't attack you. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BlazingArchonRestrictionEffect())); - + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackYouAllEffect(Duration.WhileOnBattlefield))); + } public BlazingArchon(final BlazingArchon card) { @@ -72,30 +68,3 @@ public class BlazingArchon extends CardImpl { return new BlazingArchon(this); } } - -class BlazingArchonRestrictionEffect extends RestrictionEffect { - - BlazingArchonRestrictionEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "Creatures can't attack you"; - } - - BlazingArchonRestrictionEffect(final BlazingArchonRestrictionEffect effect) { - super(effect); - } - - @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - return true; - } - - @Override - public boolean canAttack(UUID defenderId, Ability source, Game game) { - return !defenderId.equals(source.getControllerId()); - } - - @Override - public BlazingArchonRestrictionEffect copy() { - return new BlazingArchonRestrictionEffect(this); - } -} diff --git a/Mage.Sets/src/mage/sets/returntoravnica/SphereOfSafety.java b/Mage.Sets/src/mage/sets/returntoravnica/SphereOfSafety.java index fbccc1f078..d3e15931f0 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/SphereOfSafety.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/SphereOfSafety.java @@ -25,24 +25,21 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.returntoravnica; import java.util.UUID; - -import mage.constants.*; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.combat.CantAttackYouUnlessPayManaAllEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.filter.common.FilterEnchantmentPermanent; -import mage.filter.predicate.permanent.ControllerPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; /** * @@ -50,16 +47,16 @@ import mage.players.Player; */ public class SphereOfSafety extends CardImpl { - public SphereOfSafety (UUID ownerId) { + public SphereOfSafety(UUID ownerId) { super(ownerId, 24, "Sphere of Safety", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{4}{W}"); this.expansionSetCode = "RTR"; // Creatures can't attack you or a planeswalker you control unless their controller pays {X} for each of those creatures, where X is the number of enchantments you control. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SphereOfSafetyReplacementEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SphereOfSafetyPayManaToAttackAllEffect())); } - public SphereOfSafety (final SphereOfSafety card) { + public SphereOfSafety(final SphereOfSafety card) { super(card); } @@ -70,64 +67,29 @@ public class SphereOfSafety extends CardImpl { } -class SphereOfSafetyReplacementEffect extends ReplacementEffectImpl { +class SphereOfSafetyPayManaToAttackAllEffect extends CantAttackYouUnlessPayManaAllEffect { - private static final String effectText = "Creatures can't attack you or a planeswalker you control unless their controller pays {X} for each of those creatures, where X is the number of enchantments you control"; - private static final FilterEnchantmentPermanent filter = new FilterEnchantmentPermanent("enchantment you control"); - static { - filter.add(new ControllerPredicate(TargetController.YOU)); - } - private final PermanentsOnBattlefieldCount countEnchantments = new PermanentsOnBattlefieldCount(filter); - - - SphereOfSafetyReplacementEffect ( ) { - super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = effectText; + SphereOfSafetyPayManaToAttackAllEffect() { + super(null, true); + staticText = "Creatures can't attack you or a planeswalker you control unless their controller pays {X} for each of those creatures, where X is the number of enchantments you control."; } - SphereOfSafetyReplacementEffect ( SphereOfSafetyReplacementEffect effect ) { + SphereOfSafetyPayManaToAttackAllEffect(SphereOfSafetyPayManaToAttackAllEffect effect) { super(effect); } @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARE_ATTACKER; + public ManaCosts getManaCostToPay(GameEvent event, Ability source, Game game) { + int enchantments = game.getBattlefield().countAll(new FilterEnchantmentPermanent(), source.getControllerId(), game); + if (enchantments > 0) { + return new ManaCostsImpl<>("{" + enchantments + "}"); + } + return null; } @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getTargetId().equals(source.getControllerId()) ) { - return true; - } - // planeswalker - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.getControllerId().equals(source.getControllerId()) - && permanent.getCardType().contains(CardType.PLANESWALKER)) { - return true; - } - return false; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Player player = game.getPlayer(event.getPlayerId()); - if ( player != null ) { - int ce = countEnchantments.calculate(game, source, this); - ManaCostsImpl safetyCosts = new ManaCostsImpl("{"+ ce +"}"); - if ( safetyCosts.canPay(source, source.getSourceId(), event.getPlayerId(), game) && - player.chooseUse(Outcome.Benefit, "Pay {"+ ce +"} to declare attacker?", source, game) ) { - if (safetyCosts.payOrRollback(source, game, this.getId(), event.getPlayerId())) { - return false; - } - } - return true; - } - return false; - } - - @Override - public SphereOfSafetyReplacementEffect copy() { - return new SphereOfSafetyReplacementEffect(this); + public SphereOfSafetyPayManaToAttackAllEffect copy() { + return new SphereOfSafetyPayManaToAttackAllEffect(this); } } diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/Reverence.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/Reverence.java index dae5b5ee48..0257d9036a 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/Reverence.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/Reverence.java @@ -28,20 +28,16 @@ package mage.sets.saviorsofkamigawa; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.RestrictionEffect; +import mage.abilities.effects.common.combat.CantAttackYouAllEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.Filter; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; /** * @@ -49,12 +45,18 @@ import mage.game.permanent.Permanent; */ public class Reverence extends CardImpl { + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Creatures with power 2 or less"); + + static { + filter.add(new PowerPredicate(Filter.ComparisonType.LessThan, 3)); + } + public Reverence(UUID ownerId) { super(ownerId, 26, "Reverence", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}"); this.expansionSetCode = "SOK"; // Creatures with power 2 or less can't attack you. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ReverenceRestrictionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackYouAllEffect(Duration.WhileOnBattlefield, filter))); } public Reverence(final Reverence card) { @@ -66,37 +68,3 @@ public class Reverence extends CardImpl { return new Reverence(this); } } - -class ReverenceRestrictionEffect extends RestrictionEffect { - - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Creatures with power 2 or less"); - - static { - filter.add(new PowerPredicate(Filter.ComparisonType.LessThan, 3)); - } - - ReverenceRestrictionEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "Creatures with power 2 or less can't attack you"; - } - - ReverenceRestrictionEffect(final ReverenceRestrictionEffect effect) { - super(effect); - } - - @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - return filter.match(permanent, source.getSourceId(), source.getControllerId(), game); - } - - @Override - public boolean canAttack(UUID defenderId, Ability source, Game game) { - return !defenderId.equals(source.getControllerId()); - } - - @Override - public ReverenceRestrictionEffect copy() { - return new ReverenceRestrictionEffect(this); - } -} diff --git a/Mage.Sets/src/mage/sets/scourge/FormOfTheDragon.java b/Mage.Sets/src/mage/sets/scourge/FormOfTheDragon.java index 255e17ceaf..ac3b132005 100644 --- a/Mage.Sets/src/mage/sets/scourge/FormOfTheDragon.java +++ b/Mage.Sets/src/mage/sets/scourge/FormOfTheDragon.java @@ -33,8 +33,8 @@ import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.combat.CantAttackYouAllEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.constants.CardType; @@ -47,7 +47,6 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AbilityPredicate; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreatureOrPlayer; @@ -56,8 +55,9 @@ import mage.target.common.TargetCreatureOrPlayer; * @author emerald000 */ public class FormOfTheDragon extends CardImpl { - + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Creatures without flying"); + static { filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); } @@ -66,17 +66,16 @@ public class FormOfTheDragon extends CardImpl { super(ownerId, 93, "Form of the Dragon", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{4}{R}{R}{R}"); this.expansionSetCode = "SCG"; - // At the beginning of your upkeep, Form of the Dragon deals 5 damage to target creature or player. Ability ability = new BeginningOfUpkeepTriggeredAbility(new DamageTargetEffect(5), TargetController.YOU, false); ability.addTarget(new TargetCreatureOrPlayer()); this.addAbility(ability); - + // At the beginning of each end step, your life total becomes 5. this.addAbility(new BeginningOfEndStepTriggeredAbility(new FormOfTheDragonEffect(), TargetController.ANY, false)); - + // Creatures without flying can't attack you. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new FormOfTheDragonRestrictionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackYouAllEffect(Duration.WhileOnBattlefield, filter))); } public FormOfTheDragon(final FormOfTheDragon card) { @@ -90,21 +89,21 @@ public class FormOfTheDragon extends CardImpl { } class FormOfTheDragonEffect extends OneShotEffect { - + FormOfTheDragonEffect() { super(Outcome.Neutral); this.staticText = "your life total becomes 5"; } - + FormOfTheDragonEffect(final FormOfTheDragonEffect effect) { super(effect); } - + @Override public FormOfTheDragonEffect copy() { return new FormOfTheDragonEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); @@ -115,30 +114,3 @@ class FormOfTheDragonEffect extends OneShotEffect { return false; } } - -class FormOfTheDragonRestrictionEffect extends RestrictionEffect { - - FormOfTheDragonRestrictionEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "Creatures without flying can't attack you"; - } - - FormOfTheDragonRestrictionEffect(final FormOfTheDragonRestrictionEffect effect) { - super(effect); - } - - @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - return permanent.getCardType().contains(CardType.CREATURE) && !permanent.getAbilities().contains(FlyingAbility.getInstance()); - } - - @Override - public boolean canAttack(UUID defenderId, Ability source, Game game) { - return !defenderId.equals(source.getControllerId()); - } - - @Override - public FormOfTheDragonRestrictionEffect copy() { - return new FormOfTheDragonRestrictionEffect(this); - } -} diff --git a/Mage.Sets/src/mage/sets/tempest/Propaganda.java b/Mage.Sets/src/mage/sets/tempest/Propaganda.java index 9b5f60aa26..4939421251 100644 --- a/Mage.Sets/src/mage/sets/tempest/Propaganda.java +++ b/Mage.Sets/src/mage/sets/tempest/Propaganda.java @@ -30,7 +30,7 @@ package mage.sets.tempest; import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.replacement.CantAttackYouUnlessPayManaAllEffect; +import mage.abilities.effects.common.combat.CantAttackYouUnlessPayManaAllEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; diff --git a/Mage.Sets/src/mage/sets/tenthedition/WindbornMuse.java b/Mage.Sets/src/mage/sets/tenthedition/WindbornMuse.java index b16714da35..cd938527a2 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/WindbornMuse.java +++ b/Mage.Sets/src/mage/sets/tenthedition/WindbornMuse.java @@ -29,21 +29,14 @@ package mage.sets.tenthedition; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.ReplacementEffectImpl; -import mage.abilities.effects.common.replacement.CantAttackYouUnlessPayManaAllEffect; +import mage.abilities.effects.common.combat.CantAttackYouUnlessPayManaAllEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.players.Player; /** * @@ -75,56 +68,3 @@ public class WindbornMuse extends CardImpl { return new WindbornMuse(this); } } - -class WindbornMuseReplacementEffect extends ReplacementEffectImpl { - - private static final String effectText = "Creatures can't attack you unless their controller pays {2} for each creature he or she controls that's attacking you"; - - WindbornMuseReplacementEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = effectText; - } - - WindbornMuseReplacementEffect(WindbornMuseReplacementEffect effect) { - super(effect); - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARE_ATTACKER; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getTargetId().equals(source.getControllerId())) { - Player attackedPlayer = game.getPlayer(event.getTargetId()); - if (attackedPlayer != null) { - // only if a player is attacked. Attacking a planeswalker is free - return true; - } - } - return false; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Player player = game.getPlayer(event.getPlayerId()); - if (player != null && event.getTargetId().equals(source.getControllerId())) { - ManaCostsImpl attackTax = new ManaCostsImpl("{2}"); - if (attackTax.canPay(source, source.getSourceId(), event.getPlayerId(), game) - && player.chooseUse(Outcome.Benefit, "Pay {2} to attack player?", source, game)) { - if (attackTax.payOrRollback(source, game, this.getId(), event.getPlayerId())) { - return false; - } - } - return true; - } - return false; - } - - @Override - public WindbornMuseReplacementEffect copy() { - return new WindbornMuseReplacementEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/sets/visions/ElephantGrass.java b/Mage.Sets/src/mage/sets/visions/ElephantGrass.java index 2c10185b73..46bbb1c09e 100644 --- a/Mage.Sets/src/mage/sets/visions/ElephantGrass.java +++ b/Mage.Sets/src/mage/sets/visions/ElephantGrass.java @@ -28,18 +28,20 @@ package mage.sets.visions; import java.util.UUID; - -import mage.constants.*; -import mage.abilities.Ability; +import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.combat.CantAttackYouAllEffect; +import mage.abilities.effects.common.combat.CantAttackYouUnlessPayManaAllEffect; import mage.abilities.keyword.CumulativeUpkeepAbility; import mage.cards.CardImpl; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; /** * @@ -47,16 +49,26 @@ import mage.players.Player; */ public class ElephantGrass extends CardImpl { + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Nonblack creatures"); + private static final FilterCreaturePermanent filterBlack = new FilterCreaturePermanent("Black creatures"); + + static { + filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); + filterBlack.add(new ColorPredicate(ObjectColor.BLACK)); + } + public ElephantGrass(UUID ownerId) { super(ownerId, 54, "Elephant Grass", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{G}"); this.expansionSetCode = "VIS"; // Cumulative upkeep {1} this.addAbility(new CumulativeUpkeepAbility(new ManaCostsImpl("{1}"))); + // Black creatures can't attack you. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ElephantGrassReplacementEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackYouAllEffect(Duration.WhileOnBattlefield, filterBlack))); + // Nonblack creatures can't attack you unless their controller pays {2} for each creature he or she controls that's attacking you. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ElephantGrassReplacementEffect2())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackYouUnlessPayManaAllEffect(new ManaCostsImpl<>("{2"), false, filter))); } public ElephantGrass(final ElephantGrass card) { @@ -68,97 +80,3 @@ public class ElephantGrass extends CardImpl { return new ElephantGrass(this); } } - - -class ElephantGrassReplacementEffect extends ReplacementEffectImpl { - - ElephantGrassReplacementEffect ( ) { - super(Duration.WhileOnBattlefield, Outcome.Neutral); - staticText = "Black creatures can't attack you"; - } - - ElephantGrassReplacementEffect ( ElephantGrassReplacementEffect effect ) { - super(effect); - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARE_ATTACKER; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getTargetId().equals(source.getControllerId()) ) { - Permanent creature = game.getPermanent(event.getSourceId()); - if(creature != null && creature.getColor(game).isBlack()){ - return true; - } - } - return false; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return true; - } - - @Override - public ElephantGrassReplacementEffect copy() { - return new ElephantGrassReplacementEffect(this); - } - -} - -class ElephantGrassReplacementEffect2 extends ReplacementEffectImpl { - - ElephantGrassReplacementEffect2 ( ) { - super(Duration.WhileOnBattlefield, Outcome.Neutral); - staticText = "Nonblack creatures can't attack you unless their controller pays {2} for each creature he or she controls that's attacking you"; - } - - ElephantGrassReplacementEffect2 ( ElephantGrassReplacementEffect2 effect ) { - super(effect); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Player player = game.getPlayer(event.getPlayerId()); - if ( player != null && event.getTargetId().equals(source.getControllerId())) { - ManaCostsImpl attackCost = new ManaCostsImpl("{2}"); - if ( attackCost.canPay(source, source.getSourceId(), event.getPlayerId(), game) && - player.chooseUse(Outcome.Benefit, "Pay {2} to attack player?", source, game) ) { - if (attackCost.payOrRollback(source, game, this.getId(), event.getPlayerId())) { - return false; - } - } - return true; - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARE_ATTACKER; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getTargetId().equals(source.getControllerId()) ) { - Permanent creature = game.getPermanent(event.getSourceId()); - if (creature != null && !creature.getColor(game).isBlack()) { - Player attackedPlayer = game.getPlayer(event.getTargetId()); - if (attackedPlayer != null) { - // only if a player is attacked. Attacking a planeswalker is free - return true; - } - } - } - return false; - } - - @Override - public ElephantGrassReplacementEffect2 copy() { - return new ElephantGrassReplacementEffect2(this); - } - -} diff --git a/Mage.Sets/src/mage/sets/visions/PhyrexianMarauder.java b/Mage.Sets/src/mage/sets/visions/PhyrexianMarauder.java index 6d13d24901..3079b7ca4d 100644 --- a/Mage.Sets/src/mage/sets/visions/PhyrexianMarauder.java +++ b/Mage.Sets/src/mage/sets/visions/PhyrexianMarauder.java @@ -34,23 +34,20 @@ import mage.abilities.SpellAbility; import mage.abilities.common.CantBlockAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.EntersBattlefieldEffect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.combat.CantAttackBlockUnlessPaysSourceEffect; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; -import mage.players.Player; /** * @@ -67,12 +64,12 @@ public class PhyrexianMarauder extends CardImpl { // Phyrexian Marauder enters the battlefield with X +1/+1 counters on it. this.addAbility(new EntersBattlefieldAbility(new PhyrexianMarauderEntersEffect(), "with X +1/+1 counters on it")); - + // Phyrexian Marauder can't block. this.addAbility(new CantBlockAbility()); - + // Phyrexian Marauder can't attack unless you pay {1} for each +1/+1 counter on it. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PhyrexianMarauderPayEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PhyrexianMarauderCantAttackUnlessYouPayEffect())); } public PhyrexianMarauder(final PhyrexianMarauder card) { @@ -116,53 +113,37 @@ class PhyrexianMarauderEntersEffect extends OneShotEffect { } } -class PhyrexianMarauderPayEffect extends ReplacementEffectImpl { +class PhyrexianMarauderCantAttackUnlessYouPayEffect extends CantAttackBlockUnlessPaysSourceEffect { - PhyrexianMarauderPayEffect() { - super(Duration.WhileOnBattlefield, Outcome.Neutral); + PhyrexianMarauderCantAttackUnlessYouPayEffect() { + super(new ManaCostsImpl("{0}"), RestrictType.ATTACK); staticText = "{this} can't attack unless you pay {1} for each +1/+1 counter on it"; } - PhyrexianMarauderPayEffect(PhyrexianMarauderPayEffect effect) { + PhyrexianMarauderCantAttackUnlessYouPayEffect(PhyrexianMarauderCantAttackUnlessYouPayEffect effect) { super(effect); } - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Player player = game.getPlayer(event.getPlayerId()); - Permanent permanent = game.getPermanent(source.getSourceId()); - if (player != null && permanent != null) { - int xValue = permanent.getCounters().getCount(CounterType.P1P1); - String chooseText; - if (event.getType().equals(GameEvent.EventType.DECLARE_ATTACKER)) { - chooseText = "Pay {" + xValue + "} to attack?"; - } else { - chooseText = "Pay {" + xValue + "} to block?"; - } - ManaCostsImpl attackTax = new ManaCostsImpl<>("{" + xValue + "}"); - if (attackTax.canPay(source, source.getSourceId(), event.getPlayerId(), game) - && player.chooseUse(Outcome.Neutral, chooseText, source, game)) { - if (attackTax.payOrRollback(source, game, source.getSourceId(), event.getPlayerId())) { - return false; - } - } - return true; - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == EventType.DECLARE_ATTACKER; - } - @Override public boolean applies(GameEvent event, Ability source, Game game) { - return event.getSourceId().equals(source.getSourceId()); + return source.getSourceId().equals(event.getSourceId()); } @Override - public PhyrexianMarauderPayEffect copy() { - return new PhyrexianMarauderPayEffect(this); + public ManaCosts getManaCostToPay(GameEvent event, Ability source, Game game) { + Permanent sourceObject = game.getPermanent(source.getSourceId()); + if (sourceObject != null) { + int counter = sourceObject.getCounters().getCount(CounterType.P1P1); + if (counter > 0) { + return new ManaCostsImpl<>("{" + counter + "}"); + } + } + return null; } + + @Override + public PhyrexianMarauderCantAttackUnlessYouPayEffect copy() { + return new PhyrexianMarauderCantAttackUnlessYouPayEffect(this); + } + } diff --git a/Mage/src/mage/abilities/effects/ContinuousEffects.java b/Mage/src/mage/abilities/effects/ContinuousEffects.java index 12ae10a9fc..9f8f39211e 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffects.java @@ -336,7 +336,8 @@ public class ContinuousEffects implements Serializable { if (ability.getAbilityType() != AbilityType.STATIC || ability.isInUseableZone(game, null, event)) { if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) { if (!game.getScopeRelevant() || effect.hasSelfScope() || !event.getTargetId().equals(ability.getSourceId())) { - if (effect.applies(event, ability, game)) { + if (effect.applies(event, ability, game) + && !((PayCostToAttackBlockEffect) effect).isCostless(event, ability, game)) { return true; } } diff --git a/Mage/src/mage/abilities/effects/PayCostToAttackBlockEffect.java b/Mage/src/mage/abilities/effects/PayCostToAttackBlockEffect.java index f73af6074f..d9b9b8a150 100644 --- a/Mage/src/mage/abilities/effects/PayCostToAttackBlockEffect.java +++ b/Mage/src/mage/abilities/effects/PayCostToAttackBlockEffect.java @@ -42,4 +42,6 @@ public interface PayCostToAttackBlockEffect extends ReplacementEffect { ManaCosts getManaCostToPay(GameEvent event, Ability source, Game game); Cost getOtherCostToPay(GameEvent event, Ability source, Game game); + + boolean isCostless(GameEvent event, Ability source, Game game); } diff --git a/Mage/src/mage/abilities/effects/PayCostToAttackBlockEffectImpl.java b/Mage/src/mage/abilities/effects/PayCostToAttackBlockEffectImpl.java index 9473fbcd9c..d31b8e71cf 100644 --- a/Mage/src/mage/abilities/effects/PayCostToAttackBlockEffectImpl.java +++ b/Mage/src/mage/abilities/effects/PayCostToAttackBlockEffectImpl.java @@ -46,13 +46,26 @@ public abstract class PayCostToAttackBlockEffectImpl extends ReplacementEffectIm public static enum RestrictType { - ATTACK, ATTACK_AND_BLOCK, BLOCK + ATTACK("attack"), + ATTACK_AND_BLOCK("attack or block"), + BLOCK("block"); + + private final String text; + + RestrictType(String text) { + this.text = text; + } + + @Override + public String toString() { + return text; + } } - private final Cost cost; - private final ManaCosts manaCosts; + protected final Cost cost; + protected final ManaCosts manaCosts; - private final RestrictType restrictType; + protected final RestrictType restrictType; public PayCostToAttackBlockEffectImpl(Duration duration, Outcome outcome, RestrictType restrictType) { super(duration, outcome, false); @@ -165,4 +178,13 @@ public abstract class PayCostToAttackBlockEffectImpl extends ReplacementEffectIm return manaCosts; } + @Override + public boolean isCostless(GameEvent event, Ability source, Game game) { + ManaCosts currentManaCosts = getManaCostToPay(event, source, game); + if (currentManaCosts != null && currentManaCosts.convertedManaCost() > 0) { + return false; + } + return getOtherCostToPay(event, source, game) == null; + } + } diff --git a/Mage/src/mage/abilities/effects/common/combat/CantAttackBlockUnlessPaysAttachedEffect.java b/Mage/src/mage/abilities/effects/common/combat/CantAttackBlockUnlessPaysAttachedEffect.java new file mode 100644 index 0000000000..3a0a9b6f10 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/combat/CantAttackBlockUnlessPaysAttachedEffect.java @@ -0,0 +1,77 @@ +/* + * 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.combat; + +import mage.abilities.Ability; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.effects.PayCostToAttackBlockEffectImpl; +import mage.abilities.effects.PayCostToAttackBlockEffectImpl.RestrictType; +import mage.constants.AttachmentType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; + +/** + * + * @author LevelX2 + */ +public class CantAttackBlockUnlessPaysAttachedEffect extends PayCostToAttackBlockEffectImpl { + + public CantAttackBlockUnlessPaysAttachedEffect(ManaCosts manaCosts, AttachmentType attachmentType) { + super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK_AND_BLOCK, manaCosts); + staticText = attachmentType.equals(AttachmentType.AURA) ? "Enchanted " : "Equipped " + + "creature can't attack or block unless its controller pays " + + manaCosts == null ? "" : manaCosts.getText(); + } + + public CantAttackBlockUnlessPaysAttachedEffect(CantAttackBlockUnlessPaysAttachedEffect effect) { + super(effect); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent enchantment = game.getPermanent(source.getSourceId()); + if (enchantment != null && enchantment.getAttachedTo() != null) { + if (event.getType().equals(EventType.DECLARE_ATTACKER)) { + return event.getSourceId().equals(enchantment.getAttachedTo()); + } + if (event.getType().equals(EventType.DECLARE_BLOCKER)) { + return event.getSourceId().equals(enchantment.getAttachedTo()); + } + } + return false; + } + + @Override + public CantAttackBlockUnlessPaysAttachedEffect copy() { + return new CantAttackBlockUnlessPaysAttachedEffect(this); + } +} diff --git a/Mage/src/mage/abilities/effects/common/combat/CantAttackBlockUnlessPaysSourceEffect.java b/Mage/src/mage/abilities/effects/common/combat/CantAttackBlockUnlessPaysSourceEffect.java new file mode 100644 index 0000000000..b20b50b857 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/combat/CantAttackBlockUnlessPaysSourceEffect.java @@ -0,0 +1,78 @@ +/* + * 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.combat; + +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.effects.PayCostToAttackBlockEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; + +/** + * + * @author LevelX2 + */ +public class CantAttackBlockUnlessPaysSourceEffect extends PayCostToAttackBlockEffectImpl { + + public CantAttackBlockUnlessPaysSourceEffect(Cost cost, RestrictType restrictType) { + super(Duration.WhileOnBattlefield, Outcome.Detriment, restrictType, cost); + staticText = "{this} can't " + restrictType.toString() + " unless you " + + cost == null ? "" : cost.getText() + + (restrictType.equals(RestrictType.ATTACK) ? " (This cost is paid as attackers are declared.)" : ""); + } + + public CantAttackBlockUnlessPaysSourceEffect(ManaCosts manaCosts, RestrictType restrictType) { + super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK_AND_BLOCK, manaCosts); + staticText = "{this} can't " + restrictType.toString() + " unless you pay " + + manaCosts == null ? "" : manaCosts.getText(); + } + + public CantAttackBlockUnlessPaysSourceEffect(CantAttackBlockUnlessPaysSourceEffect effect) { + super(effect); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (!restrictType.equals(RestrictType.BLOCK) && event.getType().equals(EventType.DECLARE_ATTACKER)) { + return event.getSourceId().equals(source.getSourceId()); + } + if (!restrictType.equals(RestrictType.ATTACK) && event.getType().equals(EventType.DECLARE_BLOCKER)) { + return event.getSourceId().equals(source.getSourceId()); + } + return false; + } + + @Override + public CantAttackBlockUnlessPaysSourceEffect copy() { + return new CantAttackBlockUnlessPaysSourceEffect(this); + } +} diff --git a/Mage/src/mage/abilities/effects/common/combat/CantAttackYouAllEffect.java b/Mage/src/mage/abilities/effects/common/combat/CantAttackYouAllEffect.java new file mode 100644 index 0000000000..519ad93ff7 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/combat/CantAttackYouAllEffect.java @@ -0,0 +1,76 @@ +/* + * 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.combat; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.RestrictionEffect; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author LevelX2 + */ +public class CantAttackYouAllEffect extends RestrictionEffect { + + private final FilterCreaturePermanent filterAttacker; + + public CantAttackYouAllEffect(Duration duration) { + this(duration, new FilterCreaturePermanent()); + } + + public CantAttackYouAllEffect(Duration duration, FilterCreaturePermanent filter) { + super(duration, Outcome.Benefit); + this.filterAttacker = filter; + staticText = "Creatures can't attack you"; + } + + CantAttackYouAllEffect(final CantAttackYouAllEffect effect) { + super(effect); + this.filterAttacker = effect.filterAttacker; + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return filterAttacker.match(permanent, source.getSourceId(), source.getControllerId(), game); + } + + @Override + public boolean canAttack(UUID defenderId, Ability source, Game game) { + return !defenderId.equals(source.getControllerId()); + } + + @Override + public CantAttackYouAllEffect copy() { + return new CantAttackYouAllEffect(this); + } +} diff --git a/Mage/src/mage/abilities/effects/common/combat/CantAttackYouUnlessPayManaAllEffect.java b/Mage/src/mage/abilities/effects/common/combat/CantAttackYouUnlessPayManaAllEffect.java new file mode 100644 index 0000000000..1846384395 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/combat/CantAttackYouUnlessPayManaAllEffect.java @@ -0,0 +1,84 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common.combat; + +import mage.abilities.Ability; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.effects.PayCostToAttackBlockEffectImpl; +import mage.abilities.effects.PayCostToAttackBlockEffectImpl.RestrictType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +/** + * + * @author LevelX2 + */ +public class CantAttackYouUnlessPayManaAllEffect extends PayCostToAttackBlockEffectImpl { + + private final FilterCreaturePermanent filterCreaturePermanent; + private final boolean payAlsoForAttackingPlaneswalker; + + public CantAttackYouUnlessPayManaAllEffect(ManaCosts manaCosts) { + this(manaCosts, false); + } + + public CantAttackYouUnlessPayManaAllEffect(ManaCosts manaCosts, boolean payAlsoForAttackingPlaneswalker) { + this(manaCosts, payAlsoForAttackingPlaneswalker, null); + } + + public CantAttackYouUnlessPayManaAllEffect(ManaCosts manaCosts, boolean payAlsoForAttackingPlaneswalker, FilterCreaturePermanent filter) { + super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK, manaCosts); + this.payAlsoForAttackingPlaneswalker = payAlsoForAttackingPlaneswalker; + this.filterCreaturePermanent = filter; + staticText = (filterCreaturePermanent == null ? "Creatures" : filterCreaturePermanent.getMessage()) + + " can't attack you " + + (payAlsoForAttackingPlaneswalker ? "or a planeswalker you control " : "") + + "unless their controller pays " + + (manaCosts == null ? "" : manaCosts.getText()) + + " for each creature he or she controls that's attacking you"; + } + + public CantAttackYouUnlessPayManaAllEffect(CantAttackYouUnlessPayManaAllEffect effect) { + super(effect); + this.payAlsoForAttackingPlaneswalker = effect.payAlsoForAttackingPlaneswalker; + this.filterCreaturePermanent = effect.filterCreaturePermanent; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + // check if attacking creature fullfills filter criteria + if (filterCreaturePermanent != null) { + Permanent permanent = game.getPermanent(event.getSourceId()); + if (!filterCreaturePermanent.match(permanent, source.getSourceId(), source.getControllerId(), game)) { + return false; + } + } + // attack target is controlling player + if (source.getControllerId().equals(event.getTargetId())) { + return true; + } + // or attack target is a planeswalker of the controlling player + if (payAlsoForAttackingPlaneswalker) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null + && permanent.getCardType().contains(CardType.PLANESWALKER) + && permanent.getControllerId().equals(source.getControllerId())) { + return true; + } + } + return false; + } + + @Override + public CantAttackYouUnlessPayManaAllEffect copy() { + return new CantAttackYouUnlessPayManaAllEffect(this); + } +} diff --git a/Mage/src/mage/abilities/effects/common/replacement/CantAttackYouUnlessPayManaAllEffect.java b/Mage/src/mage/abilities/effects/common/replacement/CantAttackYouUnlessPayManaAllEffect.java deleted file mode 100644 index 36f7be673a..0000000000 --- a/Mage/src/mage/abilities/effects/common/replacement/CantAttackYouUnlessPayManaAllEffect.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.effects.common.replacement; - -import mage.abilities.Ability; -import mage.abilities.costs.mana.ManaCosts; -import mage.abilities.effects.PayCostToAttackBlockEffectImpl; -import mage.abilities.effects.PayCostToAttackBlockEffectImpl.RestrictType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.events.GameEvent; - -/** - * - * @author LevelX2 - */ -public class CantAttackYouUnlessPayManaAllEffect extends PayCostToAttackBlockEffectImpl { - - public CantAttackYouUnlessPayManaAllEffect(ManaCosts manaCosts) { - super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK, manaCosts); - staticText = "Creatures can't attack you unless their controller pays " + manaCosts.getText() + " for each creature he or she controls that's attacking you"; - } - - CantAttackYouUnlessPayManaAllEffect(CantAttackYouUnlessPayManaAllEffect effect) { - super(effect); - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return source.getControllerId().equals(event.getTargetId()); - } - - @Override - public CantAttackYouUnlessPayManaAllEffect copy() { - return new CantAttackYouUnlessPayManaAllEffect(this); - } -} diff --git a/Mage/src/mage/game/combat/Combat.java b/Mage/src/mage/game/combat/Combat.java index 80caa16dba..59317b4a64 100644 --- a/Mage/src/mage/game/combat/Combat.java +++ b/Mage/src/mage/game/combat/Combat.java @@ -71,6 +71,7 @@ public class Combat implements Serializable, Copyable { protected List groups = new ArrayList<>(); protected Map blockingGroups = new HashMap<>(); + // player and plainswalker ids protected Set defenders = new HashSet<>(); // how many creatures attack defending player protected Map> numberCreaturesDefenderAttackedBy = new HashMap<>();