diff --git a/.gitignore b/.gitignore index 53ce9bb177c..e98869c519f 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ Mage.Server.Plugins/Mage.Deck.Limited/target Mage.Server.Plugins/Mage.Game.CommanderDuel/target Mage.Server.Plugins/Mage.Game.FreeForAll/target Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/target +Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/target Mage.Server.Plugins/Mage.Player.AI/target Mage.Server.Plugins/Mage.Player.AIMinimax/target Mage.Server.Plugins/Mage.Player.AI.MA/target @@ -82,4 +83,7 @@ Mage.Server.Plugins/Mage.Draft.8PlayerBooster/target /Mage.Server/config/ai.please.cast.this.txt /Mage.Stats/target/ -/Utils/*_unimplemented.txt \ No newline at end of file +/Utils/*_unimplemented.txt +*.netbeans_automatic_build +*.txt +Mage.Client/serverlist.txt \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/game/GamePanel.java b/Mage.Client/src/main/java/mage/client/game/GamePanel.java index 02c14fc8acb..88480f2d30e 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -27,6 +27,7 @@ */ package mage.client.game; +import com.sun.java.swing.plaf.windows.WindowsBorders; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; @@ -606,7 +607,14 @@ public final class GamePanel extends javax.swing.JPanel { else { this.txtStep.setText(""); } - this.txtPhasesBottomInfo.setText(" " + (game.getSpellsCastCurrentTurn() > 0 ? Integer.toString(game.getSpellsCastCurrentTurn()):"")); + if (game.getSpellsCastCurrentTurn() > 0) { + this.phasesBottomPanel.setVisible(true); + this.txtPhasesBottomInfo.setText(Integer.toString(game.getSpellsCastCurrentTurn())); + } else { + this.phasesBottomPanel.setVisible(false); + this.txtPhasesBottomInfo.setText(""); + } + this.txtActivePlayer.setText(game.getActivePlayerName()); this.txtPriority.setText(game.getPriorityPlayerName()); this.txtTurn.setText(Integer.toString(game.getTurn())); @@ -1508,8 +1516,9 @@ public final class GamePanel extends javax.swing.JPanel { phasesContainer.add(empty1, ratio); phasesContainer.add(jPhases); - JPanel phasesBottomPanel = new JPanel(); - phasesBottomPanel.setBackground(new Color(0, 0, 0, 0)); + phasesBottomPanel = new JPanel(); + phasesBottomPanel.setBackground(Color.LIGHT_GRAY); + phasesBottomPanel.setBorder(new LineBorder(Color.DARK_GRAY, 2)); phasesBottomPanel.add(txtPhasesBottomInfo); phasesContainer.add(phasesBottomPanel); @@ -1862,6 +1871,7 @@ public final class GamePanel extends javax.swing.JPanel { private javax.swing.JSplitPane jSplitPane2; private JPanel jPhases; private JPanel phasesContainer; + private JPanel phasesBottomPanel; private javax.swing.JLabel txtPhasesBottomInfo; private HoverButton currentStep; diff --git a/Mage.Client/src/main/java/mage/client/util/sets/ConstructedFormats.java b/Mage.Client/src/main/java/mage/client/util/sets/ConstructedFormats.java index c8a147d96fb..01aed15a9d3 100644 --- a/Mage.Client/src/main/java/mage/client/util/sets/ConstructedFormats.java +++ b/Mage.Client/src/main/java/mage/client/util/sets/ConstructedFormats.java @@ -27,7 +27,7 @@ public class ConstructedFormats { private static final String[] constructedFormats = { ALL, STANDARD, EXTENDED, MODERN, - "* Khans of Tarkir Block", "Khans of Tarkir", "Fate Reforged", /*"Dragons of Tarkir"*/ + "* Khans of Tarkir Block", "Khans of Tarkir", "Fate Reforged", "Dragons of Tarkir", "* Theros Block", "Journey into Nyx", "Born of the Gods", "Theros", "* Return to Ravnica Block", "Dragon's Maze", "Gatecrash", "Return to Ravnica", "* Innistrad Block", "Avacyn Restored", "Dark Ascension", "Innistrad", @@ -113,7 +113,10 @@ public class ConstructedFormats { public static List getSetsByFormat(String format) { if (format.equals("* Khans of Tarkir Block")) { - return Arrays.asList("KTK", "FRF"); + return Arrays.asList("KTK", "FRF","DTK"); + } + if (format.equals("Dragons of Tarkir")) { + return Arrays.asList("DTK"); } if (format.equals("Fate Reforged")) { return Arrays.asList("FRF"); diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java index c5d6ea70307..08f07a44e2b 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java @@ -44,8 +44,7 @@ public class GathererSets implements Iterable { "MMA", "THS", "BNG", "JOU", "CNS", "VMA", - "KTK", "FRF" - }; + "KTK", "FRF", "DTK"}; private static final HashMap symbolsReplacements = new HashMap<>(); static { diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagicCardsImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagicCardsImageSource.java index 94a9287442d..4c82016c580 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagicCardsImageSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagicCardsImageSource.java @@ -16,6 +16,7 @@ public class MagicCardsImageSource implements CardImageSource { private static final Map setNameTokenReplacement = new HashMap() { { + put("DTK", "dragons-of-tarkir"); put("GRC","wpngateway"); put("MBP","media-inserts"); put("MLP", "launch-party"); diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgImageSource.java index ca58560210d..0898c54bb33 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgImageSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgImageSource.java @@ -28,8 +28,6 @@ package org.mage.plugins.card.dl.sources; -import java.net.URLEncoder; -import mage.cards.SplitCard; import org.mage.plugins.card.images.CardDownloadData; /** @@ -56,7 +54,7 @@ public class MtgImageSource implements CardImageSource { throw new Exception("Wrong parameters for image: collector id: " + collectorId + ",card set: " + cardSet); } StringBuilder url = new StringBuilder("http://mtgimage.com/set/"); - url.append(cardSet.toUpperCase()).append("/"); + url.append(cardSet.toUpperCase()).append("/"); if (card.isSplitCard()) { url.append(card.getDownloadName().replaceAll(" // ", "")); diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java index 8057eb6bd00..d2371ba39f3 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java @@ -1,203 +1,204 @@ -package org.mage.plugins.card.dl.sources; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.select.Elements; -import org.mage.plugins.card.images.CardDownloadData; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -/** - * - * @author North - */ -public class WizardCardsImageSource implements CardImageSource { - - private static CardImageSource instance; - private static Map setsAliases; - private final Map> sets; - - public static CardImageSource getInstance() { - if (instance == null) { - instance = new WizardCardsImageSource(); - } - return instance; - } - - public WizardCardsImageSource() { - sets = new HashMap<>(); - setsAliases = new HashMap<>(); - setsAliases.put("FRF", "fatereforged/cig"); - setsAliases.put("C14", "commander2014/cig"); - setsAliases.put("KTK", "khansoftarkir/cig"); - setsAliases.put("M15", "magic2015coreset/cig"); - setsAliases.put("CNS", "vintagemasters/cig"); - setsAliases.put("CNS", "conspiracy/cig"); - setsAliases.put("JOU", "journeyintonyx/cig"); - setsAliases.put("BNG", "bornofthegods/cig"); - setsAliases.put("C13", "commander2013/cig"); - setsAliases.put("THS", "theros/cig"); - setsAliases.put("M14", "magic2014coreset/cig"); - setsAliases.put("MMA", "modernmasters/cig"); - setsAliases.put("DGM", "dragonsmaze/cig"); - setsAliases.put("GTC", "gatecrash/cig"); - setsAliases.put("RTR", "returntoravnica/cig"); - setsAliases.put("M13", "magic2013/cig"); - setsAliases.put("AVR", "avacynrestored/cig"); - setsAliases.put("DKA", "darkascension/cig"); - setsAliases.put("ISD", "innistrad/cig"); - setsAliases.put("M12", "magic2012/cig"); - setsAliases.put("CMD", "commander/cig"); - setsAliases.put("NPH", "newphyrexia/spoiler"); - setsAliases.put("MBS", "mirrodinbesieged/spoiler"); - setsAliases.put("SOM", "scarsofmirrodin/spoiler"); - setsAliases.put("M11", "magic2011/spoiler"); - setsAliases.put("ROE", "riseoftheeldrazi/spoiler"); - setsAliases.put("WWK", "worldwake/spoiler"); - setsAliases.put("ZEN", "zendikar/spoiler"); - setsAliases.put("M10", "magic2010/spoiler"); - setsAliases.put("ARB", "alarareborn/spoiler"); - setsAliases.put("CON", "conflux/spoiler"); - setsAliases.put("ALA", "shardsofalara/spoiler"); - setsAliases.put("PC2", "planechase2012edition/cig"); - setsAliases.put("PTK", "portalthreekingdoms/cig"); - setsAliases.put("EVG", "elvesvsgoblins/cig"); - setsAliases.put("DD2", "jacevschandra/cig"); - setsAliases.put("DDC", "divinevsdemonic/cig"); - setsAliases.put("DDD", "garrukvsliliana/cig"); - setsAliases.put("DDE", "phyrexiavsthecoalition/cig"); - setsAliases.put("DDF", "elspethvstezzeret/cig"); - setsAliases.put("DDG", "knightsvsdragons/cig"); - setsAliases.put("DDH", "ajanivsnicolbolas/cig"); - setsAliases.put("DDI", "venservskoth/cig"); - setsAliases.put("DDJ", "izzetvsgolgari/cig"); - setsAliases.put("DDK", "sorinvstibalt/cig"); - setsAliases.put("DDL", "heroesvsmonsters/cig"); - setsAliases.put("DDM", "jacevsvraska/cig"); - setsAliases.put("DDN", "speedvscunning/cig"); - } - - private Map getSetLinks(String cardSet) { - Map setLinks = new HashMap<>(); - try { - String urlDocument; - if (cardSet.equals("M15")) { - urlDocument = "http://magic.wizards.com/en/content/magic-2015-core-set-card-set-archive-products-game-info"; - Document doc = Jsoup.connect(urlDocument).get(); - Elements cardsImages = doc.select("div.advanced-card img"); - for (int i = 0; i < cardsImages.size(); i++) { - String cardName = normalizeName(cardsImages.get(i).attr("alt")); - if (cardName != null && !cardName.isEmpty()) { - if (cardName.equals("Forest") || cardName.equals("Swamp") || cardName.equals("Mountain") || cardName.equals("Island") || cardName.equals("Plains")) { - int landNumber = 1; - while (setLinks.get((cardName + landNumber).toLowerCase()) != null) { - landNumber++; - } - cardName += landNumber; - } - setLinks.put(cardName.toLowerCase(), cardsImages.get(i).attr("src")); - } else { - setLinks.put(Integer.toString(i), cardsImages.get(i).attr("src")); - } - } - - } else { - urlDocument = "http://www.wizards.com/magic/tcg/article.aspx?x=mtg/tcg/" + setsAliases.get(cardSet); - Document doc = Jsoup.connect(urlDocument).get(); - Elements cardsImages = doc.select("img[height$=370]"); - for (int i = 0; i < cardsImages.size(); i++) { - String cardName = normalizeName(cardsImages.get(i).attr("title")); - if (cardName != null && !cardName.isEmpty()) { - if (cardName.equals("Forest") || cardName.equals("Swamp") || cardName.equals("Mountain") || cardName.equals("Island") || cardName.equals("Plains")) { - int landNumber = 1; - while (setLinks.get((cardName + landNumber).toLowerCase()) != null) { - landNumber++; - } - cardName += landNumber; - } - setLinks.put(cardName.toLowerCase(), cardsImages.get(i).attr("src")); - } else { - setLinks.put(Integer.toString(i), cardsImages.get(i).attr("src")); - } - } - - cardsImages = doc.select("img[height$=470]"); - for (int i = 0; i < cardsImages.size(); i++) { - String cardName = normalizeName(cardsImages.get(i).attr("title")); - - if (cardName != null && !cardName.isEmpty()) { - String[] cardNames = cardName.replace(")", "").split(" \\("); - for (String name : cardNames) { - setLinks.put(name.toLowerCase(), cardsImages.get(i).attr("src")); - } - } else { - setLinks.put(Integer.toString(i), cardsImages.get(i).attr("src")); - } - } - - } - } catch (IOException ex) { - System.out.println("Exception when parsing the wizards page: " + ex.getMessage()); - } - return setLinks; - } - - private String normalizeName(String name) { - return name.replace("\u2014", "-").replace("\u2019", "'") - .replace("\u00C6", "AE").replace("\u00E6", "ae") - .replace("\u00C1", "A").replace("\u00E1", "a") - .replace("\u00C2", "A").replace("\u00E2", "a") - .replace("\u00D6", "O").replace("\u00F6", "o") - .replace("\u00DB", "U").replace("\u00FB", "u") - .replace("\u00DC", "U").replace("\u00FC", "u") - .replace("\u00E9", "e").replace("&", "//") - .replace("Hintreland Scourge", "Hinterland Scourge"); - } - - @Override - public String generateURL(CardDownloadData card) throws Exception { - Integer collectorId = card.getCollectorId(); - String cardSet = card.getSet(); - if (collectorId == null || cardSet == null) { - throw new Exception("Wrong parameters for image: collector id: " + collectorId + ",card set: " + cardSet); - } - if (card.isFlippedSide()) { //doesn't support rotated images - return null; - } - if (setsAliases.get(cardSet) != null) { - Map setLinks = sets.get(cardSet); - if (setLinks == null) { - setLinks = getSetLinks(cardSet); - sets.put(cardSet, setLinks); - } - String link = setLinks.get(card.getDownloadName().toLowerCase()); - if (link == null) { - if (setLinks.size() >= collectorId) { - link = setLinks.get(Integer.toString(collectorId - 1)); - } else { - link = setLinks.get(Integer.toString(collectorId - 21)); - if (link != null) { - link = link.replace(Integer.toString(collectorId - 20), (Integer.toString(collectorId - 20) + "a")); - } - } - } - if (link != null && !link.startsWith("http://")) { - link = "http://www.wizards.com" + link; - } - return link; - } - return null; - } - - @Override - public String generateTokenUrl(CardDownloadData card) { - return null; - } - - @Override - public Float getAverageSize() { - return 60.0f; - } -} +package org.mage.plugins.card.dl.sources; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.select.Elements; +import org.mage.plugins.card.images.CardDownloadData; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author North + */ +public class WizardCardsImageSource implements CardImageSource { + + private static CardImageSource instance; + private static Map setsAliases; + private final Map> sets; + + public static CardImageSource getInstance() { + if (instance == null) { + instance = new WizardCardsImageSource(); + } + return instance; + } + + public WizardCardsImageSource() { + sets = new HashMap<>(); + setsAliases = new HashMap<>(); + setsAliases.put("DTK", "dragonsoftarkir/cig"); + setsAliases.put("FRF", "fatereforged/cig"); + setsAliases.put("C14", "commander2014/cig"); + setsAliases.put("KTK", "khansoftarkir/cig"); + setsAliases.put("M15", "magic2015coreset/cig"); + setsAliases.put("CNS", "vintagemasters/cig"); + setsAliases.put("CNS", "conspiracy/cig"); + setsAliases.put("JOU", "journeyintonyx/cig"); + setsAliases.put("BNG", "bornofthegods/cig"); + setsAliases.put("C13", "commander2013/cig"); + setsAliases.put("THS", "theros/cig"); + setsAliases.put("M14", "magic2014coreset/cig"); + setsAliases.put("MMA", "modernmasters/cig"); + setsAliases.put("DGM", "dragonsmaze/cig"); + setsAliases.put("GTC", "gatecrash/cig"); + setsAliases.put("RTR", "returntoravnica/cig"); + setsAliases.put("M13", "magic2013/cig"); + setsAliases.put("AVR", "avacynrestored/cig"); + setsAliases.put("DKA", "darkascension/cig"); + setsAliases.put("ISD", "innistrad/cig"); + setsAliases.put("M12", "magic2012/cig"); + setsAliases.put("CMD", "commander/cig"); + setsAliases.put("NPH", "newphyrexia/spoiler"); + setsAliases.put("MBS", "mirrodinbesieged/spoiler"); + setsAliases.put("SOM", "scarsofmirrodin/spoiler"); + setsAliases.put("M11", "magic2011/spoiler"); + setsAliases.put("ROE", "riseoftheeldrazi/spoiler"); + setsAliases.put("WWK", "worldwake/spoiler"); + setsAliases.put("ZEN", "zendikar/spoiler"); + setsAliases.put("M10", "magic2010/spoiler"); + setsAliases.put("ARB", "alarareborn/spoiler"); + setsAliases.put("CON", "conflux/spoiler"); + setsAliases.put("ALA", "shardsofalara/spoiler"); + setsAliases.put("PC2", "planechase2012edition/cig"); + setsAliases.put("PTK", "portalthreekingdoms/cig"); + setsAliases.put("EVG", "elvesvsgoblins/cig"); + setsAliases.put("DD2", "jacevschandra/cig"); + setsAliases.put("DDC", "divinevsdemonic/cig"); + setsAliases.put("DDD", "garrukvsliliana/cig"); + setsAliases.put("DDE", "phyrexiavsthecoalition/cig"); + setsAliases.put("DDF", "elspethvstezzeret/cig"); + setsAliases.put("DDG", "knightsvsdragons/cig"); + setsAliases.put("DDH", "ajanivsnicolbolas/cig"); + setsAliases.put("DDI", "venservskoth/cig"); + setsAliases.put("DDJ", "izzetvsgolgari/cig"); + setsAliases.put("DDK", "sorinvstibalt/cig"); + setsAliases.put("DDL", "heroesvsmonsters/cig"); + setsAliases.put("DDM", "jacevsvraska/cig"); + setsAliases.put("DDN", "speedvscunning/cig"); + } + + private Map getSetLinks(String cardSet) { + Map setLinks = new HashMap<>(); + try { + String urlDocument; + if (cardSet.equals("M15")) { + urlDocument = "http://magic.wizards.com/en/content/magic-2015-core-set-card-set-archive-products-game-info"; + Document doc = Jsoup.connect(urlDocument).get(); + Elements cardsImages = doc.select("div.advanced-card img"); + for (int i = 0; i < cardsImages.size(); i++) { + String cardName = normalizeName(cardsImages.get(i).attr("alt")); + if (cardName != null && !cardName.isEmpty()) { + if (cardName.equals("Forest") || cardName.equals("Swamp") || cardName.equals("Mountain") || cardName.equals("Island") || cardName.equals("Plains")) { + int landNumber = 1; + while (setLinks.get((cardName + landNumber).toLowerCase()) != null) { + landNumber++; + } + cardName += landNumber; + } + setLinks.put(cardName.toLowerCase(), cardsImages.get(i).attr("src")); + } else { + setLinks.put(Integer.toString(i), cardsImages.get(i).attr("src")); + } + } + + } else { + urlDocument = "http://www.wizards.com/magic/tcg/article.aspx?x=mtg/tcg/" + setsAliases.get(cardSet); + Document doc = Jsoup.connect(urlDocument).get(); + Elements cardsImages = doc.select("img[height$=370]"); + for (int i = 0; i < cardsImages.size(); i++) { + String cardName = normalizeName(cardsImages.get(i).attr("title")); + if (cardName != null && !cardName.isEmpty()) { + if (cardName.equals("Forest") || cardName.equals("Swamp") || cardName.equals("Mountain") || cardName.equals("Island") || cardName.equals("Plains")) { + int landNumber = 1; + while (setLinks.get((cardName + landNumber).toLowerCase()) != null) { + landNumber++; + } + cardName += landNumber; + } + setLinks.put(cardName.toLowerCase(), cardsImages.get(i).attr("src")); + } else { + setLinks.put(Integer.toString(i), cardsImages.get(i).attr("src")); + } + } + + cardsImages = doc.select("img[height$=470]"); + for (int i = 0; i < cardsImages.size(); i++) { + String cardName = normalizeName(cardsImages.get(i).attr("title")); + + if (cardName != null && !cardName.isEmpty()) { + String[] cardNames = cardName.replace(")", "").split(" \\("); + for (String name : cardNames) { + setLinks.put(name.toLowerCase(), cardsImages.get(i).attr("src")); + } + } else { + setLinks.put(Integer.toString(i), cardsImages.get(i).attr("src")); + } + } + + } + } catch (IOException ex) { + System.out.println("Exception when parsing the wizards page: " + ex.getMessage()); + } + return setLinks; + } + + private String normalizeName(String name) { + return name.replace("\u2014", "-").replace("\u2019", "'") + .replace("\u00C6", "AE").replace("\u00E6", "ae") + .replace("\u00C1", "A").replace("\u00E1", "a") + .replace("\u00C2", "A").replace("\u00E2", "a") + .replace("\u00D6", "O").replace("\u00F6", "o") + .replace("\u00DB", "U").replace("\u00FB", "u") + .replace("\u00DC", "U").replace("\u00FC", "u") + .replace("\u00E9", "e").replace("&", "//") + .replace("Hintreland Scourge", "Hinterland Scourge"); + } + + @Override + public String generateURL(CardDownloadData card) throws Exception { + Integer collectorId = card.getCollectorId(); + String cardSet = card.getSet(); + if (collectorId == null || cardSet == null) { + throw new Exception("Wrong parameters for image: collector id: " + collectorId + ",card set: " + cardSet); + } + if (card.isFlippedSide()) { //doesn't support rotated images + return null; + } + if (setsAliases.get(cardSet) != null) { + Map setLinks = sets.get(cardSet); + if (setLinks == null) { + setLinks = getSetLinks(cardSet); + sets.put(cardSet, setLinks); + } + String link = setLinks.get(card.getDownloadName().toLowerCase()); + if (link == null) { + if (setLinks.size() >= collectorId) { + link = setLinks.get(Integer.toString(collectorId - 1)); + } else { + link = setLinks.get(Integer.toString(collectorId - 21)); + if (link != null) { + link = link.replace(Integer.toString(collectorId - 20), (Integer.toString(collectorId - 20) + "a")); + } + } + } + if (link != null && !link.startsWith("http://")) { + link = "http://www.wizards.com" + link; + } + return link; + } + return null; + } + + @Override + public String generateTokenUrl(CardDownloadData card) { + return null; + } + + @Override + public Float getAverageSize() { + return 60.0f; + } +} diff --git a/Mage.Client/src/main/resources/image.url.properties b/Mage.Client/src/main/resources/image.url.properties index e66667518f8..7bd4c80ff01 100644 --- a/Mage.Client/src/main/resources/image.url.properties +++ b/Mage.Client/src/main/resources/image.url.properties @@ -65,6 +65,6 @@ ddd=gvl unh=uh dde=pvc # Remove setname as soon as the images can be downloaded -ignore.urls=TOK +ignore.urls=TOK,DTK # sets ordered by release time (newest goes first) -token.lookup.order=FRF,KTK,M15,VMA,CNS,JOU,BNG,THS,DDL,M14,MMA,DGM,GTC,RTR,M13,AVR,DDI,DKA,ISD,M12,NPH,MBS,SOM,M11,ROE,DDE,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS,CHK,GRC \ No newline at end of file +token.lookup.order=DTK,FRF,KTK,M15,VMA,CNS,JOU,BNG,THS,DDL,M14,MMA,DGM,GTC,RTR,M13,AVR,DDI,DKA,ISD,M12,NPH,MBS,SOM,M11,ROE,DDE,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS,CHK,GRC \ No newline at end of file diff --git a/Mage.Common/src/mage/view/CardView.java b/Mage.Common/src/mage/view/CardView.java index 3de576a563a..61012447fb3 100644 --- a/Mage.Common/src/mage/view/CardView.java +++ b/Mage.Common/src/mage/view/CardView.java @@ -50,6 +50,7 @@ import mage.target.Targets; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import mage.game.Game; /** * @author BetaSteward_at_googlemail.com @@ -116,21 +117,27 @@ public class CardView extends SimpleCardView { protected boolean canAttack; public CardView(Card card) { - this(card, null, false); + this(card, null, null, false); } public CardView(Card card, UUID cardId) { - this(card, null, false); + this(card, null, null, false); + this.id = cardId; + } + + public CardView(Card card, Game game, UUID cardId) { + this(card, game, null, false); this.id = cardId; } /** * * @param card + * @param game * @param cardId * @param controlled is the card view created for the card controller - used for morph / face down cards to know which player may see information for the card */ - public CardView(Card card, UUID cardId, boolean controlled) { + public CardView(Card card, Game game, UUID cardId, boolean controlled) { super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.isFaceDown(), card.getUsesVariousArt(), card.getTokenSetCode()); this.morphCard = card.isMorphCard(); // no information available for face down cards as long it's not a controlled face down morph card @@ -241,9 +248,9 @@ public class CardView extends SimpleCardView { this.rarity = card.getRarity(); this.isToken = false; } - if (card.getCounters() != null && !card.getCounters().isEmpty()) { + if (game != null && card.getCounters(game) != null && !card.getCounters(game).isEmpty()) { counters = new ArrayList<>(); - for (Counter counter: card.getCounters().values()) { + for (Counter counter: card.getCounters(game).values()) { counters.add(new CounterView(counter)); } } diff --git a/Mage.Common/src/mage/view/GameView.java b/Mage.Common/src/mage/view/GameView.java index bd6203a9ee6..02adfe140d5 100644 --- a/Mage.Common/src/mage/view/GameView.java +++ b/Mage.Common/src/mage/view/GameView.java @@ -136,7 +136,7 @@ public class GameView implements Serializable { } else { // Spell - stack.put(stackObject.getId(), new CardView((Spell)stackObject, null, stackObject.getControllerId().equals(createdForPlayerId))); + stack.put(stackObject.getId(), new CardView((Spell)stackObject, game, null, stackObject.getControllerId().equals(createdForPlayerId))); checkPaid(stackObject.getId(), (Spell)stackObject); } //stackOrder.add(stackObject.getId()); diff --git a/Mage.Common/src/mage/view/PermanentView.java b/Mage.Common/src/mage/view/PermanentView.java index 80451c02503..dfc3c49b89f 100644 --- a/Mage.Common/src/mage/view/PermanentView.java +++ b/Mage.Common/src/mage/view/PermanentView.java @@ -62,7 +62,7 @@ public class PermanentView extends CardView { private final boolean attachedToPermanent; public PermanentView(Permanent permanent, Card card, UUID createdForPlayerId, Game game) { - super(permanent, null, permanent.getControllerId().equals(createdForPlayerId)); + super(permanent, game, null, permanent.getControllerId().equals(createdForPlayerId)); this.controlled = permanent.getControllerId().equals(createdForPlayerId); this.rules = permanent.getRules(); this.tapped = permanent.isTapped(); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java new file mode 100644 index 00000000000..d425a8bf318 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java @@ -0,0 +1,213 @@ +/* + * Copyright 2011 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.deck; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import mage.abilities.common.CanBeYourCommanderAbility; +import mage.cards.Card; +import mage.cards.decks.Deck; +import mage.cards.decks.DeckValidator; +import mage.constants.CardType; +import mage.filter.FilterMana; +import mage.util.CardUtil; + +/** + * + * @author JRHerlehy + */ +public class TinyLeaders extends DeckValidator { + + protected List banned = new ArrayList<>(); + protected List bannedCommander = new ArrayList<>(); + + public TinyLeaders() { + this("Tiny Leaders"); + //Banned list from tinyleaders.blodspot.ca/p/ban-list.html + //Ban list updated as of 11/08/14 + banned.add("Ancestral Recall"); + banned.add("Balance"); + banned.add("Black Lotus"); + banned.add("Channel"); + banned.add("Counterbalance"); + banned.add("Demonic Tutor"); + banned.add("Earthcraft"); + banned.add("Edric, Spymaster of Trest"); + banned.add("Fastbond"); + banned.add("Goblin Recruiter"); + banned.add("Hermit Druid"); + banned.add("Imperial Seal"); + banned.add("Library of Alexandria"); + banned.add("Karakas"); + banned.add("Mana Crypt"); + banned.add("Mana Drain"); + banned.add("Mana Vault"); + banned.add("metalworker"); + banned.add("Mind Twist"); + banned.add("Mishra's Workshop"); + banned.add("Mox Emerald"); + banned.add("Mox Jet"); + banned.add("Mox Pearl"); + banned.add("Mox Ruby"); + banned.add("Mox Sapphire"); + banned.add("Necropotence"); + banned.add("Painter's Servant"); + banned.add("Shahrazad"); + banned.add("Skullclamp"); + banned.add("Sol Ring"); + banned.add("Strip Mine"); + banned.add("Survival of the Fittest"); + banned.add("Sword of Body and Mind"); + banned.add("Time Vault"); + banned.add("Time Walk"); + banned.add("Timetwister"); + banned.add("Tolarian Academy"); + banned.add("Umezawa's Jitte"); + banned.add("Vampiric Tutor"); + banned.add("Yawgmoth's Will"); + + //Additionally, these Legendary creatures cannot be used as Commanders + bannedCommander.add("Erayo, Soratami Ascendant"); + bannedCommander.add("Rofellos, Llanowar Emissary"); + bannedCommander.add("Derevi, Empyrical Tactician"); + } + + public TinyLeaders(String name) { + super(name); + } + + /** + * + * @param deck + * @return - True if deck is valid + */ + @Override + public boolean validate(Deck deck) { + boolean valid = true; + + if (deck.getCards().size() != 49) { + invalid.put("Deck", "Must contain 49 cards: has " + deck.getCards().size() + " cards"); + valid = false; + } + + List basicLandNames = new ArrayList<>(Arrays.asList("Forest", "Island", "Mountain", "Swamp", "Plains", + "Snow-Covered Forest", "Snow-Covered Island", "Snow-Covered Mountain", "Snow-Covered Swamp", "Snow-Covered Plains")); + Map counts = new HashMap<>(); + countCards(counts, deck.getCards()); + countCards(counts, deck.getSideboard()); + for (Map.Entry entry : counts.entrySet()) { + if (entry.getValue() > 1) { + if (!basicLandNames.contains(entry.getKey()) && !entry.getKey().equals("Relentless Rats") && !entry.getKey().equals("Shadowborn Apostle")) { + invalid.put(entry.getKey(), "Too many: " + entry.getValue()); + valid = false; + } + } + } + + for (String bannedCard : banned) { + if (counts.containsKey(bannedCard)) { + invalid.put(bannedCard, "Banned"); + valid = false; + } + } + + if (deck.getSideboard().size() <= 11) { + Card commander = null; + + for (Card card : deck.getSideboard()) { + if (card.getName().equalsIgnoreCase(deck.getName())) { + commander = card; + } + } + + /** + * 905.5b - Each card must have a converted mana cost of three of less. + * Cards with {X} in their mana cost count X as zero. + * Split and double-face cards are legal only if both of their halves would be legal independently. + */ + + if (commander == null || commander.getManaCost().convertedManaCost() > 3) { + if (commander == null) { + invalid.put("Leader", "Please be sure to set your leader in the NAME field in the DECK EDITOR"); + } + if (commander != null && commander.getManaCost().convertedManaCost() > 3) { + invalid.put("Leader", "Commander CMC is Greater than 3"); + } + return false; + } + if ((commander.getCardType().contains(CardType.CREATURE) && commander.getSupertype().contains("Legendary")) + || (commander.getCardType().contains(CardType.PLANESWALKER) && commander.getAbilities().contains(CanBeYourCommanderAbility.getInstance()))) { + if (!bannedCommander.contains(commander.getName())) { + FilterMana color = CardUtil.getColorIdentity(commander); + for (Card card : deck.getCards()) { + if (!cardHasValideColor(color, card)) { + invalid.put(card.getName(), "Invalid color (" + commander.getName() + ")"); + valid = false; + } + + //905.5b - Converted mana cost must be 3 or less + if (card.getManaCost().convertedManaCost() > 3) { + invalid.put(card.getName(), "Invalid cost (" + card.getManaCost().convertedManaCost() + ")"); + valid = false; + } + } + } else { + invalid.put("Commander", "Commander banned (" + commander.getName() + ")"); + valid = false; + } + } else { + invalid.put("Commander", "Commander invalide (" + commander.getName() + ")"); + valid = false; + } + } else { + invalid.put("Commander", "Sideboard must contain only the commander and a maximum of 10 sideboard cards"); + valid = false; + } + + return valid; + } + + /** + * + * @param commander FilterMana object with Color Identity of Commander set + * @param card Card to validate + * @return True if card has a valid color identity + */ + public boolean cardHasValideColor(FilterMana commander, Card card) { + FilterMana cardColor = CardUtil.getColorIdentity(card); + return !(cardColor.isBlack() && !commander.isBlack() + || cardColor.isBlue() && !commander.isBlue() + || cardColor.isGreen() && !commander.isGreen() + || cardColor.isRed() && !commander.isRed() + || cardColor.isWhite() && !commander.isWhite()); + } + +} diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml new file mode 100644 index 00000000000..ccf6b733a04 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml @@ -0,0 +1,50 @@ + + + + 4.0.0 + + + org.mage + mage-server-plugins + 1.3.0 + + + mage-game-tinyleadersduel + jar + Mage Game Tiny Leaders Two Player + + + + ${project.groupId} + mage + ${project.version} + + + + + src + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.7 + 1.7 + + + + maven-resources-plugin + + UTF-8 + + + + + + mage-game-tinyleadersduel + + + + + diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuel.java b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuel.java new file mode 100644 index 00000000000..b8e05f76073 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuel.java @@ -0,0 +1,64 @@ +/* + * 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.game; + +import mage.constants.MultiplayerAttackOption; +import mage.constants.RangeOfInfluence; +import mage.game.match.MatchType; + +/** + * + * @author JRHerlehy + */ +public class TinyLeadersDuel extends GameTinyLeadersImpl { + + public TinyLeadersDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range, int freeMulligans, int startLife) { + super(attackOption, range, freeMulligans, startLife); + } + + public TinyLeadersDuel(final TinyLeadersDuel game) { + super(game); + } + + @Override + public MatchType getGameType() { + return new TinyLeadersDuelType(); + } + + @Override + public int getNumPlayers() { + return 2; + } + + @Override + public TinyLeadersDuel copy() { + return new TinyLeadersDuel(this); + } + +} diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelMatch.java b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelMatch.java new file mode 100644 index 00000000000..6349384e7c1 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelMatch.java @@ -0,0 +1,58 @@ +/* + * 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.game; + +import mage.game.match.MatchImpl; +import mage.game.match.MatchOptions; + +/** + * + * @author JRHerlehy + */ +public class TinyLeadersDuelMatch extends MatchImpl { + + public TinyLeadersDuelMatch(MatchOptions options) { + super(options); + } + + @Override + public void startGame() throws GameException { + //Tiny Leaders Play Rule 13: Players begin the game with 25 life. + int startLife = 25; + + TinyLeadersDuel game = new TinyLeadersDuel(options.getAttackOption(), options.getRange(), options.getFreeMulligans(), startLife); + game.setStartMessage(this.createGameStartMessage()); + + //Tucking a Tiny Leader is legal + game.setAlsoLibrary(false); + this.initGame(game); + games.add(game); + } + +} diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java new file mode 100644 index 00000000000..7936d4e8909 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/src/mage/game/TinyLeadersDuelType.java @@ -0,0 +1,58 @@ +/* + * 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.game; + +import mage.game.match.MatchType; + +/** + * + * @author JRHerlehy + */ +public class TinyLeadersDuelType extends MatchType { + + public TinyLeadersDuelType() { + this.name = "Tiny Leaders Two Player Duel"; + this.maxPlayers = 2; + this.minPlayers = 2; + this.numTeams = 0; + this.useAttackOption = false; + this.useRange = false; + this.sideboardingAllowed = true; + } + + protected TinyLeadersDuelType(final TinyLeadersDuelType matchType){ + super(matchType); + } + + @Override + public TinyLeadersDuelType copy() { + return new TinyLeadersDuelType(this); + } + +} diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 11d8d71fae1..df1a684fa0c 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -75,6 +75,9 @@ import java.io.IOException; import java.io.Serializable; import java.util.*; import java.util.Map.Entry; +import mage.filter.Filter; +import mage.filter.predicate.other.PlayerIdPredicate; +import mage.filter.predicate.permanent.ControllerIdPredicate; /** @@ -1011,7 +1014,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } @Override - public boolean playMana(ManaCost unpaid, Game game) { + public boolean playMana(ManaCost unpaid, String promptText, Game game) { payManaMode = true; boolean result = playManaHandling(unpaid, game); payManaMode = false; @@ -1283,6 +1286,9 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (card != null) { target.addTarget(card.getId(), source, game); cardChoices.remove(card); + } else { + // We don't have any valid target to choose so stop choosing + return target.getTargets().size() < target.getNumberOfTargets(); } if (outcome.equals(Outcome.Neutral) && target.getTargets().size() > target.getNumberOfTargets() + (target.getMaxNumberOfTargets() - target.getNumberOfTargets()) / 2) { return true; @@ -1947,10 +1953,14 @@ public class ComputerPlayer extends PlayerImpl implements Player { } protected List threats(UUID playerId, UUID sourceId, FilterPermanent filter, Game game, List targets) { - List threats = (playerId == null || sourceId ==null) ? - game.getBattlefield().getActivePermanents(filter, this.getId(), sourceId, game) : // all permanents within the range of the player - game.getBattlefield().getActivePermanents(filter, playerId, sourceId, game); - + List threats; + if (playerId == null) { + threats = game.getBattlefield().getActivePermanents(filter, this.getId(), sourceId, game); // all permanents within the range of the player + } else { + FilterPermanent filterCopy = filter.copy(); + filterCopy.add(new ControllerIdPredicate(playerId)); + threats = game.getBattlefield().getActivePermanents(filter, this.getId(), sourceId, game); + } Iterator it = threats.iterator(); while (it.hasNext()) { // remove permanents already targeted Permanent test = it.next(); diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 9c863bc9f3c..b29ea9d02d9 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -432,13 +432,29 @@ public class HumanPlayer extends PlayerImpl { if (target.getTargets().size() >= target.getNumberOfTargets()) { required = false; } - game.fireSelectTargetEvent(playerId, target.getMessage(), cards, required, getOptions(target, null)); + Map options = getOptions(target, null); + List chosen = target.getTargets(); + options.put("chosen", (Serializable)chosen); + List choosable = new ArrayList<>(); + for (UUID cardId : cards) { + if (target.canTarget(cardId, cards, game)) { + choosable.add(cardId); + } + } + if (!choosable.isEmpty()) { + options.put("choosable", (Serializable) choosable); + } + game.fireSelectTargetEvent(playerId, target.getMessage(), cards, required, options); waitForResponse(game); if (response.getUUID() != null) { - if (target.canTarget(response.getUUID(), cards, game)) { - target.addTarget(response.getUUID(), source, game); - if(target.doneChosing()){ - return true; + if (target.getTargets().contains(response.getUUID())) { // if already included remove it + target.remove(response.getUUID()); + } else { + if (target.canTarget(response.getUUID(), cards, game)) { + target.addTarget(response.getUUID(), source, game); + if (target.doneChosing()) { + return true; + } } } } else { @@ -606,17 +622,17 @@ public class HumanPlayer extends PlayerImpl { @Override - public boolean playMana(ManaCost unpaid, Game game) { + public boolean playMana(ManaCost unpaid, String promptText, Game game) { payManaMode = true; - boolean result = playManaHandling(unpaid, game); + boolean result = playManaHandling(unpaid, promptText, game); payManaMode = false; return result; } - protected boolean playManaHandling(ManaCost unpaid, Game game) { + protected boolean playManaHandling(ManaCost unpaid, String promptText, Game game) { updateGameStatePriority("playMana", game); - game.firePlayManaEvent(playerId, "Pay " + unpaid.getText()); + game.firePlayManaEvent(playerId, "Pay " + promptText); waitForResponse(game); if (!this.isInGame()) { return false; diff --git a/Mage.Server.Plugins/pom.xml b/Mage.Server.Plugins/pom.xml index 42835c4d180..5235f4a7a17 100644 --- a/Mage.Server.Plugins/pom.xml +++ b/Mage.Server.Plugins/pom.xml @@ -17,19 +17,12 @@ Mage.Deck.Constructed Mage.Deck.Limited - Mage.Game.CommanderDuel + Mage.Game.CommanderDuel Mage.Game.CommanderFreeForAll Mage.Game.FreeForAll Mage.Game.TwoPlayerDuel - Mage.Player.AI - Mage.Player.AIMinimax - Mage.Player.AI.MA - Mage.Player.AIMCTS - Mage.Player.AI.DraftBot Mage.Player.Human - Mage.Tournament.BoosterDraft - Mage.Tournament.Constructed - Mage.Tournament.Sealed - + Mage.Game.TinyLeadersDuel + \ No newline at end of file diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index 81bf9f2ea4d..c1aff2d008d 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -41,13 +41,14 @@ - + + @@ -91,6 +92,7 @@ + diff --git a/Mage.Server/pom.xml b/Mage.Server/pom.xml index 461172aaab3..f9f5951f3da 100644 --- a/Mage.Server/pom.xml +++ b/Mage.Server/pom.xml @@ -133,6 +133,12 @@ ${project.version} runtime + + ${project.groupId} + mage-game-tinyleadersduel + ${project.version} + runtime + diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java index bec8fda788f..f2a236d52f7 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -924,7 +924,9 @@ public class TableController { } if (matchPlayer.getPlayer().isHuman()) { humanPlayers++; - if ((table.getState().equals(TableState.WAITING) || table.getState().equals(TableState.STARTING) || table.getState().equals(TableState.READY_TO_START)) || + if ((table.getState().equals(TableState.WAITING) || + table.getState().equals(TableState.STARTING) || + table.getState().equals(TableState.READY_TO_START)) || !match.isDoneSideboarding() || (!matchPlayer.hasQuit() && match.getGame() != null && matchPlayer.getPlayer().isInGame())) { User user = UserManager.getInstance().getUser(userPlayerEntry.getKey()); diff --git a/Mage.Server/src/main/java/mage/server/TableManager.java b/Mage.Server/src/main/java/mage/server/TableManager.java index 1ef11e9f7cc..385634738f9 100644 --- a/Mage.Server/src/main/java/mage/server/TableManager.java +++ b/Mage.Server/src/main/java/mage/server/TableManager.java @@ -395,8 +395,6 @@ public class TableManager { debugServerState(); } logger.debug("TABLE HEALTH CHECK"); - List toRemove = new ArrayList<>(); - ArrayList tableCopy = new ArrayList<>(); tableCopy.addAll(tables.values()); for (Table table : tableCopy) { @@ -406,13 +404,13 @@ public class TableManager { logger.debug(table.getId() + " [" + table.getName()+ "] " + formatter.format(table.getStartTime() == null ? table.getCreateTime() : table.getCreateTime()) +" (" + table.getState().toString() + ") " + (table.isTournament() ? "- Tournament":"")); TableController tableController = getController(table.getId()); if (tableController != null) { - if (table.isTournament()) { - if (!tableController.isTournamentStillValid()) { - toRemove.add(table.getId()); - } - } else { - if (!tableController.isMatchTableStillValid()) { - toRemove.add(table.getId()); + if ((table.isTournament() && !tableController.isTournamentStillValid()) || + (!table.isTournament() && !tableController.isMatchTableStillValid())) { + try { + logger.warn("Removing unhealthy tableId " + table.getId()); + removeTable(table.getId()); + } catch (Exception e) { + logger.error(e); } } } @@ -422,14 +420,6 @@ public class TableManager { logger.debug(Arrays.toString(ex.getStackTrace())); } } - for (UUID tableId : toRemove) { - try { - logger.warn("Removing unhealthy tableId " + tableId); - removeTable(tableId); - } catch (Exception e) { - logger.error(e); - } - } logger.debug("TABLE HEALTH CHECK - END"); } diff --git a/Mage.Server/src/main/java/mage/server/game/GameWorker.java b/Mage.Server/src/main/java/mage/server/game/GameWorker.java index 16e785604f1..7a1ae9753c3 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameWorker.java +++ b/Mage.Server/src/main/java/mage/server/game/GameWorker.java @@ -62,7 +62,7 @@ public class GameWorker implements Callable { gameController.gameResult(game.getWinner()); game.cleanUp(); } catch (MageException ex) { - logger.fatal("GameWorker mage error [" + game.getId() + "]" +ex, ex); + logger.fatal("GameWorker mage error [" + game.getId() + "]" + ex, ex); ex.printStackTrace(); } catch (Exception e) { logger.fatal("GameWorker general exception [" + game.getId() + "]" + e.getMessage(), e); diff --git a/Mage.Sets/src/mage/sets/DragonsOfTarkir.java b/Mage.Sets/src/mage/sets/DragonsOfTarkir.java new file mode 100644 index 00000000000..d44ef26f23e --- /dev/null +++ b/Mage.Sets/src/mage/sets/DragonsOfTarkir.java @@ -0,0 +1,60 @@ +/* + * 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.sets; + +import java.util.GregorianCalendar; +import mage.cards.ExpansionSet; +import mage.constants.SetType; + +/** + * + * @author fireshoes + */ + +public class DragonsOfTarkir extends ExpansionSet { + + private static final DragonsOfTarkir fINSTANCE = new DragonsOfTarkir(); + + public static DragonsOfTarkir getInstance() { + return fINSTANCE; + } + + private DragonsOfTarkir() { + super("Dragons of Tarkir", "DTK", "mage.sets.dragonsoftarkir", new GregorianCalendar(2015, 3, 27).getTime(), SetType.EXPANSION); + this.blockName = "Dragons of Tarkir"; + this.hasBoosters = true; + this.hasBasicLands = true; + this.numBoosterLands = 1; + this.numBoosterCommon = 10; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 8; + } + +} diff --git a/Mage.Sets/src/mage/sets/ajanivsnicolbolas/SapseepForest.java b/Mage.Sets/src/mage/sets/ajanivsnicolbolas/SapseepForest.java new file mode 100644 index 00000000000..88947e98cf5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/ajanivsnicolbolas/SapseepForest.java @@ -0,0 +1,52 @@ +/* + * 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.sets.ajanivsnicolbolas; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class SapseepForest extends mage.sets.shadowmoor.SapseepForest { + + public SapseepForest(UUID ownerId) { + super(ownerId); + this.cardNumber = 36; + this.expansionSetCode = "DDH"; + } + + public SapseepForest(final SapseepForest card) { + super(card); + } + + @Override + public SapseepForest copy() { + return new SapseepForest(this); + } +} diff --git a/Mage.Sets/src/mage/sets/alarareborn/DefilerOfSouls.java b/Mage.Sets/src/mage/sets/alarareborn/DefilerOfSouls.java index a963945ba80..8a6afe04c35 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/DefilerOfSouls.java +++ b/Mage.Sets/src/mage/sets/alarareborn/DefilerOfSouls.java @@ -36,9 +36,8 @@ import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.MonocoloredPredicate; -import mage.filter.predicate.permanent.ControllerPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -91,12 +90,11 @@ class DefilerOfSoulsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - FilterCreaturePermanent filter = new FilterCreaturePermanent("monocolored creature"); + FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("monocolored creature"); Player player = game.getPlayer(targetPointer.getFirst(game, source)); if (player == null) { return false; } - filter.add(new ControllerPredicate(TargetController.YOU)); filter.add(new MonocoloredPredicate()); int amount; diff --git a/Mage.Sets/src/mage/sets/alarareborn/MaelstromNexus.java b/Mage.Sets/src/mage/sets/alarareborn/MaelstromNexus.java index 4577cf84e1c..26c7043aec0 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/MaelstromNexus.java +++ b/Mage.Sets/src/mage/sets/alarareborn/MaelstromNexus.java @@ -63,8 +63,7 @@ public class MaelstromNexus extends CardImpl { // The first spell you cast each turn has cascade. - this.addAbility(new MaelstromNexusTriggeredAbility()); - this.addWatcher(new FirstSpellCastThisTurnWatcher()); + this.addAbility(new MaelstromNexusTriggeredAbility(), new FirstSpellCastThisTurnWatcher()); } diff --git a/Mage.Sets/src/mage/sets/alarareborn/PredatoryAdvantage.java b/Mage.Sets/src/mage/sets/alarareborn/PredatoryAdvantage.java index 8e366c2bd1d..db1ac697415 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/PredatoryAdvantage.java +++ b/Mage.Sets/src/mage/sets/alarareborn/PredatoryAdvantage.java @@ -61,8 +61,7 @@ public class PredatoryAdvantage extends CardImpl { // At the beginning of each opponent's end step, if that player didn't cast a creature spell this turn, put a 2/2 green Lizard creature token onto the battlefield. - this.addWatcher(new CastCreatureWatcher()); - this.addAbility(new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new LizardToken()), TargetController.OPPONENT, new DidNotCastCreatureCondition(), false)); + this.addAbility(new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new LizardToken()), TargetController.OPPONENT, new DidNotCastCreatureCondition(), false), new CastCreatureWatcher()); } public PredatoryAdvantage(final PredatoryAdvantage card) { diff --git a/Mage.Sets/src/mage/sets/alarareborn/TimeSieve.java b/Mage.Sets/src/mage/sets/alarareborn/TimeSieve.java index 1796af08d14..ed18c9a367c 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/TimeSieve.java +++ b/Mage.Sets/src/mage/sets/alarareborn/TimeSieve.java @@ -37,7 +37,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.common.FilterArtifactPermanent; +import mage.filter.common.FilterControlledArtifactPermanent; import mage.target.common.TargetControlledPermanent; /** @@ -50,12 +50,9 @@ public class TimeSieve extends CardImpl { super(ownerId, 31, "Time Sieve", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{U}{B}"); this.expansionSetCode = "ARB"; - - - // {tap}, Sacrifice five artifacts: Take an extra turn after this one. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddExtraTurnControllerEffect(), new TapSourceCost()); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(5, 5, new FilterArtifactPermanent("five artifacts"), true))); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(5, 5, new FilterControlledArtifactPermanent("five artifacts"), true))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/alarareborn/UnscytheKillerOfKings.java b/Mage.Sets/src/mage/sets/alarareborn/UnscytheKillerOfKings.java index f023f51eecf..593f2f6caf6 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/UnscytheKillerOfKings.java +++ b/Mage.Sets/src/mage/sets/alarareborn/UnscytheKillerOfKings.java @@ -79,8 +79,7 @@ public class UnscytheKillerOfKings extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.EQUIPMENT))); // Whenever a creature dealt damage by equipped creature this turn dies, you may exile that card. If you do, put a 2/2 black Zombie creature token onto the battlefield. - this.addAbility(new UnscytheKillerOfKingsTriggeredAbility(new UnscytheEffect())); - this.addWatcher(new EquippedDidDamageWatcher()); + this.addAbility(new UnscytheKillerOfKingsTriggeredAbility(new UnscytheEffect()), new EquippedDidDamageWatcher()); // Equip {2} this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(2), new TargetControlledCreaturePermanent())); diff --git a/Mage.Sets/src/mage/sets/antiquities/CircleOfProtectionArtifacts.java b/Mage.Sets/src/mage/sets/antiquities/CircleOfProtectionArtifacts.java new file mode 100644 index 00000000000..2bfcc5801ed --- /dev/null +++ b/Mage.Sets/src/mage/sets/antiquities/CircleOfProtectionArtifacts.java @@ -0,0 +1,53 @@ +/* + * 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.sets.antiquities; + +import java.util.UUID; + + +/** + * + * @author Plopman + */ +public class CircleOfProtectionArtifacts extends mage.sets.fifthdawn.CircleOfProtectionArtifacts { + + public CircleOfProtectionArtifacts(UUID ownerId) { + super(ownerId); + this.cardNumber = 97; + this.expansionSetCode = "ATQ"; + } + + public CircleOfProtectionArtifacts(final CircleOfProtectionArtifacts card) { + super(card); + } + + @Override + public CircleOfProtectionArtifacts copy() { + return new CircleOfProtectionArtifacts(this); + } +} diff --git a/Mage.Sets/src/mage/sets/archenemy/TorrentOfSouls.java b/Mage.Sets/src/mage/sets/archenemy/TorrentOfSouls.java new file mode 100644 index 00000000000..186818a4775 --- /dev/null +++ b/Mage.Sets/src/mage/sets/archenemy/TorrentOfSouls.java @@ -0,0 +1,52 @@ +/* + * 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.sets.archenemy; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class TorrentOfSouls extends mage.sets.shadowmoor.TorrentOfSouls { + + public TorrentOfSouls(UUID ownerId) { + super(ownerId); + this.cardNumber = 96; + this.expansionSetCode = "ARC"; + } + + public TorrentOfSouls(final TorrentOfSouls card) { + super(card); + } + + @Override + public TorrentOfSouls copy() { + return new TorrentOfSouls(this); + } +} diff --git a/Mage.Sets/src/mage/sets/avacynrestored/Aggravate.java b/Mage.Sets/src/mage/sets/avacynrestored/Aggravate.java index 39f7d99f5e8..8cc82ddd44b 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/Aggravate.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/Aggravate.java @@ -62,7 +62,7 @@ public class Aggravate extends CardImpl { this.getSpellAbility().addTarget(new TargetPlayer()); // Each creature dealt damage this way attacks this turn if able. this.getSpellAbility().addEffect(new AggravateRequirementEffect()); - this.addWatcher(new DamagedByWatcher()); + this.getSpellAbility().addWatcher(new DamagedByWatcher()); } public Aggravate(final Aggravate card) { diff --git a/Mage.Sets/src/mage/sets/avacynrestored/CavernOfSouls.java b/Mage.Sets/src/mage/sets/avacynrestored/CavernOfSouls.java index 6a58f6f827c..b30f96371ff 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/CavernOfSouls.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/CavernOfSouls.java @@ -36,6 +36,7 @@ import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.ContinuousRuleModifiyingEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.mana.ColorlessManaAbility; @@ -79,8 +80,7 @@ public class CavernOfSouls extends CardImpl { this.addAbility(new ColorlessManaAbility()); // {T}: Add one mana of any color to your mana pool. Spend this mana only to cast a creature spell of the chosen type, and that spell can't be countered. - this.addAbility(new ConditionalAnyColorManaAbility(1, new CavernOfSoulsManaBuilder())); - this.addWatcher(new CavernOfSoulsWatcher()); + this.addAbility(new ConditionalAnyColorManaAbility(new TapSourceCost(), 1, new CavernOfSoulsManaBuilder(), true), new CavernOfSoulsWatcher()); this.addAbility(new SimpleStaticAbility(Zone.ALL, new CavernOfSoulsCantCounterEffect())); } diff --git a/Mage.Sets/src/mage/sets/avacynrestored/PillarOfFlame.java b/Mage.Sets/src/mage/sets/avacynrestored/PillarOfFlame.java index e78afa5963e..c406072413d 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/PillarOfFlame.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/PillarOfFlame.java @@ -62,6 +62,7 @@ public class PillarOfFlame extends CardImpl { this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); // If a creature dealt damage this way would die this turn, exile it instead. this.getSpellAbility().addEffect(new DealtDamageToCreatureBySourceDies(this, Duration.EndOfTurn)); + this.getSpellAbility().addWatcher(new DamagedByWatcher()); } public PillarOfFlame(final PillarOfFlame card) { diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/GoblinCohort.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/GoblinCohort.java index ee8fa8cfcc4..0020b71fe7d 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/GoblinCohort.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/GoblinCohort.java @@ -63,8 +63,7 @@ public class GoblinCohort extends CardImpl { this.toughness = new MageInt(2); // Goblin Cohort can't attack unless you've cast a creature spell this turn. - this.addWatcher(new PlayerCastCreatureWatcher()); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GoblinCohortEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GoblinCohortEffect()), new PlayerCastCreatureWatcher()); } diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/KumanosBlessing.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/KumanosBlessing.java index f34d4711a08..440717fb7c6 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/KumanosBlessing.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/KumanosBlessing.java @@ -77,8 +77,8 @@ public class KumanosBlessing extends CardImpl { this.addAbility(ability); // If a creature dealt damage by enchanted creature this turn would die, exile it instead. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KumanosBlessingEffect())); - this.addWatcher(new DamagedByEnchantedWatcher()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KumanosBlessingEffect()), new DamagedByEnchantedWatcher()); + } public KumanosBlessing(final KumanosBlessing card) { diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/Lifespinner.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/Lifespinner.java index 680a7b39179..80c0a367d11 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/Lifespinner.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/Lifespinner.java @@ -37,7 +37,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterPermanentCard; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.filter.predicate.mageobject.SupertypePredicate; @@ -70,7 +70,7 @@ public class Lifespinner extends CardImpl { SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter)), new TapSourceCost()); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(3, 3, new FilterCreaturePermanent("Spirit", "three Spirits"), false))); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(3, 3, new FilterControlledCreaturePermanent("Spirit", "three Spirits"), false))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/TorrentOfStone.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/TorrentOfStone.java index 6c148278bc0..ab7c99d6365 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/TorrentOfStone.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/TorrentOfStone.java @@ -34,7 +34,7 @@ import mage.abilities.keyword.SpliceOntoArcaneAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.common.FilterLandPermanent; +import mage.filter.common.FilterControlledLandPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanent; @@ -44,7 +44,7 @@ import mage.target.common.TargetCreaturePermanent; * @author LevelX2 */ public class TorrentOfStone extends CardImpl { - private static final FilterLandPermanent filterSacrifice = new FilterLandPermanent("two Mountains"); + private static final FilterControlledLandPermanent filterSacrifice = new FilterControlledLandPermanent("two Mountains"); static { filterSacrifice.add(new SubtypePredicate("Mountain")); diff --git a/Mage.Sets/src/mage/sets/bornofthegods/EpharaGodOfThePolis.java b/Mage.Sets/src/mage/sets/bornofthegods/EpharaGodOfThePolis.java index 3164e82d0fe..638e15f8af7 100644 --- a/Mage.Sets/src/mage/sets/bornofthegods/EpharaGodOfThePolis.java +++ b/Mage.Sets/src/mage/sets/bornofthegods/EpharaGodOfThePolis.java @@ -78,8 +78,8 @@ public class EpharaGodOfThePolis extends CardImpl { this.addAbility(new ConditionalTriggeredAbility( new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), TargetController.ANY, false), HadAnotherCreatureEnterTheBattlefieldCondition.getInstance(), - "At the beginning of each upkeep, if you had another creature enter the battlefield under your control last turn, draw a card.")); - this.addWatcher(new CreatureEnteredBattlefieldLastTurnWatcher()); + "At the beginning of each upkeep, if you had another creature enter the battlefield under your control last turn, draw a card."), + new CreatureEnteredBattlefieldLastTurnWatcher()); } diff --git a/Mage.Sets/src/mage/sets/bornofthegods/SearingBlood.java b/Mage.Sets/src/mage/sets/bornofthegods/SearingBlood.java index 3efc367388b..8f6456f0068 100644 --- a/Mage.Sets/src/mage/sets/bornofthegods/SearingBlood.java +++ b/Mage.Sets/src/mage/sets/bornofthegods/SearingBlood.java @@ -28,6 +28,7 @@ package mage.sets.bornofthegods; import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; @@ -162,6 +163,7 @@ class SearingBloodDelayedEffect extends OneShotEffect { if (permanent != null) { Player player = game.getPlayer(permanent.getControllerId()); if (player != null) { + MageObject sourceObject = source.getSourceObject(game); player.damage(3, source.getSourceId(), game, false, true); return true; } diff --git a/Mage.Sets/src/mage/sets/bornofthegods/SpiritOfTheLabyrinth.java b/Mage.Sets/src/mage/sets/bornofthegods/SpiritOfTheLabyrinth.java index 8e76586676a..6ef2615db91 100644 --- a/Mage.Sets/src/mage/sets/bornofthegods/SpiritOfTheLabyrinth.java +++ b/Mage.Sets/src/mage/sets/bornofthegods/SpiritOfTheLabyrinth.java @@ -60,8 +60,7 @@ public class SpiritOfTheLabyrinth extends CardImpl { this.toughness = new MageInt(1); // Each player can't draw more than one card each turn. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpiritOfTheLabyrinthEffect())); - this.addWatcher(new SpiritOfTheLabyrinthWatcher()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpiritOfTheLabyrinthEffect()), new SpiritOfTheLabyrinthWatcher()); } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/AshenSkinZubera.java b/Mage.Sets/src/mage/sets/championsofkamigawa/AshenSkinZubera.java index 7e1a8cce88c..007987b6a2a 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/AshenSkinZubera.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/AshenSkinZubera.java @@ -54,8 +54,7 @@ public class AshenSkinZubera extends CardImpl { this.toughness = new MageInt(2); Ability ability = new DiesTriggeredAbility(new DiscardTargetEffect(new ZuberasDiedDynamicValue())); ability.addTarget(new TargetOpponent()); - this.addAbility(ability); - this.addWatcher(new ZuberasDiedWatcher()); + this.addAbility(ability, new ZuberasDiedWatcher()); } public AshenSkinZubera(final AshenSkinZubera card) { diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/BoseijuWhoSheltersAll.java b/Mage.Sets/src/mage/sets/championsofkamigawa/BoseijuWhoSheltersAll.java index fccbae3399a..0106881bacc 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/BoseijuWhoSheltersAll.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/BoseijuWhoSheltersAll.java @@ -75,8 +75,7 @@ public class BoseijuWhoSheltersAll extends CardImpl { ability.getEffects().get(0).setText("Add {1} to your mana pool. If that mana is spent on an instant or sorcery spell, that spell can't be countered by spells or abilities"); this.addAbility(ability); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoseijuWhoSheltersAllCantCounterEffect())); - this.addWatcher(new BoseijuWhoSheltersAllWatcher()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoseijuWhoSheltersAllCantCounterEffect()), new BoseijuWhoSheltersAllWatcher()); } public BoseijuWhoSheltersAll(final BoseijuWhoSheltersAll card) { diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/DrippingTongueZubera.java b/Mage.Sets/src/mage/sets/championsofkamigawa/DrippingTongueZubera.java index a3fa0cf25eb..bb22a0a3ff2 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/DrippingTongueZubera.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/DrippingTongueZubera.java @@ -51,8 +51,7 @@ public class DrippingTongueZubera extends CardImpl { this.color.setGreen(true); this.power = new MageInt(1); this.toughness = new MageInt(2); - this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new SpiritToken(), new ZuberasDiedDynamicValue()), false)); - this.addWatcher(new ZuberasDiedWatcher()); + this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new SpiritToken(), new ZuberasDiedDynamicValue()), false), new ZuberasDiedWatcher()); } public DrippingTongueZubera (final DrippingTongueZubera card) { diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/EmberFistZubera.java b/Mage.Sets/src/mage/sets/championsofkamigawa/EmberFistZubera.java index e69b26c4c59..7478c844f2f 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/EmberFistZubera.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/EmberFistZubera.java @@ -55,8 +55,7 @@ public class EmberFistZubera extends CardImpl { this.toughness = new MageInt(2); Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(new ZuberasDiedDynamicValue())); ability.addTarget(new TargetCreatureOrPlayer()); - this.addAbility(ability); - this.addWatcher(new ZuberasDiedWatcher()); + this.addAbility(ability, new ZuberasDiedWatcher()); } public EmberFistZubera (final EmberFistZubera card) { diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/FeastOfWorms.java b/Mage.Sets/src/mage/sets/championsofkamigawa/FeastOfWorms.java index 2f2f00a5193..112c298cc8f 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/FeastOfWorms.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/FeastOfWorms.java @@ -32,13 +32,12 @@ import java.util.UUID; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.Zone; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterLandPermanent; +import mage.filter.common.FilterControlledLandPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -93,22 +92,24 @@ class FeastOfWormsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = (Permanent) game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD); - Player targetPlayer = game.getPlayer(permanent.getControllerId()); - if (targetPlayer != null && permanent != null && (permanent.getSupertype().get(0).toString().equals("Legendary"))) { - FilterPermanent filter = new FilterLandPermanent("land to sacrifice"); - filter.add(new ControllerIdPredicate(targetPlayer.getId())); - TargetControlledPermanent target = new TargetControlledPermanent(1, 1, filter, false); + Permanent permanent = game.getPermanentOrLKIBattlefield(id); + Player targetPlayer = null; + if (permanent != null) { + targetPlayer = game.getPlayer(permanent.getControllerId()); + } + if (targetPlayer != null && permanent != null && (permanent.getSupertype().get(0).equals("Legendary"))) { + FilterControlledPermanent filter = new FilterControlledLandPermanent("land to sacrifice"); + filter.add(new ControllerIdPredicate(targetPlayer.getId())); + TargetControlledPermanent target = new TargetControlledPermanent(1, 1, filter, false); - if (target.canChoose(targetPlayer.getId(), game)) { - targetPlayer.choose(Outcome.Sacrifice, target, source.getSourceId(), game); - - Permanent land = game.getPermanent(target.getFirstTarget()); - if (land != null) { - return land.sacrifice(source.getSourceId(), game); - } - return true; + if (target.canChoose(targetPlayer.getId(), game)) { + targetPlayer.chooseTarget(Outcome.Sacrifice, target, source, game); + Permanent land = game.getPermanent(target.getFirstTarget()); + if (land != null) { + land.sacrifice(source.getSourceId(), game); } + } + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/FloatingDreamZubera.java b/Mage.Sets/src/mage/sets/championsofkamigawa/FloatingDreamZubera.java index 025131fdc53..d5df6a4a5bf 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/FloatingDreamZubera.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/FloatingDreamZubera.java @@ -60,8 +60,7 @@ public class FloatingDreamZubera extends CardImpl { this.color.setBlue(true); this.power = new MageInt(1); this.toughness = new MageInt(2); - this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(new ZuberasDiedDynamicValue()))); - this.addWatcher(new ZuberasDiedWatcher()); + this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(new ZuberasDiedDynamicValue())), new ZuberasDiedWatcher()); } public FloatingDreamZubera(final FloatingDreamZubera card) { diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/Frostwielder.java b/Mage.Sets/src/mage/sets/championsofkamigawa/Frostwielder.java index 875ee4a044e..bc3c33da535 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/Frostwielder.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/Frostwielder.java @@ -42,6 +42,7 @@ import mage.constants.Duration; import mage.constants.Rarity; import mage.constants.Zone; import mage.target.common.TargetCreatureOrPlayer; +import mage.watchers.common.DamagedByWatcher; /** * @author LevelX @@ -62,7 +63,7 @@ public class Frostwielder extends CardImpl { ability.addTarget(new TargetCreatureOrPlayer()); this.addAbility(ability); // If a creature dealt damage by Frostwielder this turn would die, exile it instead. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DealtDamageToCreatureBySourceDies(this, Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DealtDamageToCreatureBySourceDies(this, Duration.WhileOnBattlefield)), new DamagedByWatcher()); } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/HallOfTheBanditLord.java b/Mage.Sets/src/mage/sets/championsofkamigawa/HallOfTheBanditLord.java index 9adb6f82e8e..e1c677e5385 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/HallOfTheBanditLord.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/HallOfTheBanditLord.java @@ -77,9 +77,7 @@ public class HallOfTheBanditLord extends CardImpl { effect.setText("Add {1} to your mana pool. If that mana is spent on a creature spell, it gains haste"); Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); ability.addCost(new PayLifeCost(3)); - this.addAbility(ability); - - this.addWatcher(new HallOfTheBanditLordWatcher(ability)); + this.addAbility(ability, new HallOfTheBanditLordWatcher(ability)); } public HallOfTheBanditLord(final HallOfTheBanditLord card) { diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/KumanoMasterYamabushi.java b/Mage.Sets/src/mage/sets/championsofkamigawa/KumanoMasterYamabushi.java index 2066dbe12fb..1a79d2e802a 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/KumanoMasterYamabushi.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/KumanoMasterYamabushi.java @@ -44,6 +44,7 @@ import mage.constants.Duration; import mage.constants.Rarity; import mage.constants.Zone; import mage.target.common.TargetCreatureOrPlayer; +import mage.watchers.common.DamagedByWatcher; /** * @author LevelX @@ -65,7 +66,7 @@ public class KumanoMasterYamabushi extends CardImpl { ability.addTarget(new TargetCreatureOrPlayer()); this.addAbility(ability); // If a creature dealt damage by Kumano this turn would die, exile it instead. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DealtDamageToCreatureBySourceDies(this, Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DealtDamageToCreatureBySourceDies(this, Duration.WhileOnBattlefield)), new DamagedByWatcher()); } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/KumanosPupils.java b/Mage.Sets/src/mage/sets/championsofkamigawa/KumanosPupils.java index 5fc1e349a0c..33bd9a6e64c 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/KumanosPupils.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/KumanosPupils.java @@ -37,6 +37,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; import mage.constants.Zone; +import mage.watchers.common.DamagedByWatcher; /** * @author LevelX @@ -53,7 +54,7 @@ public class KumanosPupils extends CardImpl { this.toughness = new MageInt(3); // If a creature dealt damage by Kumano's Pupils this turn would die, exile it instead. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DealtDamageToCreatureBySourceDies(this, Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DealtDamageToCreatureBySourceDies(this, Duration.WhileOnBattlefield)), new DamagedByWatcher()); } public KumanosPupils(final KumanosPupils card) { diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfCleansingFire.java b/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfCleansingFire.java index 6564065483d..6122b46af5c 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfCleansingFire.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfCleansingFire.java @@ -72,7 +72,7 @@ public class MyojinOfCleansingFire extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(6); - this.addWatcher(new CastFromHandWatcher()); + this.getSpellAbility().addWatcher(new CastFromHandWatcher()); // Myojin of Cleansing Fire enters the battlefield with a divinity counter on it if you cast it from your hand. this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), new CastFromHandCondition(), ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand")); diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfInfiniteRage.java b/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfInfiniteRage.java index 3a07bc0b21b..9c36ba1ce92 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfInfiniteRage.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfInfiniteRage.java @@ -68,7 +68,7 @@ public class MyojinOfInfiniteRage extends CardImpl { this.power = new MageInt(7); this.toughness = new MageInt(4); - this.addWatcher(new CastFromHandWatcher()); + this.getSpellAbility().addWatcher(new CastFromHandWatcher()); // Myojin of Infinite Rage enters the battlefield with a divinity counter on it if you cast it from your hand. this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), new CastFromHandCondition(), ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand")); diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfLifesWeb.java b/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfLifesWeb.java index 87f6211e315..d69c8927f52 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfLifesWeb.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfLifesWeb.java @@ -73,7 +73,7 @@ public class MyojinOfLifesWeb extends CardImpl { this.power = new MageInt(8); this.toughness = new MageInt(8); - this.addWatcher(new CastFromHandWatcher()); + this.getSpellAbility().addWatcher(new CastFromHandWatcher()); // Myojin of Life's Web enters the battlefield with a divinity counter on it if you cast it from your hand. this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), new CastFromHandCondition(), ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand")); diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfNightsReach.java b/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfNightsReach.java index bd545a53432..aa521f14bd0 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfNightsReach.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfNightsReach.java @@ -66,7 +66,7 @@ public class MyojinOfNightsReach extends CardImpl { this.power = new MageInt(5); this.toughness = new MageInt(2); - this.addWatcher(new CastFromHandWatcher()); + this.getSpellAbility().addWatcher(new CastFromHandWatcher()); // Myojin of Night's Reach enters the battlefield with a divinity counter on it if you cast it from your hand. this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), new CastFromHandCondition(), ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand")); diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfSeeingWinds.java b/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfSeeingWinds.java index f9f81b2bd28..d70bc58ba31 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfSeeingWinds.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfSeeingWinds.java @@ -71,7 +71,7 @@ public class MyojinOfSeeingWinds extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - this.addWatcher(new CastFromHandWatcher()); + this.getSpellAbility().addWatcher(new CastFromHandWatcher()); // Myojin of Seeing Winds enters the battlefield with a divinity counter on it if you cast it from your hand. this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), new CastFromHandCondition(), ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand")); diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/ShimatsuTheBloodcloaked.java b/Mage.Sets/src/mage/sets/championsofkamigawa/ShimatsuTheBloodcloaked.java index d12185583cc..00e064850ed 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/ShimatsuTheBloodcloaked.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/ShimatsuTheBloodcloaked.java @@ -36,7 +36,7 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.CardImpl; import mage.counters.CounterType; -import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -106,7 +106,7 @@ class ShimatsuTheBloodcloakedEffect extends ReplacementEffectImpl { Permanent creature = game.getPermanent(event.getTargetId()); Player controller = game.getPlayer(source.getControllerId()); if (creature != null && controller != null) { - Target target = new TargetControlledPermanent(0, Integer.MAX_VALUE, new FilterPermanent(), true); + Target target = new TargetControlledPermanent(0, Integer.MAX_VALUE, new FilterControlledPermanent(), true); if (!target.canChoose(source.getSourceId(), source.getControllerId(), game)) { return false; } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/SiftThroughSands.java b/Mage.Sets/src/mage/sets/championsofkamigawa/SiftThroughSands.java index 410ad49d6aa..f6050c8ffd5 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/SiftThroughSands.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/SiftThroughSands.java @@ -74,7 +74,7 @@ public class SiftThroughSands extends CardImpl { this.getSpellAbility().addEffect(effect); // If you've cast a spell named Peer Through Depths and a spell named Reach Through Mists this turn, you may search your library for a card named The Unspeakable, put it onto the battlefield, then shuffle your library. this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter), false, true), new SiftThroughSandsCondition(), rule)); - this.addWatcher(new SiftThroughSandsWatcher()); + this.getSpellAbility().addWatcher(new SiftThroughSandsWatcher()); } public SiftThroughSands(final SiftThroughSands card) { diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/SilentChantZubera.java b/Mage.Sets/src/mage/sets/championsofkamigawa/SilentChantZubera.java index 3fe88b5ad73..ef1d951c6cf 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/SilentChantZubera.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/SilentChantZubera.java @@ -56,8 +56,7 @@ public class SilentChantZubera extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(2); Ability ability = new DiesTriggeredAbility(new GainLifeEffect(new SilentChantZuberaDynamicValue())); - this.addAbility(ability); - this.addWatcher(new ZuberasDiedWatcher()); + this.addAbility(ability, new ZuberasDiedWatcher()); } public SilentChantZubera (final SilentChantZubera card) { diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/UyoSilentProphet.java b/Mage.Sets/src/mage/sets/championsofkamigawa/UyoSilentProphet.java index 8a639f0222b..0610678e95e 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/UyoSilentProphet.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/UyoSilentProphet.java @@ -41,7 +41,7 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.constants.Zone; import mage.filter.FilterSpell; -import mage.filter.common.FilterLandPermanent; +import mage.filter.common.FilterControlledLandPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.target.TargetSpell; @@ -67,7 +67,7 @@ public class UyoSilentProphet extends CardImpl { this.supertype.add("Legendary"); this.subtype.add("Moonfolk"); this.subtype.add("Wizard"); - this.color.setBlue(true); + this.power = new MageInt(4); this.toughness = new MageInt(4); @@ -75,7 +75,7 @@ public class UyoSilentProphet extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // {2}, Return two lands you control to their owner's hand: Copy target instant or sorcery spell. You may choose new targets for the copy. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CopyTargetSpellEffect(), new GenericManaCost(2)); - ability.addCost(new ReturnToHandTargetCost(new TargetControlledPermanent(2, 2, new FilterLandPermanent("lands"), false))); + ability.addCost(new ReturnToHandTargetCost(new TargetControlledPermanent(2, 2, new FilterControlledLandPermanent("lands"), false))); ability.addTarget(new TargetSpell(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/YamabushisFlame.java b/Mage.Sets/src/mage/sets/championsofkamigawa/YamabushisFlame.java index 5d51d1e5344..800169ca290 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/YamabushisFlame.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/YamabushisFlame.java @@ -37,6 +37,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; import mage.target.common.TargetCreatureOrPlayer; +import mage.watchers.common.DamagedByWatcher; /** * @@ -55,6 +56,7 @@ public class YamabushisFlame extends CardImpl { // If a creature dealt damage this way would die this turn, exile it instead. this.getSpellAbility().addEffect(new DealtDamageToCreatureBySourceDies(this, Duration.EndOfTurn)); + this.getSpellAbility().addWatcher(new DamagedByWatcher()); } public YamabushisFlame(final YamabushisFlame card) { diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/YamabushisStorm.java b/Mage.Sets/src/mage/sets/championsofkamigawa/YamabushisStorm.java index e271fcedbb3..d7bc5475aa6 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/YamabushisStorm.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/YamabushisStorm.java @@ -63,6 +63,7 @@ public class YamabushisStorm extends CardImpl { this.getSpellAbility().addEffect(new DamageAllEffect(1, new FilterCreaturePermanent())); // If a creature dealt damage this way would die this turn, exile it instead. this.getSpellAbility().addEffect(new DealtDamageToCreatureBySourceDies(this, Duration.EndOfTurn)); + this.getSpellAbility().addWatcher(new DamagedByWatcher()); } public YamabushisStorm(final YamabushisStorm card) { diff --git a/Mage.Sets/src/mage/sets/coldsnap/FuryOfTheHorde.java b/Mage.Sets/src/mage/sets/coldsnap/FuryOfTheHorde.java index 6210b029408..05e298500b4 100644 --- a/Mage.Sets/src/mage/sets/coldsnap/FuryOfTheHorde.java +++ b/Mage.Sets/src/mage/sets/coldsnap/FuryOfTheHorde.java @@ -75,7 +75,7 @@ public class FuryOfTheHorde extends CardImpl { // Untap all creatures that attacked this turn. After this main phase, there is an additional combat phase followed by an additional main phase. this.getSpellAbility().addEffect(new FuryOfTheHordeUntapEffect()); this.getSpellAbility().addEffect(new FuryOfTheHordeAddPhasesEffect()); - this.addWatcher(new AttackedThisTurnWatcher()); + this.getSpellAbility().addWatcher(new AttackedThisTurnWatcher()); } diff --git a/Mage.Sets/src/mage/sets/coldsnap/LightningStorm.java b/Mage.Sets/src/mage/sets/coldsnap/LightningStorm.java index 7f52c687939..f537e0031be 100644 --- a/Mage.Sets/src/mage/sets/coldsnap/LightningStorm.java +++ b/Mage.Sets/src/mage/sets/coldsnap/LightningStorm.java @@ -100,7 +100,7 @@ class LightningStormCountCondition implements DynamicValue { public int calculate(Game game, Ability sourceAbility, Effect effect) { Spell spell = game.getStack().getSpell(sourceAbility.getSourceId()); if (spell != null) { - return spell.getCounters().getCount(counter) + 3; + return spell.getCounters(game).getCount(counter) + 3; } return 0; } diff --git a/Mage.Sets/src/mage/sets/commander/CourtHussar.java b/Mage.Sets/src/mage/sets/commander/CourtHussar.java index a3bf1cb6d04..0da7e0960ce 100644 --- a/Mage.Sets/src/mage/sets/commander/CourtHussar.java +++ b/Mage.Sets/src/mage/sets/commander/CourtHussar.java @@ -66,8 +66,7 @@ public class CourtHussar extends CardImpl { new LookLibraryAndPickControllerEffect(new StaticValue(3), false, new StaticValue(1), new FilterCard(), Zone.LIBRARY, false, false), false)); // When Court Hussar enters the battlefield, sacrifice it unless {W} was spent to cast it. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeSourceUnlessConditionEffect(new ManaWasSpentCondition(ColoredManaSymbol.W)), false)); - this.addWatcher(new ManaSpentToCastWatcher()); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeSourceUnlessConditionEffect(new ManaWasSpentCondition(ColoredManaSymbol.W)), false), new ManaSpentToCastWatcher()); } public CourtHussar(final CourtHussar card) { diff --git a/Mage.Sets/src/mage/sets/commander/KaradorGhostChieftain.java b/Mage.Sets/src/mage/sets/commander/KaradorGhostChieftain.java index 8144d887a8e..742bec871bb 100644 --- a/Mage.Sets/src/mage/sets/commander/KaradorGhostChieftain.java +++ b/Mage.Sets/src/mage/sets/commander/KaradorGhostChieftain.java @@ -82,8 +82,7 @@ public class KaradorGhostChieftain extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.STACK, new KaradorGhostChieftainCostReductionEffect())); // During each of your turns, you may cast one creature card from your graveyard. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KaradorGhostChieftainContinuousEffect())); - this.addWatcher(new KaradorGhostChieftainWatcher()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KaradorGhostChieftainContinuousEffect()), new KaradorGhostChieftainWatcher()); } public KaradorGhostChieftain(final KaradorGhostChieftain card) { diff --git a/Mage.Sets/src/mage/sets/commander2013/AzoriusHerald.java b/Mage.Sets/src/mage/sets/commander2013/AzoriusHerald.java index 0a852fa10f6..20226aef318 100644 --- a/Mage.Sets/src/mage/sets/commander2013/AzoriusHerald.java +++ b/Mage.Sets/src/mage/sets/commander2013/AzoriusHerald.java @@ -60,8 +60,7 @@ public class AzoriusHerald extends CardImpl { // When Azorius Herald enters the battlefield, you gain 4 life. this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(4))); // When Azorius Herald enters the battlefield, sacrifice it unless {U} was spent to cast it. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeSourceUnlessConditionEffect(new ManaWasSpentCondition(ColoredManaSymbol.U)), false)); - this.addWatcher(new ManaSpentToCastWatcher()); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeSourceUnlessConditionEffect(new ManaWasSpentCondition(ColoredManaSymbol.U)), false), new ManaSpentToCastWatcher()); } public AzoriusHerald(final AzoriusHerald card) { diff --git a/Mage.Sets/src/mage/sets/commander2013/FellShepherd.java b/Mage.Sets/src/mage/sets/commander2013/FellShepherd.java index 9dde2f1e310..c99ab4a9779 100644 --- a/Mage.Sets/src/mage/sets/commander2013/FellShepherd.java +++ b/Mage.Sets/src/mage/sets/commander2013/FellShepherd.java @@ -78,8 +78,7 @@ public class FellShepherd extends CardImpl { this.toughness = new MageInt(6); // Whenever Fell Shepherd deals combat damage to a player, you may return to your hand all creature cards that were put into your graveyard from the battlefield this turn. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new FellShepherdEffect(), true)); - this.addWatcher(new FellShepherdWatcher()); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new FellShepherdEffect(), true), new FellShepherdWatcher()); // {B}, Sacrifice another creature: Target creature gets -2/-2 until end of turn. SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(-2,-2, Duration.EndOfTurn), new ManaCostsImpl("{B}")); diff --git a/Mage.Sets/src/mage/sets/commander2013/JelevaNephaliasScourge.java b/Mage.Sets/src/mage/sets/commander2013/JelevaNephaliasScourge.java index 32b8642bee6..fb2afa2871a 100644 --- a/Mage.Sets/src/mage/sets/commander2013/JelevaNephaliasScourge.java +++ b/Mage.Sets/src/mage/sets/commander2013/JelevaNephaliasScourge.java @@ -76,8 +76,7 @@ public class JelevaNephaliasScourge extends CardImpl { // When Jeleva, Nephalia's Scourge enters the battlefield, each player exiles the top X cards of his or her library, where X is the amount of mana spent to cast Jeleva. this.addAbility(new EntersBattlefieldTriggeredAbility(new JelevaNephaliasScourgeEffect(), false)); // Whenever Jeleva attacks, you may cast an instant or sorcery card exiled with it without paying its mana cost. - this.addAbility(new AttacksTriggeredAbility(new JelevaNephaliasCastEffect(), false)); - this.addWatcher(new JelevaNephaliasWatcher()); + this.addAbility(new AttacksTriggeredAbility(new JelevaNephaliasCastEffect(), false), new JelevaNephaliasWatcher()); } diff --git a/Mage.Sets/src/mage/sets/commander2013/OpalPalace.java b/Mage.Sets/src/mage/sets/commander2013/OpalPalace.java index 3fc632ea513..739b8749bdc 100644 --- a/Mage.Sets/src/mage/sets/commander2013/OpalPalace.java +++ b/Mage.Sets/src/mage/sets/commander2013/OpalPalace.java @@ -75,13 +75,12 @@ public class OpalPalace extends CardImpl { // {1}, {tap}: Add to your mana pool one mana of any color in your commander's color identity. If you spend this mana to cast your commander, it enters the battlefield with a number of +1/+1 counters on it equal to the number of times it's been cast from the command zone this game. Ability ability = new CommanderColorIdentityManaAbility(new GenericManaCost(1)); ability.addCost(new TapSourceCost()); - this.addAbility(ability); + this.addAbility(ability, new OpalPalaceWatcher()); ability = new SimpleStaticAbility(Zone.ALL, new OpalPalaceEntersBattlefieldEffect()); ability.setRuleVisible(false); this.addAbility(ability); - this.addWatcher(new OpalPalaceWatcher()); } public OpalPalace(final OpalPalace card) { diff --git a/Mage.Sets/src/mage/sets/commander2013/PhantomNantuko.java b/Mage.Sets/src/mage/sets/commander2013/PhantomNantuko.java index ac58ae83140..72a68106def 100644 --- a/Mage.Sets/src/mage/sets/commander2013/PhantomNantuko.java +++ b/Mage.Sets/src/mage/sets/commander2013/PhantomNantuko.java @@ -68,7 +68,7 @@ public class PhantomNantuko extends CardImpl { // Trample this.addAbility(TrampleAbility.getInstance()); // Phantom Nantuko enters the battlefield with two +1/+1 counters on it. - this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(2), true))); + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(2), true), "with two +1/+1 counters on it")); // If damage would be dealt to Phantom Nantuko, prevent that damage. Remove a +1/+1 counter from Phantom Nantuko. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PhantomNantukoPreventionEffect())); // {tap}: Put a +1/+1 counter on Phantom Nantuko. diff --git a/Mage.Sets/src/mage/sets/commander2014/AngelOfTheDireHour.java b/Mage.Sets/src/mage/sets/commander2014/AngelOfTheDireHour.java index e28c8532144..470ffa048ee 100644 --- a/Mage.Sets/src/mage/sets/commander2014/AngelOfTheDireHour.java +++ b/Mage.Sets/src/mage/sets/commander2014/AngelOfTheDireHour.java @@ -65,8 +65,7 @@ public class AngelOfTheDireHour extends CardImpl { Ability ability = new EntersBattlefieldTriggeredAbility( new ConditionalOneShotEffect(new ExileAllEffect(new FilterAttackingCreature("attacking creatures")), new CastFromHandCondition(), " if you cast it from your hand, exile all attacking creatures")); - this.addAbility(ability); - this.addWatcher(new CastFromHandWatcher()); + this.addAbility(ability, new CastFromHandWatcher()); } public AngelOfTheDireHour(final AngelOfTheDireHour card) { diff --git a/Mage.Sets/src/mage/sets/commander2014/BreachingLeviathan.java b/Mage.Sets/src/mage/sets/commander2014/BreachingLeviathan.java index 50a3ef6c83b..8823ae47ccb 100644 --- a/Mage.Sets/src/mage/sets/commander2014/BreachingLeviathan.java +++ b/Mage.Sets/src/mage/sets/commander2014/BreachingLeviathan.java @@ -68,8 +68,7 @@ public class BreachingLeviathan extends CardImpl { Ability ability = new EntersBattlefieldTriggeredAbility( new ConditionalOneShotEffect(new BreachingLeviathanEffect(), new CastFromHandCondition(), "if you cast it from your hand, tap all nonblue creatures. Those creatures don't untap during their controllers' next untap steps")); - this.addAbility(ability); - this.addWatcher(new CastFromHandWatcher()); + this.addAbility(ability, new CastFromHandWatcher()); } public BreachingLeviathan(final BreachingLeviathan card) { diff --git a/Mage.Sets/src/mage/sets/commander2014/ContainmentPriest.java b/Mage.Sets/src/mage/sets/commander2014/ContainmentPriest.java index 119ebc74eb5..4066ad9af4d 100644 --- a/Mage.Sets/src/mage/sets/commander2014/ContainmentPriest.java +++ b/Mage.Sets/src/mage/sets/commander2014/ContainmentPriest.java @@ -72,8 +72,7 @@ public class ContainmentPriest extends CardImpl { // Flash this.addAbility(FlashAbility.getInstance()); // If a nontoken creature would enter the battlefield and it wasn't cast, exile it instead. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ContainmentPriestReplacementEffect())); - this.addWatcher(new CreatureCastWatcher()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ContainmentPriestReplacementEffect()), new CreatureCastWatcher()); } public ContainmentPriest(final ContainmentPriest card) { diff --git a/Mage.Sets/src/mage/sets/commander2014/ImpactResonance.java b/Mage.Sets/src/mage/sets/commander2014/ImpactResonance.java index 02264ea8032..b43d4b9de5d 100644 --- a/Mage.Sets/src/mage/sets/commander2014/ImpactResonance.java +++ b/Mage.Sets/src/mage/sets/commander2014/ImpactResonance.java @@ -62,7 +62,7 @@ public class ImpactResonance extends CardImpl { effect.setText("{this} deals X damage divided as you choose among any number of target creatures, where X is the greatest amount of damage dealt by a source to a permanent or player this turn"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(xValue)); - this.addWatcher(new GreatestAmountOfDamageWatcher()); + this.getSpellAbility().addWatcher(new GreatestAmountOfDamageWatcher()); } public ImpactResonance(final ImpactResonance card) { diff --git a/Mage.Sets/src/mage/sets/commander2014/SpoilsOfBlood.java b/Mage.Sets/src/mage/sets/commander2014/SpoilsOfBlood.java index d74c42ee96c..bdbc1cb3a4d 100644 --- a/Mage.Sets/src/mage/sets/commander2014/SpoilsOfBlood.java +++ b/Mage.Sets/src/mage/sets/commander2014/SpoilsOfBlood.java @@ -61,7 +61,7 @@ public class SpoilsOfBlood extends CardImpl { // Put an X/X black Horror creature token onto the battlefield, where X is the number of creatures that died this turn. this.getSpellAbility().addEffect(new SpoilsOfBloodEffect()); - this.addWatcher(new CreaturesDiedThisTurnWatcher()); + this.getSpellAbility().addWatcher(new CreaturesDiedThisTurnWatcher()); } public SpoilsOfBlood(final SpoilsOfBlood card) { diff --git a/Mage.Sets/src/mage/sets/darkascension/DungeonGeists.java b/Mage.Sets/src/mage/sets/darkascension/DungeonGeists.java index 65bef466025..573e3043158 100644 --- a/Mage.Sets/src/mage/sets/darkascension/DungeonGeists.java +++ b/Mage.Sets/src/mage/sets/darkascension/DungeonGeists.java @@ -81,9 +81,8 @@ public class DungeonGeists extends CardImpl { ability.addEffect(new DungeonGeistsEffect()); Target target = new TargetCreaturePermanent(filter); ability.addTarget(target); - this.addAbility(ability); + this.addAbility(ability, new DungeonGeistsWatcher()); // watcher needed to send normal events to Dungeon Geists ReplacementEffect - this.addWatcher(new DungeonGeistsWatcher()); } public DungeonGeists(final DungeonGeists card) { diff --git a/Mage.Sets/src/mage/sets/darksteel/Flamebreak.java b/Mage.Sets/src/mage/sets/darksteel/Flamebreak.java index 8728b230be0..5d61f0e9768 100644 --- a/Mage.Sets/src/mage/sets/darksteel/Flamebreak.java +++ b/Mage.Sets/src/mage/sets/darksteel/Flamebreak.java @@ -67,7 +67,7 @@ public class Flamebreak extends CardImpl { // Flamebreak deals 3 damage to each creature without flying and each player. Creatures dealt damage this way can't be regenerated this turn. this.getSpellAbility().addEffect(new DamageEverythingEffect(new StaticValue(3), filter1)); this.getSpellAbility().addEffect(new FlamebreakCantRegenerateEffect()); - this.addWatcher(new DamagedByWatcher()); + this.getSpellAbility().addWatcher(new DamagedByWatcher()); } public Flamebreak(final Flamebreak card) { diff --git a/Mage.Sets/src/mage/sets/dissension/CryptChampion.java b/Mage.Sets/src/mage/sets/dissension/CryptChampion.java index 0462c2cd705..f596d796793 100644 --- a/Mage.Sets/src/mage/sets/dissension/CryptChampion.java +++ b/Mage.Sets/src/mage/sets/dissension/CryptChampion.java @@ -75,8 +75,7 @@ public class CryptChampion extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new CryptChampionEffect())); // When Crypt Champion enters the battlefield, sacrifice it unless {R} was spent to cast it. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeSourceUnlessConditionEffect(new ManaWasSpentCondition(ColoredManaSymbol.R)), false)); - this.addWatcher(new ManaSpentToCastWatcher()); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeSourceUnlessConditionEffect(new ManaWasSpentCondition(ColoredManaSymbol.R)), false), new ManaSpentToCastWatcher()); } public CryptChampion(final CryptChampion card) { diff --git a/Mage.Sets/src/mage/sets/dissension/PatagiaViper.java b/Mage.Sets/src/mage/sets/dissension/PatagiaViper.java index 5c492ed8472..c707b3b4102 100644 --- a/Mage.Sets/src/mage/sets/dissension/PatagiaViper.java +++ b/Mage.Sets/src/mage/sets/dissension/PatagiaViper.java @@ -63,8 +63,7 @@ public class PatagiaViper extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new PatagiaViperSnakeToken(), 2), false)); // When Patagia Viper enters the battlefield, sacrifice it unless {U} was spent to cast it. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeSourceUnlessConditionEffect(new ManaWasSpentCondition(ColoredManaSymbol.U)), false)); - this.addWatcher(new ManaSpentToCastWatcher()); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeSourceUnlessConditionEffect(new ManaWasSpentCondition(ColoredManaSymbol.U)), false), new ManaSpentToCastWatcher()); } public PatagiaViper(final PatagiaViper card) { diff --git a/Mage.Sets/src/mage/sets/divinevsdemonic/ReiverDemon.java b/Mage.Sets/src/mage/sets/divinevsdemonic/ReiverDemon.java index e090fcabcd8..92b37c9990c 100644 --- a/Mage.Sets/src/mage/sets/divinevsdemonic/ReiverDemon.java +++ b/Mage.Sets/src/mage/sets/divinevsdemonic/ReiverDemon.java @@ -72,8 +72,7 @@ public class ReiverDemon extends CardImpl { Ability ability = new EntersBattlefieldTriggeredAbility( new ConditionalOneShotEffect(new DestroyAllEffect(filter), new CastFromHandCondition(), "if you cast it from your hand, destroy all nonartifact, nonblack creatures. They can't be regenerated")); - this.addAbility(ability); - this.addWatcher(new CastFromHandWatcher()); + this.addAbility(ability, new CastFromHandWatcher()); } public ReiverDemon(final ReiverDemon card) { diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/CatchRelease.java b/Mage.Sets/src/mage/sets/dragonsmaze/CatchRelease.java index a4d45bbd0fa..ef78adf890f 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/CatchRelease.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/CatchRelease.java @@ -43,14 +43,12 @@ import mage.abilities.effects.common.continious.GainAbilityTargetEffect; import mage.abilities.effects.common.continious.GainControlTargetEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.SplitCard; -import mage.constants.TargetController; import mage.filter.FilterPermanent; -import mage.filter.common.FilterArtifactPermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.common.FilterEnchantmentPermanent; -import mage.filter.common.FilterLandPermanent; -import mage.filter.common.FilterPlaneswalkerPermanent; -import mage.filter.predicate.permanent.ControllerPredicate; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledEnchantmentPermanent; +import mage.filter.common.FilterControlledLandPermanent; +import mage.filter.common.FilterControlledPlaneswalkerPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -70,13 +68,8 @@ public class CatchRelease extends SplitCard { super(ownerId, 125, "Catch", "Release", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{1}{U}{R}", "{4}{R}{W}",true); this.expansionSetCode = "DGM"; - this.color.setBlue(true); - this.color.setRed(true); - this.color.setWhite(true); - // Catch // Gain control of target permanent until end of turn. Untap it. It gains haste until end of turn. - getLeftHalfCard().getColor().setRed(true); getLeftHalfCard().getSpellAbility().addTarget(new TargetPermanent(new FilterPermanent())); getLeftHalfCard().getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn)); getLeftHalfCard().getSpellAbility().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn)); @@ -84,7 +77,6 @@ public class CatchRelease extends SplitCard { // Release // Each player sacrifices an artifact, a creature, an enchantment, a land, and a planeswalker. - getRightHalfCard().getColor().setGreen(true); getRightHalfCard().getSpellAbility().addEffect(new ReleaseSacrificeEffect()); } @@ -101,20 +93,6 @@ public class CatchRelease extends SplitCard { class ReleaseSacrificeEffect extends OneShotEffect { - private static final FilterArtifactPermanent filter1 = new FilterArtifactPermanent("artifact you control"); - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creature you control"); - private static final FilterEnchantmentPermanent filter3 = new FilterEnchantmentPermanent("enchantment you control"); - private static final FilterLandPermanent filter4 = new FilterLandPermanent("land you control"); - private static final FilterPlaneswalkerPermanent filter5 = new FilterPlaneswalkerPermanent("planeswalker you control"); - - static { - filter1.add(new ControllerPredicate(TargetController.YOU)); - filter2.add(new ControllerPredicate(TargetController.YOU)); - filter3.add(new ControllerPredicate(TargetController.YOU)); - filter4.add(new ControllerPredicate(TargetController.YOU)); - filter5.add(new ControllerPredicate(TargetController.YOU)); - } - public ReleaseSacrificeEffect() { super(Outcome.DestroyPermanent); staticText = "Each player sacrifices an artifact, a creature, an enchantment, a land, and a planeswalker"; @@ -126,7 +104,7 @@ class ReleaseSacrificeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - List chosen = new ArrayList(); + List chosen = new ArrayList<>(); Player controller = game.getPlayer(source.getControllerId()); if (controller == null) { return false; @@ -134,18 +112,11 @@ class ReleaseSacrificeEffect extends OneShotEffect { for (UUID playerId : controller.getInRange()) { Player player = game.getPlayer(playerId); - Target target1 = new TargetControlledPermanent(1, 1, filter1, false); - Target target2 = new TargetControlledPermanent(1, 1, filter2, false); - Target target3 = new TargetControlledPermanent(1, 1, filter3, false); - Target target4 = new TargetControlledPermanent(1, 1, filter4, false); - Target target5 = new TargetControlledPermanent(1, 1, filter5, false); - - - target1.setNotTarget(false); - target2.setNotTarget(false); - target3.setNotTarget(false); - target4.setNotTarget(false); - target5.setNotTarget(false); + Target target1 = new TargetControlledPermanent(1, 1, new FilterControlledArtifactPermanent(), true); + Target target2 = new TargetControlledPermanent(1, 1, new FilterControlledCreaturePermanent(), true); + Target target3 = new TargetControlledPermanent(1, 1, new FilterControlledEnchantmentPermanent(), true); + Target target4 = new TargetControlledPermanent(1, 1, new FilterControlledLandPermanent(), true); + Target target5 = new TargetControlledPermanent(1, 1, new FilterControlledPlaneswalkerPermanent(), true); if (target1.canChoose(player.getId(), game)) { while (player.isInGame() && !target1.isChosen() && target1.canChoose(player.getId(), game)) { diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/NotionThief.java b/Mage.Sets/src/mage/sets/dragonsmaze/NotionThief.java index 90c9864da2c..cba8b75fec1 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/NotionThief.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/NotionThief.java @@ -65,8 +65,7 @@ public class NotionThief extends CardImpl { // Flash this.addAbility(FlashAbility.getInstance()); // If an opponent would draw a card except the first one he or she draws in each of his or her draw steps, instead that player skips that draw and you draw a card. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new NotionThiefReplacementEffect())); - this.addWatcher(new CardsDrawnDuringDrawStepWatcher()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new NotionThiefReplacementEffect()), new CardsDrawnDuringDrawStepWatcher()); } diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/RestoreThePeace.java b/Mage.Sets/src/mage/sets/dragonsmaze/RestoreThePeace.java index 40a0815cfa2..8121038d6e9 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/RestoreThePeace.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/RestoreThePeace.java @@ -54,7 +54,7 @@ public class RestoreThePeace extends CardImpl { // Return each creature that dealt damage this turn to its owner's hand. this.getSpellAbility().addEffect(new RestoreThePeaceEffect()); - this.addWatcher(new SourceDidDamageWatcher()); + this.getSpellAbility().addWatcher(new SourceDidDamageWatcher()); } diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/ScionOfVituGhazi.java b/Mage.Sets/src/mage/sets/dragonsmaze/ScionOfVituGhazi.java index 879068e0942..6020c6b811d 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/ScionOfVituGhazi.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/ScionOfVituGhazi.java @@ -59,14 +59,12 @@ public class ScionOfVituGhazi extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - this.addWatcher(new CastFromHandWatcher()); - // When Scion of Vitu-Ghazi enters the battlefield, if you cast it from your hand, put a 1/1 white Bird creature token with flying onto the battlefield, then populate. Ability ability = new EntersBattlefieldTriggeredAbility( new ConditionalOneShotEffect(new CreateTokenEffect(new BirdToken()), new CastFromHandCondition(), "if you cast it from your hand, put a 1/1 white Bird creature token with flying onto the battlefield,")); ability.addEffect(new PopulateEffect("then")); - this.addAbility(ability); + this.addAbility(ability, new CastFromHandWatcher()); } public ScionOfVituGhazi (final ScionOfVituGhazi card) { diff --git a/Mage.Sets/src/mage/sets/eventide/BatwingBrume.java b/Mage.Sets/src/mage/sets/eventide/BatwingBrume.java index 1d2cf21e633..ef6380c78f6 100644 --- a/Mage.Sets/src/mage/sets/eventide/BatwingBrume.java +++ b/Mage.Sets/src/mage/sets/eventide/BatwingBrume.java @@ -70,7 +70,7 @@ public class BatwingBrume extends CardImpl { new BatwingBrumeEffect(), new ManaWasSpentCondition(ColoredManaSymbol.B), "Each player loses 1 life for each attacking creature he or she controls if {B} was spent to cast {this}")); this.addInfo("Info1", "(Do both if {W}{B} was spent.)"); - this.addWatcher(new ManaSpentToCastWatcher()); + this.getSpellAbility().addWatcher(new ManaSpentToCastWatcher()); } diff --git a/Mage.Sets/src/mage/sets/eventide/CankerousThirst.java b/Mage.Sets/src/mage/sets/eventide/CankerousThirst.java index 26e06b9f84e..aefee598300 100644 --- a/Mage.Sets/src/mage/sets/eventide/CankerousThirst.java +++ b/Mage.Sets/src/mage/sets/eventide/CankerousThirst.java @@ -66,7 +66,7 @@ public class CankerousThirst extends CardImpl { "If {G} was spent to cast {this}, you may have target creature get +3/+3 until end of turn")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.addInfo("Info1", "(Do both if {B}{G} was spent.)"); - this.addWatcher(new ManaSpentToCastWatcher()); + this.getSpellAbility().addWatcher(new ManaSpentToCastWatcher()); } public CankerousThirst(final CankerousThirst card) { diff --git a/Mage.Sets/src/mage/sets/eventide/DreamThief.java b/Mage.Sets/src/mage/sets/eventide/DreamThief.java index 2d751f261c4..7c36af41584 100644 --- a/Mage.Sets/src/mage/sets/eventide/DreamThief.java +++ b/Mage.Sets/src/mage/sets/eventide/DreamThief.java @@ -70,8 +70,7 @@ public class DreamThief extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Dream Thief enters the battlefield, draw a card if you've cast another blue spell this turn. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ConditionalOneShotEffect(new DrawCardSourceControllerEffect(1), new CastBlueSpellThisTurnCondition(), rule))); - this.addWatcher(new DreamThiefWatcher(this.getId())); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ConditionalOneShotEffect(new DrawCardSourceControllerEffect(1), new CastBlueSpellThisTurnCondition(), rule)), new DreamThiefWatcher(this.getId())); } diff --git a/Mage.Sets/src/mage/sets/eventide/GroundlingPouncer.java b/Mage.Sets/src/mage/sets/eventide/GroundlingPouncer.java index e92e11b6156..820325fcffe 100644 --- a/Mage.Sets/src/mage/sets/eventide/GroundlingPouncer.java +++ b/Mage.Sets/src/mage/sets/eventide/GroundlingPouncer.java @@ -79,8 +79,7 @@ public class GroundlingPouncer extends CardImpl { Effect effect2 = new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, false, true); Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{G/U}"), condition, rule); ability.addEffect(effect2); - this.addAbility(ability); - this.addWatcher(new ActivatedAbilityUsedThisTurnWatcher()); + this.addAbility(ability, new ActivatedAbilityUsedThisTurnWatcher()); } diff --git a/Mage.Sets/src/mage/sets/eventide/HotheadedGiant.java b/Mage.Sets/src/mage/sets/eventide/HotheadedGiant.java index 3c9305b46d0..0c239c7c81e 100644 --- a/Mage.Sets/src/mage/sets/eventide/HotheadedGiant.java +++ b/Mage.Sets/src/mage/sets/eventide/HotheadedGiant.java @@ -71,8 +71,7 @@ public class HotheadedGiant extends CardImpl { // Hotheaded Giant enters the battlefield with two -1/-1 counters on it unless you've cast another red spell this turn. Condition condition = new CastRedSpellThisTurnCondition(); - this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.M1M1.createInstance(2)), new InvertCondition(condition), ""), "with two -1/-1 counters on it unless you've cast another red spell this turn")); - this.addWatcher(new HotHeadedGiantWatcher(this.getId())); + this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.M1M1.createInstance(2)), new InvertCondition(condition), ""), "with two -1/-1 counters on it unless you've cast another red spell this turn"), new HotHeadedGiantWatcher(this.getId())); } diff --git a/Mage.Sets/src/mage/sets/eventide/InvertTheSkies.java b/Mage.Sets/src/mage/sets/eventide/InvertTheSkies.java index c3e4db7546a..3a7878016ea 100644 --- a/Mage.Sets/src/mage/sets/eventide/InvertTheSkies.java +++ b/Mage.Sets/src/mage/sets/eventide/InvertTheSkies.java @@ -73,7 +73,7 @@ public class InvertTheSkies extends CardImpl { new LockedInCondition(new ManaWasSpentCondition(ColoredManaSymbol.U)), "and creatures you control gain flying until end of turn if {U} was spent to cast it")); this.addInfo("Info1", "(Do both if {G}{U} was spent.)"); - this.addWatcher(new ManaSpentToCastWatcher()); + this.getSpellAbility().addWatcher(new ManaSpentToCastWatcher()); } public InvertTheSkies(final InvertTheSkies card) { diff --git a/Mage.Sets/src/mage/sets/eventide/Moonhold.java b/Mage.Sets/src/mage/sets/eventide/Moonhold.java index 4279bb178e8..1f5294f3c83 100644 --- a/Mage.Sets/src/mage/sets/eventide/Moonhold.java +++ b/Mage.Sets/src/mage/sets/eventide/Moonhold.java @@ -73,7 +73,7 @@ public class Moonhold extends CardImpl { new LockedInCondition(new ManaWasSpentCondition(ColoredManaSymbol.W)))); this.getSpellAbility().addTarget(new TargetPlayer()); this.addInfo("Info1", "(Do both if {R}{W} was spent.)"); - this.addWatcher(new ManaSpentToCastWatcher()); + this.getSpellAbility().addWatcher(new ManaSpentToCastWatcher()); } public Moonhold(final Moonhold card) { diff --git a/Mage.Sets/src/mage/sets/eventide/SoulReap.java b/Mage.Sets/src/mage/sets/eventide/SoulReap.java index a8ca79abf52..33a34b5ce3c 100644 --- a/Mage.Sets/src/mage/sets/eventide/SoulReap.java +++ b/Mage.Sets/src/mage/sets/eventide/SoulReap.java @@ -78,7 +78,7 @@ public class SoulReap extends CardImpl { this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new SoulReapEffect(), new CastBlackSpellThisTurnCondition(), rule)); - this.addWatcher(new SoulReapWatcher(this.getId())); + this.getSpellAbility().addWatcher(new SoulReapWatcher(this.getId())); } diff --git a/Mage.Sets/src/mage/sets/eventide/TalarasBattalion.java b/Mage.Sets/src/mage/sets/eventide/TalarasBattalion.java index d533eaf4b01..70c382e1075 100644 --- a/Mage.Sets/src/mage/sets/eventide/TalarasBattalion.java +++ b/Mage.Sets/src/mage/sets/eventide/TalarasBattalion.java @@ -71,8 +71,7 @@ public class TalarasBattalion extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Cast Talara's Battalion only if you've cast another green spell this turn. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new TalarasBattalionEffect())); - this.addWatcher(new TalarasBattalionWatcher(this.getId())); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new TalarasBattalionEffect()), new TalarasBattalionWatcher(this.getId())); } diff --git a/Mage.Sets/src/mage/sets/eventide/UnnervingAssault.java b/Mage.Sets/src/mage/sets/eventide/UnnervingAssault.java index 7b13336ef5c..164d5a042fb 100644 --- a/Mage.Sets/src/mage/sets/eventide/UnnervingAssault.java +++ b/Mage.Sets/src/mage/sets/eventide/UnnervingAssault.java @@ -72,7 +72,7 @@ public class UnnervingAssault extends CardImpl { new BoostAllEffect(1, 0, Duration.EndOfTurn, filter2, false), new ManaWasSpentCondition(ColoredManaSymbol.R), " and creatures you control get +1/0 until end of turn if {R} was spent to cast it")); this.addInfo("Info1", "(Do both if {U}{R} was spent.)"); - this.addWatcher(new ManaSpentToCastWatcher()); + this.getSpellAbility().addWatcher(new ManaSpentToCastWatcher()); } diff --git a/Mage.Sets/src/mage/sets/eventide/WavesOfAggression.java b/Mage.Sets/src/mage/sets/eventide/WavesOfAggression.java index d146ecd8b2d..8e1380b2827 100644 --- a/Mage.Sets/src/mage/sets/eventide/WavesOfAggression.java +++ b/Mage.Sets/src/mage/sets/eventide/WavesOfAggression.java @@ -62,7 +62,7 @@ public class WavesOfAggression extends CardImpl { this.color.setWhite(true); // Untap all creatures that attacked this turn. After this main phase, there is an additional combat phase followed by an additional main phase. - this.addWatcher(new AttackedThisTurnWatcher()); + this.getSpellAbility().addWatcher(new AttackedThisTurnWatcher()); this.getSpellAbility().addEffect(new WavesOfAggressionUntapEffect()); this.getSpellAbility().addEffect(new WavesOfAggressionAddPhasesEffect()); // Retrace diff --git a/Mage.Sets/src/mage/sets/exodus/Cataclysm.java b/Mage.Sets/src/mage/sets/exodus/Cataclysm.java index 202824f77b0..e9eac061644 100644 --- a/Mage.Sets/src/mage/sets/exodus/Cataclysm.java +++ b/Mage.Sets/src/mage/sets/exodus/Cataclysm.java @@ -37,12 +37,10 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.TargetController; -import mage.filter.common.FilterArtifactPermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.common.FilterEnchantmentPermanent; -import mage.filter.common.FilterLandPermanent; -import mage.filter.predicate.permanent.ControllerPredicate; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledEnchantmentPermanent; +import mage.filter.common.FilterControlledLandPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -59,8 +57,6 @@ public class Cataclysm extends CardImpl { super(ownerId, 3, "Cataclysm", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{2}{W}{W}"); this.expansionSetCode = "EXO"; - this.color.setWhite(true); - // Each player chooses from the permanents he or she controls an artifact, a creature, an enchantment, and a land, then sacrifices the rest. this.getSpellAbility().addEffect(new CataclysmEffect()); } @@ -77,18 +73,6 @@ public class Cataclysm extends CardImpl { class CataclysmEffect extends OneShotEffect { - private static final FilterArtifactPermanent filter1 = new FilterArtifactPermanent("artifact you control"); - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creature you control"); - private static final FilterEnchantmentPermanent filter3 = new FilterEnchantmentPermanent("enchantment you control"); - private static final FilterLandPermanent filter4 = new FilterLandPermanent("land you control"); - - static { - filter1.add(new ControllerPredicate(TargetController.YOU)); - filter2.add(new ControllerPredicate(TargetController.YOU)); - filter3.add(new ControllerPredicate(TargetController.YOU)); - filter4.add(new ControllerPredicate(TargetController.YOU)); - } - public CataclysmEffect() { super(Outcome.DestroyPermanent); staticText = "Each player chooses from among the permanents he or she controls an artifact, a creature, an enchantment, and a land, then sacrifices the rest"; @@ -105,20 +89,14 @@ class CataclysmEffect extends OneShotEffect { for (UUID playerId : game.getPlayerList()) { Player player = game.getPlayer(playerId); - Target target1 = new TargetControlledPermanent(1, 1, filter1, false); - Target target2 = new TargetControlledPermanent(1, 1, filter2, false); - Target target3 = new TargetControlledPermanent(1, 1, filter3, false); - Target target4 = new TargetControlledPermanent(1, 1, filter4, false); - - - target1.setNotTarget(true); - target2.setNotTarget(true); - target3.setNotTarget(true); - target4.setNotTarget(true); + Target target1 = new TargetControlledPermanent(1, 1, new FilterControlledArtifactPermanent(), true); + Target target2 = new TargetControlledPermanent(1, 1, new FilterControlledCreaturePermanent(), true); + Target target3 = new TargetControlledPermanent(1, 1, new FilterControlledEnchantmentPermanent(), true); + Target target4 = new TargetControlledPermanent(1, 1, new FilterControlledLandPermanent(), true); if (target1.canChoose(player.getId(), game)) { while (player.isInGame() && !target1.isChosen() && target1.canChoose(player.getId(), game)) { - player.choose(Outcome.Benefit, target1, source.getSourceId(), game); + player.chooseTarget(Outcome.Benefit, target1, source, game); } Permanent artifact = game.getPermanent(target1.getFirstTarget()); if (artifact != null) { @@ -129,7 +107,7 @@ class CataclysmEffect extends OneShotEffect { if (target2.canChoose(player.getId(), game)) { while (player.isInGame() && !target2.isChosen() && target2.canChoose(player.getId(), game)) { - player.choose(Outcome.Benefit, target2, source.getSourceId(), game); + player.chooseTarget(Outcome.Benefit, target2, source, game); } Permanent creature = game.getPermanent(target2.getFirstTarget()); if (creature != null) { @@ -140,7 +118,7 @@ class CataclysmEffect extends OneShotEffect { if (target3.canChoose(player.getId(), game)) { while (player.isInGame() && !target3.isChosen() && target3.canChoose(player.getId(), game)) { - player.choose(Outcome.Benefit, target3, source.getSourceId(), game); + player.chooseTarget(Outcome.Benefit, target3, source, game); } Permanent enchantment = game.getPermanent(target3.getFirstTarget()); if (enchantment != null) { @@ -151,7 +129,7 @@ class CataclysmEffect extends OneShotEffect { if (target4.canChoose(player.getId(), game)) { while (player.isInGame() && !target4.isChosen() && target4.canChoose(player.getId(), game)) { - player.choose(Outcome.Benefit, target4, source.getSourceId(), game); + player.chooseTarget(Outcome.Benefit, target4, source, game); } Permanent land = game.getPermanent(target4.getFirstTarget()); if (land != null) { diff --git a/Mage.Sets/src/mage/sets/fatereforged/GhastlyConscription.java b/Mage.Sets/src/mage/sets/fatereforged/GhastlyConscription.java index f4dd69f1cc5..f0d993bb7d5 100644 --- a/Mage.Sets/src/mage/sets/fatereforged/GhastlyConscription.java +++ b/Mage.Sets/src/mage/sets/fatereforged/GhastlyConscription.java @@ -115,8 +115,8 @@ class GhastlyConscriptionEffect extends OneShotEffect { } } MageObjectReference objectReference= new MageObjectReference(card.getId(), card.getZoneChangeCounter() +1, game); - game.addEffect(new BecomesFaceDownCreatureEffect(manaCosts, objectReference, Duration.Custom, FaceDownType.MANIFESTED), newSource); - if (card.moveToZone(Zone.BATTLEFIELD, newSource.getSourceId(), game, false)) { + game.addEffect(new BecomesFaceDownCreatureEffect(manaCosts, objectReference, Duration.Custom, FaceDownType.MANIFESTED), newSource); + if (controller.putOntoBattlefieldWithInfo(card, game, Zone.EXILED, source.getSourceId())) { game.informPlayers(new StringBuilder(controller.getName()) .append(" puts facedown card from exile onto the battlefield").toString()); } diff --git a/Mage.Sets/src/mage/sets/fatereforged/SoulfireGrandMaster.java b/Mage.Sets/src/mage/sets/fatereforged/SoulfireGrandMaster.java index 1d7bfdb4ee2..92b5ccb2087 100644 --- a/Mage.Sets/src/mage/sets/fatereforged/SoulfireGrandMaster.java +++ b/Mage.Sets/src/mage/sets/fatereforged/SoulfireGrandMaster.java @@ -92,8 +92,7 @@ public class SoulfireGrandMaster extends CardImpl { // Instant and sorcery spells you control have lifelink. Effect effect = new GainAbilitySpellsEffect(LifelinkAbility.getInstance(), filter); effect.setText("Instant and sorcery spells you control have lifelink"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); - this.addWatcher(new SoulfireGrandMasterLeavesStackWatcher()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect), new SoulfireGrandMasterLeavesStackWatcher()); // {2}{U/R}{U/R}: The next time you cast an instant or sorcery spell from your hand this turn, put that card into your hand instead of your graveyard as it resolves. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new SoulfireGrandMasterCastFromHandReplacementEffect(), new ManaCostsImpl("{2}{U/R}{U/R}"))); diff --git a/Mage.Sets/src/mage/sets/fifthdawn/ArmedResponse.java b/Mage.Sets/src/mage/sets/fifthdawn/ArmedResponse.java new file mode 100644 index 00000000000..d42f0588ae7 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthdawn/ArmedResponse.java @@ -0,0 +1,73 @@ +/* + * 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.sets.fifthdawn; + +import java.util.UUID; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.target.common.TargetAttackingCreature; + +/** + * + * @author Plopman + */ +public class ArmedResponse extends CardImpl { + + private static final FilterControlledArtifactPermanent filter = new FilterControlledArtifactPermanent("Equipment you control"); + + static { + filter.add(new SubtypePredicate("Equipment")); + } + + + public ArmedResponse(UUID ownerId) { + super(ownerId, 2, "Armed Response", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{W}"); + this.expansionSetCode = "5DN"; + + // Armed Response deals damage to target attacking creature equal to the number of Equipment you control. + Effect effect = new DamageTargetEffect(new PermanentsOnBattlefieldCount(filter)); + effect.setText("{source} deals damage to target attacking creature equal to the number of Equipment you control"); + this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addTarget(new TargetAttackingCreature()); + } + + public ArmedResponse(final ArmedResponse card) { + super(card); + } + + @Override + public ArmedResponse copy() { + return new ArmedResponse(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fifthdawn/CircleOfProtectionArtifacts.java b/Mage.Sets/src/mage/sets/fifthdawn/CircleOfProtectionArtifacts.java new file mode 100644 index 00000000000..63fbd9de86b --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthdawn/CircleOfProtectionArtifacts.java @@ -0,0 +1,73 @@ +/* + * 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.sets.fifthdawn; + +import java.util.UUID; + +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterObject; +import mage.filter.predicate.mageobject.CardTypePredicate; + +/** + * + * @author Plopman + */ +public class CircleOfProtectionArtifacts extends CardImpl { + + private static final FilterObject filter = new FilterObject("artifact source"); + + static { + filter.add(new CardTypePredicate(CardType.ARTIFACT)); + } + + public CircleOfProtectionArtifacts(UUID ownerId) { + super(ownerId, 8, "Circle of Protection: Artifacts", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); + this.expansionSetCode = "5DN"; + + // {2}: The next time an artifact source of your choice would deal damage to you this turn, prevent that damage. + Effect effect = new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, filter); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("2"))); + } + + public CircleOfProtectionArtifacts(final CircleOfProtectionArtifacts card) { + super(card); + } + + @Override + public CircleOfProtectionArtifacts copy() { + return new CircleOfProtectionArtifacts(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fifthdawn/CosmicLarva.java b/Mage.Sets/src/mage/sets/fifthdawn/CosmicLarva.java index dfaff47e1ed..9a5c5d9b662 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/CosmicLarva.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/CosmicLarva.java @@ -37,8 +37,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.common.FilterLandPermanent; +import mage.filter.common.FilterControlledLandPermanent; import mage.target.common.TargetControlledPermanent; /** @@ -59,7 +58,7 @@ public class CosmicLarva extends CardImpl { // Trample this.addAbility(TrampleAbility.getInstance()); // At the beginning of your upkeep, sacrifice Cosmic Larva unless you sacrifice two lands. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceUnlessPaysEffect(new SacrificeTargetCost(new TargetControlledPermanent(2, 2, new FilterLandPermanent("two lands"), true))), TargetController.YOU, false)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceUnlessPaysEffect(new SacrificeTargetCost(new TargetControlledPermanent(2, 2, new FilterControlledLandPermanent("two lands"), true))), TargetController.YOU, false)); } public CosmicLarva(final CosmicLarva card) { diff --git a/Mage.Sets/src/mage/sets/fifthdawn/EnsouledScimitar.java b/Mage.Sets/src/mage/sets/fifthdawn/EnsouledScimitar.java new file mode 100644 index 00000000000..4c9dd258ff5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthdawn/EnsouledScimitar.java @@ -0,0 +1,89 @@ +/* + * 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.sets.fifthdawn; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continious.BecomesCreatureSourceEffect; +import mage.abilities.effects.common.continious.BoostEquippedEffect; +import mage.abilities.keyword.EquipAbility; +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.permanent.token.Token; + +/** + * + * @author Plopman + */ +public class EnsouledScimitar extends CardImpl { + + public EnsouledScimitar(UUID ownerId) { + super(ownerId, 119, "Ensouled Scimitar", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "5DN"; + this.subtype.add("Equipment"); + + // {3}: Ensouled Scimitar becomes a 1/5 Spirit artifact creature with flying until end of turn. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new EnsouledScimitarToken(), "", Duration.EndOfTurn), new ManaCostsImpl("{3}"))); + // Equipped creature gets +1/+5. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(1, 5))); + // Equip {2} + this.addAbility(new EquipAbility(Outcome.BoostCreature, new ManaCostsImpl("{2}"))); + } + + public EnsouledScimitar(final EnsouledScimitar card) { + super(card); + } + + @Override + public EnsouledScimitar copy() { + return new EnsouledScimitar(this); + } +} + +class EnsouledScimitarToken extends Token { + + public EnsouledScimitarToken() { + super("Pincher", "1/5 Spirit artifact creature with flying"); + setOriginalExpansionSetCode("5ND"); + cardType.add(CardType.CREATURE); + subtype.add("Spirit"); + power = new MageInt(1); + toughness = new MageInt(5); + this.addAbility(FlyingAbility.getInstance()); + + } + +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/fifthdawn/KrarkClanEngineers.java b/Mage.Sets/src/mage/sets/fifthdawn/KrarkClanEngineers.java index 9c3f2a44dfd..3a756a953a6 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/KrarkClanEngineers.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/KrarkClanEngineers.java @@ -38,7 +38,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.common.FilterArtifactPermanent; +import mage.filter.common.FilterControlledArtifactPermanent; import mage.target.common.TargetArtifactPermanent; import mage.target.common.TargetControlledPermanent; @@ -54,13 +54,12 @@ public class KrarkClanEngineers extends CardImpl { this.subtype.add("Goblin"); this.subtype.add("Artificer"); - this.color.setRed(true); this.power = new MageInt(2); this.toughness = new MageInt(2); // {R}, Sacrifice two artifacts: Destroy target artifact. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{R}")); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(2, 2, new FilterArtifactPermanent("two artifacts"), true))); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(2, 2, new FilterControlledArtifactPermanent("two artifacts"), true))); ability.addTarget(new TargetArtifactPermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/sets/fifthdawn/KrarkClanOgre.java b/Mage.Sets/src/mage/sets/fifthdawn/KrarkClanOgre.java index ef5456e9ca9..2ffe0c13884 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/KrarkClanOgre.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/KrarkClanOgre.java @@ -39,7 +39,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.common.FilterArtifactPermanent; +import mage.filter.common.FilterControlledArtifactPermanent; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanent; @@ -60,7 +60,7 @@ public class KrarkClanOgre extends CardImpl { // {R}, Sacrifice an artifact: Target creature can't block this turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CantBlockTargetEffect(Duration.EndOfTurn), new ManaCostsImpl("{R}")); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(1, 1, new FilterArtifactPermanent("an artifact"), true))); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(1, 1, new FilterControlledArtifactPermanent("an artifact"), true))); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/fifthdawn/Retaliate.java b/Mage.Sets/src/mage/sets/fifthdawn/Retaliate.java new file mode 100644 index 00000000000..b6759ba4273 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthdawn/Retaliate.java @@ -0,0 +1,64 @@ +/* + * 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.sets.fifthdawn; + +import java.util.UUID; +import mage.abilities.effects.common.DestroyAllEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.DamagedPlayerThisTurnPredicate; + +/** + * + * @author Plopman + */ +public class Retaliate extends CardImpl { + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures that dealt damage to you this turn"); + static { + filter.add(new DamagedPlayerThisTurnPredicate(TargetController.YOU)); + } + public Retaliate(UUID ownerId) { + super(ownerId, 13, "Retaliate", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{2}{W}{W}"); + this.expansionSetCode = "5DN"; + + // Destroy all creatures that dealt damage to you this turn. + this.getSpellAbility().addEffect(new DestroyAllEffect(filter)); + } + + public Retaliate(final Retaliate card) { + super(card); + } + + @Override + public Retaliate copy() { + return new Retaliate(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fifthdawn/StasisCocoon.java b/Mage.Sets/src/mage/sets/fifthdawn/StasisCocoon.java new file mode 100644 index 00000000000..c65531b98a3 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthdawn/StasisCocoon.java @@ -0,0 +1,79 @@ +/* + * 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.sets.fifthdawn; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.CantBlockAttackActivateAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.TargetPermanent; +import mage.target.common.TargetArtifactPermanent; + +/** + * + * @author Plopman + */ +public class StasisCocoon extends CardImpl { + + public StasisCocoon(UUID ownerId) { + super(ownerId, 18, "Stasis Cocoon", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); + this.expansionSetCode = "5DN"; + this.subtype.add("Aura"); + + // Enchant artifact + TargetPermanent auraTarget = new TargetArtifactPermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + + // Enchanted artifact can't attack or block, and its activated abilities can't be activated. + Effect effect = new CantBlockAttackActivateAttachedEffect(); + effect.setText("Enchanted artifact can't attack or block, and its activated abilities can't be activated"); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + + } + + public StasisCocoon(final StasisCocoon card) { + super(card); + } + + @Override + public StasisCocoon copy() { + return new StasisCocoon(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fifthdawn/SummonersEgg.java b/Mage.Sets/src/mage/sets/fifthdawn/SummonersEgg.java new file mode 100644 index 00000000000..764fff6aca1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthdawn/SummonersEgg.java @@ -0,0 +1,160 @@ +/* + * 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.sets.fifthdawn; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetCard; +import mage.util.CardUtil; + +/** + * + * @author Plopman + */ +public class SummonersEgg extends CardImpl { + + public SummonersEgg(UUID ownerId) { + super(ownerId, 157, "Summoner's Egg", Rarity.RARE, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); + this.expansionSetCode = "5DN"; + this.subtype.add("Construct"); + this.power = new MageInt(0); + this.toughness = new MageInt(4); + + // Imprint - When Summoner's Egg enters the battlefield, you may exile a card from your hand face down. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SummonersEggImprintEffect(), true, "Imprint - ")); + // When Summoner's Egg dies, turn the exiled card face up. If it's a creature card, put it onto the battlefield under your control. + this.addAbility(new DiesTriggeredAbility(new SummonersEggPutOntoBattlefieldEffect())); + } + + public SummonersEgg(final SummonersEgg card) { + super(card); + } + + @Override + public SummonersEgg copy() { + return new SummonersEgg(this); + } +} + +class SummonersEggImprintEffect extends OneShotEffect { + + public SummonersEggImprintEffect() { + super(Outcome.Benefit); + staticText = "exile a card from your hand face down"; + } + + public SummonersEggImprintEffect(SummonersEggImprintEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (controller != null) { + if (controller.getHand().size() > 0) { + TargetCard target = new TargetCard(Zone.HAND, new FilterCard()); + if (target.canChoose(source.getSourceId(), source.getControllerId(), game) + && controller.choose(Outcome.Benefit, controller.getHand(), target, game)) { + Card card = controller.getHand().get(target.getFirstTarget(), game); + if (card != null) { + card.setFaceDown(true); + controller.moveCardToExileWithInfo(card, source.getSourceId(), sourcePermanent.getLogName() +" (Imprint)", source.getSourceId(), game, Zone.HAND); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + permanent.imprint(card.getId(), game); + permanent.addInfo("imprint", CardUtil.addToolTipMarkTags("[Imprinted card]")); + } + } + } + } + return true; + } + return false; + + } + + @Override + public SummonersEggImprintEffect copy() { + return new SummonersEggImprintEffect(this); + } + +} + +class SummonersEggPutOntoBattlefieldEffect extends OneShotEffect { + + public SummonersEggPutOntoBattlefieldEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "turn the exiled card face up. If it's a creature card, put it onto the battlefield under your control"; + } + + public SummonersEggPutOntoBattlefieldEffect(final SummonersEggPutOntoBattlefieldEffect effect) { + super(effect); + } + + @Override + public SummonersEggPutOntoBattlefieldEffect copy() { + return new SummonersEggPutOntoBattlefieldEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Permanent SummonersEgg = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (SummonersEgg != null && SummonersEgg.getImprinted() != null && !SummonersEgg.getImprinted().isEmpty()) { + Card imprintedCard = game.getCard(SummonersEgg.getImprinted().get(0)); + if (imprintedCard != null && game.getState().getZone(imprintedCard.getId()).equals(Zone.EXILED)) { + //turn the exiled card face up. + imprintedCard.turnFaceUp(game, source.getControllerId()); + //If it's a creature card, + if(imprintedCard.getCardType().contains(CardType.CREATURE)){ + //put it onto the battlefield under your control + imprintedCard.putOntoBattlefield(game, Zone.EXILED, source.getSourceId(), source.getControllerId()); + } + } + } + return true; + } + return false; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/fifthdawn/SummoningStation.java b/Mage.Sets/src/mage/sets/fifthdawn/SummoningStation.java new file mode 100644 index 00000000000..6153ce3a0dc --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthdawn/SummoningStation.java @@ -0,0 +1,82 @@ +/* + * 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.sets.fifthdawn; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.PutIntoGraveFromBattlefieldAllTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.UntapSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterArtifactPermanent; +import mage.game.permanent.token.Token; + +/** + * + * @author Plopman + */ +public class SummoningStation extends CardImpl { + + public SummoningStation(UUID ownerId) { + super(ownerId, 158, "Summoning Station", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{7}"); + this.expansionSetCode = "5DN"; + + // {tap}: Put a 2/2 colorless Pincher creature token onto the battlefield. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD,new CreateTokenEffect(new PincherToken()), new TapSourceCost())); + // Whenever an artifact is put into a graveyard from the battlefield, you may untap Summoning Station. + this.addAbility(new PutIntoGraveFromBattlefieldAllTriggeredAbility(new UntapSourceEffect(),true, new FilterArtifactPermanent(), false)); + } + + public SummoningStation(final SummoningStation card) { + super(card); + } + + @Override + public SummoningStation copy() { + return new SummoningStation(this); + } +} + +class PincherToken extends Token { + + public PincherToken() { + super("Pincher", "2/2 colorless Pincher creature token"); + setOriginalExpansionSetCode("5ND"); + cardType.add(CardType.CREATURE); + subtype.add("Pincher"); + power = new MageInt(2); + toughness = new MageInt(2); + + } + +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/fifthedition/CircleOfProtectionArtifacts.java b/Mage.Sets/src/mage/sets/fifthedition/CircleOfProtectionArtifacts.java new file mode 100644 index 00000000000..7ae404629fa --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthedition/CircleOfProtectionArtifacts.java @@ -0,0 +1,53 @@ +/* + * 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.sets.fifhedition; + +import java.util.UUID; + + +/** + * + * @author Plopman + */ +public class CircleOfProtectionArtifacts extends mage.sets.fifthdawn.CircleOfProtectionArtifacts { + + public CircleOfProtectionArtifacts(UUID ownerId) { + super(ownerId); + this.cardNumber = 292; + this.expansionSetCode = "5ED"; + } + + public CircleOfProtectionArtifacts(final CircleOfProtectionArtifacts card) { + super(card); + } + + @Override + public CircleOfProtectionArtifacts copy() { + return new CircleOfProtectionArtifacts(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fifthedition/ForceOfNature.java b/Mage.Sets/src/mage/sets/fifthedition/ForceOfNature.java new file mode 100644 index 00000000000..58dc7137e64 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthedition/ForceOfNature.java @@ -0,0 +1,52 @@ +/* + * 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.sets.fifthedition; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class ForceOfNature extends mage.sets.fourthedition.ForceOfNature { + + public ForceOfNature(UUID ownerId) { + super(ownerId); + this.cardNumber = 156; + this.expansionSetCode = "5ED"; + } + + public ForceOfNature(final ForceOfNature card) { + super(card); + } + + @Override + public ForceOfNature copy() { + return new ForceOfNature(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fifthedition/SylvanLibrary.java b/Mage.Sets/src/mage/sets/fifthedition/SylvanLibrary.java index cfc591a8566..6beb7cf0317 100644 --- a/Mage.Sets/src/mage/sets/fifthedition/SylvanLibrary.java +++ b/Mage.Sets/src/mage/sets/fifthedition/SylvanLibrary.java @@ -64,9 +64,7 @@ public class SylvanLibrary extends CardImpl { this.color.setGreen(true); // At the beginning of your draw step, you may draw two additional cards. If you do, choose two cards in your hand drawn this turn. For each of those cards, pay 4 life or put the card on top of your library. - this.addAbility(new BeginningOfDrawTriggeredAbility(new SylvanLibraryEffect(), TargetController.YOU, true)); - - this.addWatcher(new CardsDrawnThisTurnWatcher()); + this.addAbility(new BeginningOfDrawTriggeredAbility(new SylvanLibraryEffect(), TargetController.YOU, true), new CardsDrawnThisTurnWatcher()); } diff --git a/Mage.Sets/src/mage/sets/fourthedition/CircleOfProtectionArtifacts.java b/Mage.Sets/src/mage/sets/fourthedition/CircleOfProtectionArtifacts.java new file mode 100644 index 00000000000..7e630e27c15 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fourthedition/CircleOfProtectionArtifacts.java @@ -0,0 +1,53 @@ +/* + * 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.sets.fourthedition; + +import java.util.UUID; + + +/** + * + * @author Plopman + */ +public class CircleOfProtectionArtifacts extends mage.sets.fifthdawn.CircleOfProtectionArtifacts { + + public CircleOfProtectionArtifacts(UUID ownerId) { + super(ownerId); + this.cardNumber = 263; + this.expansionSetCode = "4ED"; + } + + public CircleOfProtectionArtifacts(final CircleOfProtectionArtifacts card) { + super(card); + } + + @Override + public CircleOfProtectionArtifacts copy() { + return new CircleOfProtectionArtifacts(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fourthedition/ForceOfNature.java b/Mage.Sets/src/mage/sets/fourthedition/ForceOfNature.java new file mode 100644 index 00000000000..e1e9cd6060c --- /dev/null +++ b/Mage.Sets/src/mage/sets/fourthedition/ForceOfNature.java @@ -0,0 +1,108 @@ +/* + * 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.sets.fourthedition; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author jeffwadsworth + */ +public class ForceOfNature extends CardImpl { + + public ForceOfNature(UUID ownerId) { + super(ownerId, 129, "Force of Nature", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{G}{G}{G}{G}"); + this.expansionSetCode = "4ED"; + this.subtype.add("Elemental"); + this.power = new MageInt(8); + this.toughness = new MageInt(8); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // At the beginning of your upkeep, Force of Nature deals 8 damage to you unless you pay {G}{G}{G}{G}. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ForceOfNatureEffect(), TargetController.YOU, false)); + + } + + public ForceOfNature(final ForceOfNature card) { + super(card); + } + + @Override + public ForceOfNature copy() { + return new ForceOfNature(this); + } +} + +class ForceOfNatureEffect extends OneShotEffect { + + public ForceOfNatureEffect() { + super(Outcome.Damage); + this.staticText = "{this} deals 8 damage to you unless you pay {G}{G}{G}{G}"; + } + + public ForceOfNatureEffect(final ForceOfNatureEffect effect) { + super(effect); + } + + @Override + public ForceOfNatureEffect copy() { + return new ForceOfNatureEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Cost cost = new ManaCostsImpl("{G}{G}{G}{G}"); + String message = "Would you like to pay {G}{G}{G}{G} to prevent taking 8 damage from {this}?"; + if (!(controller.chooseUse(Outcome.Benefit, message, game) + && cost.pay(source, game, source.getSourceId(), controller.getId(), false))) { + controller.damage(8, source.getSourceId(), game, false, true); + } + return true; + } + return false; + } + +} diff --git a/Mage.Sets/src/mage/sets/fourthedition/SirensCall.java b/Mage.Sets/src/mage/sets/fourthedition/SirensCall.java index 9c90f8fab86..8ce7034cc59 100644 --- a/Mage.Sets/src/mage/sets/fourthedition/SirensCall.java +++ b/Mage.Sets/src/mage/sets/fourthedition/SirensCall.java @@ -69,7 +69,7 @@ public class SirensCall extends CardImpl { // At the beginning of the next end step, destroy all non-Wall creatures that player controls that didn't attack this turn. Ignore this effect for each creature the player didn't control continuously since the beginning of the turn. this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new SirensCallDestroyEffect()))); - this.addWatcher(new AttackedThisTurnWatcher()); + this.getSpellAbility().addWatcher(new AttackedThisTurnWatcher()); } public SirensCall(final SirensCall card) { diff --git a/Mage.Sets/src/mage/sets/futuresight/PatriciansScorn.java b/Mage.Sets/src/mage/sets/futuresight/PatriciansScorn.java index 4bcaa61aa4e..bdb7be230ca 100644 --- a/Mage.Sets/src/mage/sets/futuresight/PatriciansScorn.java +++ b/Mage.Sets/src/mage/sets/futuresight/PatriciansScorn.java @@ -59,8 +59,7 @@ public class PatriciansScorn extends CardImpl { this.color.setWhite(true); // If you've cast another white spell this turn, you may cast Patrician's Scorn without paying its mana cost. - this.addAbility(new AlternativeCostSourceAbility(new CastWhiteSpellThisTurnCondition())); - this.addWatcher(new PatriciansScornWatcher()); + this.addAbility(new AlternativeCostSourceAbility(new CastWhiteSpellThisTurnCondition()), new PatriciansScornWatcher()); // Destroy all enchantments. this.getSpellAbility().addEffect(new DestroyAllEffect(new FilterEnchantmentPermanent())); } diff --git a/Mage.Sets/src/mage/sets/futuresight/RiverOfTears.java b/Mage.Sets/src/mage/sets/futuresight/RiverOfTears.java index 0ccc49fdc9b..7ba8ddb7251 100644 --- a/Mage.Sets/src/mage/sets/futuresight/RiverOfTears.java +++ b/Mage.Sets/src/mage/sets/futuresight/RiverOfTears.java @@ -56,10 +56,8 @@ public class RiverOfTears extends CardImpl { new BasicManaEffect(Mana.BlueMana), LandfallCondition.getInstance(), "Add {U} to your mana pool. If you played a land this turn, add {B} to your mana pool instead"), - new TapSourceCost())); - - - this.addWatcher(new LandfallWatcher()); + new TapSourceCost()), + new LandfallWatcher()); } public RiverOfTears(final RiverOfTears card) { diff --git a/Mage.Sets/src/mage/sets/futuresight/ScoutsWarning.java b/Mage.Sets/src/mage/sets/futuresight/ScoutsWarning.java index e88f4401292..22155b2292f 100644 --- a/Mage.Sets/src/mage/sets/futuresight/ScoutsWarning.java +++ b/Mage.Sets/src/mage/sets/futuresight/ScoutsWarning.java @@ -60,7 +60,7 @@ public class ScoutsWarning extends CardImpl { // The next creature card you play this turn can be played as though it had flash. this.getSpellAbility().addEffect(new ScoutsWarningAsThoughEffect()); - this.addWatcher(new ScoutsWarningWatcher()); + this.getSpellAbility().addWatcher(new ScoutsWarningWatcher()); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); diff --git a/Mage.Sets/src/mage/sets/futuresight/YixlidJailer.java b/Mage.Sets/src/mage/sets/futuresight/YixlidJailer.java index 44606a0c3b9..a758f4abe93 100644 --- a/Mage.Sets/src/mage/sets/futuresight/YixlidJailer.java +++ b/Mage.Sets/src/mage/sets/futuresight/YixlidJailer.java @@ -102,6 +102,7 @@ class YixlidJailerEffect extends ContinuousEffectImpl { for (Card card : player.getGraveyard().getCards(game)) { if (card != null) { card.getAbilities().clear(); // Will the abilities ever come back???? + // TODO: Fix that (LevelX2) // game.getContinuousEffects().removeGainedEffectsForSource(card.getId()); // game.getState().resetTriggersForSourceId(card.getId()); Abilities abilities = game.getState().getAllOtherAbilities(card.getId()); diff --git a/Mage.Sets/src/mage/sets/gatecrash/AureliasFury.java b/Mage.Sets/src/mage/sets/gatecrash/AureliasFury.java index fb6726fa711..994edc96f19 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/AureliasFury.java +++ b/Mage.Sets/src/mage/sets/gatecrash/AureliasFury.java @@ -96,7 +96,7 @@ public class AureliasFury extends CardImpl { this.getSpellAbility().addEffect(new DamageMultiEffect(xValue)); this.getSpellAbility().addEffect(new AureliasFuryEffect()); this.getSpellAbility().addTarget(new TargetCreatureOrPlayerAmount(xValue)); - this.addWatcher(new AureliasFuryDamagedByWatcher()); + this.getSpellAbility().addWatcher(new AureliasFuryDamagedByWatcher()); } diff --git a/Mage.Sets/src/mage/sets/gatecrash/DevourFlesh.java b/Mage.Sets/src/mage/sets/gatecrash/DevourFlesh.java index efd2afa8bca..08c0c7cd7c0 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/DevourFlesh.java +++ b/Mage.Sets/src/mage/sets/gatecrash/DevourFlesh.java @@ -35,10 +35,7 @@ import mage.constants.Rarity; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; -import mage.filter.predicate.permanent.ControllerPredicate; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -52,17 +49,10 @@ import mage.target.common.TargetControlledPermanent; */ public class DevourFlesh extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - static{ - filter.add(new ControllerPredicate(TargetController.OPPONENT)); - } - public DevourFlesh (UUID ownerId) { super(ownerId, 63, "Devour Flesh", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{B}"); this.expansionSetCode = "GTC"; - this.color.setBlack(true); - // Target player sacrifices a creature, then gains life equal to that creature's toughness. this.getSpellAbility().addEffect(new DevourFleshSacrificeEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); @@ -100,13 +90,12 @@ class DevourFleshSacrificeEffect extends OneShotEffect { if (player == null) { return false; } - FilterCreaturePermanent filter = new FilterCreaturePermanent(); - filter.add(new ControllerIdPredicate(player.getId())); + FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(); int realCount = game.getBattlefield().countAll(filter, player.getId(), game); if (realCount > 0) { Target target = new TargetControlledPermanent(1, 1, filter, true); while (player.isInGame() && !target.isChosen() && target.canChoose(player.getId(), game)) { - player.choose(Outcome.Sacrifice, target, source.getSourceId(), game); + player.chooseTarget(Outcome.Sacrifice, target, source, game); } Permanent permanent = game.getPermanent(target.getFirstTarget()); if (permanent != null) { diff --git a/Mage.Sets/src/mage/sets/gatecrash/ExecutionersSwing.java b/Mage.Sets/src/mage/sets/gatecrash/ExecutionersSwing.java index c02249b33d8..0149fc1d036 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/ExecutionersSwing.java +++ b/Mage.Sets/src/mage/sets/gatecrash/ExecutionersSwing.java @@ -60,7 +60,7 @@ public class ExecutionersSwing extends CardImpl { this.getSpellAbility().addEffect(new BoostTargetEffect(-5,-5, Duration.EndOfTurn)); this.getSpellAbility().addTarget(new TargetCreaturePermanentThatDealtDamageThisTurn()); - this.addWatcher(new SourceDidDamageWatcher()); + this.getSpellAbility().addWatcher(new SourceDidDamageWatcher()); } public ExecutionersSwing(final ExecutionersSwing card) { diff --git a/Mage.Sets/src/mage/sets/gatecrash/HellkiteTyrant.java b/Mage.Sets/src/mage/sets/gatecrash/HellkiteTyrant.java index 3e3bcea42e3..7d2da234f22 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/HellkiteTyrant.java +++ b/Mage.Sets/src/mage/sets/gatecrash/HellkiteTyrant.java @@ -139,7 +139,7 @@ class HellkiteTyrantControlEffect extends ContinuousEffectImpl { private final UUID controllerId; public HellkiteTyrantControlEffect(UUID controllerId) { - super(Duration.EndOfCombat, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); + super(Duration.EndOfGame, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); this.controllerId = controllerId; } diff --git a/Mage.Sets/src/mage/sets/gatecrash/IncursionSpecialist.java b/Mage.Sets/src/mage/sets/gatecrash/IncursionSpecialist.java index 3f20f8c88ae..4bd963b2838 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/IncursionSpecialist.java +++ b/Mage.Sets/src/mage/sets/gatecrash/IncursionSpecialist.java @@ -57,8 +57,7 @@ public class IncursionSpecialist extends CardImpl { this.toughness = new MageInt(3); // Whenever you cast your second spell each turn, Incursion Specialist gets +2/+0 until end of turn and is unblockable this turn. - this.addAbility(new IncursionTriggeredAbility()); - this.addWatcher(new IncursionWatcher()); + this.addAbility(new IncursionTriggeredAbility(), new IncursionWatcher()); } public IncursionSpecialist(final IncursionSpecialist card) { diff --git a/Mage.Sets/src/mage/sets/guildpact/Ghostway.java b/Mage.Sets/src/mage/sets/guildpact/Ghostway.java index 4e45ee3c595..ed6267976c2 100644 --- a/Mage.Sets/src/mage/sets/guildpact/Ghostway.java +++ b/Mage.Sets/src/mage/sets/guildpact/Ghostway.java @@ -28,18 +28,24 @@ package mage.sets.guildpact; import java.util.UUID; - -import mage.constants.*; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnFromExileEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.permanent.ControllerPredicate; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.util.CardUtil; /** * @@ -51,8 +57,6 @@ public class Ghostway extends CardImpl { super(ownerId, 6, "Ghostway", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{2}{W}"); this.expansionSetCode = "GPT"; - this.color.setWhite(true); - // Exile each creature you control. Return those cards to the battlefield under their owner's control at the beginning of the next end step. this.getSpellAbility().addEffect(new GhostwayEffect()); } @@ -87,19 +91,25 @@ class GhostwayEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - UUID exileId = source.getSourceId(); - if (exileId != null) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (sourceObject != null && controller != null) { + int numberCreatures = 0; + UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject); for (Permanent creature : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { if (creature != null) { - if (creature.moveToExile(source.getSourceId(), "Ghostway Exile", source.getSourceId(), game)) { - AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD, true)); - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game)); - game.addDelayedTriggeredAbility(delayedAbility); - } + controller.moveCardToExileWithInfo(creature, exileId,sourceObject.getLogName(), source.getSourceId(), game, Zone.BATTLEFIELD); + numberCreatures++; } } + if (numberCreatures > 0) { + AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new ReturnFromExileEffect(exileId, Zone.BATTLEFIELD, false)); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game)); + game.addDelayedTriggeredAbility(delayedAbility); + } return true; } return false; @@ -109,4 +119,4 @@ class GhostwayEffect extends OneShotEffect { public GhostwayEffect copy() { return new GhostwayEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/guildpact/Quicken.java b/Mage.Sets/src/mage/sets/guildpact/Quicken.java index 9d5728e6a1d..58de31799a8 100644 --- a/Mage.Sets/src/mage/sets/guildpact/Quicken.java +++ b/Mage.Sets/src/mage/sets/guildpact/Quicken.java @@ -61,7 +61,7 @@ public class Quicken extends CardImpl { // The next sorcery card you cast this turn can be cast as though it had flash. this.getSpellAbility().addEffect(new QuickenAsThoughEffect()); - this.addWatcher(new QuickenWatcher()); + this.getSpellAbility().addWatcher(new QuickenWatcher()); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); diff --git a/Mage.Sets/src/mage/sets/guildpact/TinStreetHooligan.java b/Mage.Sets/src/mage/sets/guildpact/TinStreetHooligan.java index 1ac034abfd3..62bd685a8ab 100644 --- a/Mage.Sets/src/mage/sets/guildpact/TinStreetHooligan.java +++ b/Mage.Sets/src/mage/sets/guildpact/TinStreetHooligan.java @@ -61,8 +61,7 @@ public class TinStreetHooligan extends CardImpl { Ability ability = new EntersBattlefieldTriggeredAbility(new ConditionalOneShotEffect(new DestroyTargetEffect(), new ManaWasSpentCondition(ColoredManaSymbol.G), "if {G} was spent to cast {this}, destroy target artifact"), false); ability.addTarget(new TargetArtifactPermanent()); - this.addAbility(ability); - this.addWatcher(new ManaSpentToCastWatcher()); + this.addAbility(ability, new ManaSpentToCastWatcher()); } public TinStreetHooligan(final TinStreetHooligan card) { diff --git a/Mage.Sets/src/mage/sets/iceage/GlacialChasm.java b/Mage.Sets/src/mage/sets/iceage/GlacialChasm.java index 1a529307de7..80c73192bba 100644 --- a/Mage.Sets/src/mage/sets/iceage/GlacialChasm.java +++ b/Mage.Sets/src/mage/sets/iceage/GlacialChasm.java @@ -27,29 +27,23 @@ */ package mage.sets.iceage; -import mage.abilities.Ability; +import java.util.UUID; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.PayLifeCost; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.PreventAllDamageToControllerEffect; +import mage.abilities.effects.common.SacrificeControllerEffect; +import mage.abilities.effects.common.combat.CantAttackAllAnyPlayerEffect; import mage.abilities.keyword.CumulativeUpkeepAbility; import mage.cards.CardImpl; -import mage.constants.*; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterLandPermanent; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.common.FilterControlledLandPermanent; +import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.ControllerPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.Target; -import mage.target.common.TargetControlledPermanent; - -import java.util.UUID; /** * @@ -64,9 +58,11 @@ public class GlacialChasm extends CardImpl { // Cumulative upkeep-Pay 2 life. this.addAbility(new CumulativeUpkeepAbility(new PayLifeCost(2))); // When Glacial Chasm enters the battlefield, sacrifice a land. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeControllerEffect(new FilterLandPermanent(), 1, ""))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeControllerEffect(new FilterControlledLandPermanent(), 1, ""))); // Creatures you control can't attack. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackEffect())); + FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures you control"); + filter.add(new ControllerPredicate(TargetController.YOU)); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackAllAnyPlayerEffect(Duration.WhileOnBattlefield, filter))); // Prevent all damage that would be dealt to you. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PreventAllDamageToControllerEffect(Duration.WhileOnBattlefield))); } @@ -80,108 +76,3 @@ public class GlacialChasm extends CardImpl { return new GlacialChasm(this); } } -class SacrificeControllerEffect extends OneShotEffect{ - - private FilterPermanent filter; - private DynamicValue count; - - public SacrificeControllerEffect ( FilterPermanent filter, DynamicValue count, String preText ) { - super(Outcome.Sacrifice); - this.filter = filter; - this.count = count; - staticText = "sacrifice a land"; - } - - public SacrificeControllerEffect ( FilterPermanent filter, int count, String preText ) { - this(filter, new StaticValue(count), preText); - } - - public SacrificeControllerEffect ( SacrificeControllerEffect effect ) { - super(effect); - this.filter = effect.filter; - this.count = effect.count; - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - - if (player == null) { - return false; - } - - filter.add(new ControllerPredicate(TargetController.YOU)); - - int amount = count.calculate(game, source, this); - int realCount = game.getBattlefield().countAll(filter, player.getId(), game); - amount = Math.min(amount, realCount); - - Target target = new TargetControlledPermanent(amount, amount, filter, false); - - //A spell or ability could have removed the only legal target this player - //had, if thats the case this ability should fizzle. - if (amount > 0 && target.canChoose(source.getSourceId(), player.getId(), game)) { - boolean abilityApplied = false; - while (player.isInGame() && !target.isChosen() && target.canChoose(player.getId(), game)) { - player.choose(Outcome.Sacrifice, target, source.getSourceId(), game); - } - - for ( int idx = 0; idx < target.getTargets().size(); idx++) { - Permanent permanent = game.getPermanent((UUID)target.getTargets().get(idx)); - - if ( permanent != null ) { - abilityApplied |= permanent.sacrifice(source.getSourceId(), game); - } - } - - return abilityApplied; - } - return false; - } - - public void setAmount(DynamicValue amount) { - this.count = amount; - } - - @Override - public SacrificeControllerEffect copy() { - return new SacrificeControllerEffect(this); - } - -} - - -class CantAttackEffect extends ReplacementEffectImpl { - - public CantAttackEffect() { - super(Duration.WhileOnBattlefield, Outcome.Detriment); - staticText = "Creatures you control can't attack"; - } - - public CantAttackEffect(final CantAttackEffect effect) { - super(effect); - } - - @Override - public CantAttackEffect copy() { - return new CantAttackEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return true; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.DECLARE_ATTACKER && source.getControllerId().equals(event.getPlayerId())) { - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/iceage/SongsOfTheDamned.java b/Mage.Sets/src/mage/sets/iceage/SongsOfTheDamned.java new file mode 100644 index 00000000000..5732302dad6 --- /dev/null +++ b/Mage.Sets/src/mage/sets/iceage/SongsOfTheDamned.java @@ -0,0 +1,62 @@ +/* + * 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.sets.iceage; + +import java.util.UUID; +import mage.Mana; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.DynamicManaEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.common.FilterCreatureCard; + +/** + * + * @author fireshoes + */ +public class SongsOfTheDamned extends CardImpl { + + public SongsOfTheDamned(UUID ownerId) { + super(ownerId, 48, "Songs of the Damned", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{B}"); + this.expansionSetCode = "ICE"; + + // Add {B} to your mana pool for each creature card in your graveyard. + DynamicManaEffect effect = new DynamicManaEffect(Mana.BlackMana, new CardsInControllerGraveyardCount(new FilterCreatureCard())); + this.getSpellAbility().addEffect(effect); + } + + public SongsOfTheDamned(final SongsOfTheDamned card) { + super(card); + } + + @Override + public SongsOfTheDamned copy() { + return new SongsOfTheDamned(this); + } +} diff --git a/Mage.Sets/src/mage/sets/iceage/TinderWall.java b/Mage.Sets/src/mage/sets/iceage/TinderWall.java index bfeed21e545..45da4fc5702 100644 --- a/Mage.Sets/src/mage/sets/iceage/TinderWall.java +++ b/Mage.Sets/src/mage/sets/iceage/TinderWall.java @@ -80,10 +80,7 @@ public class TinderWall extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(2), new ManaCostsImpl("{R}")); ability.addTarget(new TargetCreaturePermanent(filter)); ability.addCost(new SacrificeSourceCost()); - this.addAbility(ability); - - - this.addWatcher(new BlockedByWatcher()); + this.addAbility(ability, new BlockedByWatcher()); } public TinderWall(final TinderWall card) { diff --git a/Mage.Sets/src/mage/sets/innistrad/CivilizedScholar.java b/Mage.Sets/src/mage/sets/innistrad/CivilizedScholar.java index 6db7fe8cee4..e96f3b66911 100644 --- a/Mage.Sets/src/mage/sets/innistrad/CivilizedScholar.java +++ b/Mage.Sets/src/mage/sets/innistrad/CivilizedScholar.java @@ -67,8 +67,7 @@ public class CivilizedScholar extends CardImpl { // {tap}: Draw a card, then discard a card. If a creature card is discarded this way, untap Civilized Scholar, then transform it. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CivilizedScholarEffect(), new TapSourceCost())); - this.addAbility(new TransformAbility()); - this.addWatcher(new HomicidalBruteWatcher()); + this.addAbility(new TransformAbility(), new HomicidalBruteWatcher()); } public CivilizedScholar(final CivilizedScholar card) { diff --git a/Mage.Sets/src/mage/sets/innistrad/DivineReckoning.java b/Mage.Sets/src/mage/sets/innistrad/DivineReckoning.java index 4b8a91f4066..90b7cc12eea 100644 --- a/Mage.Sets/src/mage/sets/innistrad/DivineReckoning.java +++ b/Mage.Sets/src/mage/sets/innistrad/DivineReckoning.java @@ -49,6 +49,7 @@ import mage.target.common.TargetControlledPermanent; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import mage.filter.common.FilterControlledCreaturePermanent; /** * @author nantuko @@ -59,8 +60,6 @@ public class DivineReckoning extends CardImpl { super(ownerId, 10, "Divine Reckoning", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{2}{W}{W}"); this.expansionSetCode = "ISD"; - this.color.setWhite(true); - // Each player chooses a creature he or she controls. Destroy the rest. this.getSpellAbility().addEffect(new DivineReckoningEffect()); @@ -79,13 +78,7 @@ public class DivineReckoning extends CardImpl { } class DivineReckoningEffect extends OneShotEffect { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control"); - - static { - filter.add(new ControllerPredicate(TargetController.YOU)); - } - + public DivineReckoningEffect() { super(Outcome.DestroyPermanent); staticText = "Each player chooses a creature he or she controls. Destroy the rest"; @@ -97,16 +90,16 @@ class DivineReckoningEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - List chosen = new ArrayList(); + List chosen = new ArrayList<>(); Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { for (UUID playerId : controller.getInRange()) { Player player = game.getPlayer(playerId); - Target target = new TargetControlledPermanent(1, 1, filter, false); + Target target = new TargetControlledPermanent(1, 1, new FilterControlledCreaturePermanent(), false); if (target.canChoose(player.getId(), game)) { while (player.isInGame() && !target.isChosen() && target.canChoose(player.getId(), game)) { - player.choose(Outcome.Benefit, target, source.getSourceId(), game); + player.chooseTarget(Outcome.Benefit, target, source, game); } Permanent permanent = game.getPermanent(target.getFirstTarget()); if (permanent != null) { diff --git a/Mage.Sets/src/mage/sets/innistrad/GarrukTheVeilCursed.java b/Mage.Sets/src/mage/sets/innistrad/GarrukTheVeilCursed.java index ed4c7e2360b..d71b89f3d53 100644 --- a/Mage.Sets/src/mage/sets/innistrad/GarrukTheVeilCursed.java +++ b/Mage.Sets/src/mage/sets/innistrad/GarrukTheVeilCursed.java @@ -42,11 +42,8 @@ import mage.cards.CardImpl; import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.*; -import mage.filter.FilterPermanent; import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; -import mage.filter.predicate.permanent.ControllerPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.WolfTokenWithDeathtouch; @@ -56,6 +53,7 @@ import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetControlledPermanent; import java.util.UUID; +import mage.filter.common.FilterControlledCreaturePermanent; /** * @author nantuko @@ -132,12 +130,7 @@ class GarrukTheVeilCursedValue implements DynamicValue { class GarrukTheVeilCursedEffect extends OneShotEffect { - private static final FilterPermanent filterCreature = new FilterPermanent("a creature you control"); - - static { - filterCreature.add(new CardTypePredicate(CardType.CREATURE)); - filterCreature.add(new ControllerPredicate(TargetController.YOU)); - } + private static final FilterControlledCreaturePermanent filterCreature = new FilterControlledCreaturePermanent("a creature you control"); public GarrukTheVeilCursedEffect() { super(Outcome.Benefit); @@ -150,22 +143,22 @@ class GarrukTheVeilCursedEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); - if (player == null) { + if (controller == null) { return false; } // sacrifice a creature Target target = new TargetControlledPermanent(1, 1, filterCreature, false); boolean sacrificed = false; - if (target.canChoose(player.getId(), game)) { - while (player.isInGame() && !target.isChosen() && target.canChoose(player.getId(), game)) { - player.choose(Outcome.Sacrifice, target, source.getSourceId(), game); + if (target.canChoose(controller.getId(), game)) { + while (controller.isInGame() && !target.isChosen() && target.canChoose(controller.getId(), game)) { + controller.chooseTarget(Outcome.Sacrifice, target, source, game); } for (int idx = 0; idx < target.getTargets().size(); idx++) { - Permanent permanent = game.getPermanent((UUID) target.getTargets().get(idx)); + Permanent permanent = game.getPermanent(target.getTargets().get(idx)); if (permanent != null) { sacrificed |= permanent.sacrifice(source.getSourceId(), game); } @@ -177,9 +170,9 @@ class GarrukTheVeilCursedEffect extends OneShotEffect { FilterCreatureCard filter = new FilterCreatureCard(); TargetCardInLibrary targetInLibrary = new TargetCardInLibrary(filter); Cards cards = new CardsImpl(); - if (player.searchLibrary(targetInLibrary, game)) { + if (controller.searchLibrary(targetInLibrary, game)) { for (UUID cardId : targetInLibrary.getTargets()) { - Card card = player.getLibrary().remove(cardId, game); + Card card = controller.getLibrary().remove(cardId, game); if (card != null) { card.moveToZone(Zone.HAND, source.getSourceId(), game, false); cards.add(card); @@ -188,10 +181,10 @@ class GarrukTheVeilCursedEffect extends OneShotEffect { } // reveal if (cards.size() > 0) { - player.revealCards("Garruk, the Veil-Cursed", cards, game); + controller.revealCards("Garruk, the Veil-Cursed", cards, game); } // shuffle - player.shuffleLibrary(game); + controller.shuffleLibrary(game); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/jacevschandra/FathomSeer.java b/Mage.Sets/src/mage/sets/jacevschandra/FathomSeer.java index 3f4e1e169b5..4bb0680c422 100644 --- a/Mage.Sets/src/mage/sets/jacevschandra/FathomSeer.java +++ b/Mage.Sets/src/mage/sets/jacevschandra/FathomSeer.java @@ -36,7 +36,7 @@ import mage.abilities.keyword.MorphAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.target.common.TargetControlledPermanent; @@ -46,7 +46,7 @@ import mage.target.common.TargetControlledPermanent; */ public class FathomSeer extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("Islands"); + private static final FilterControlledPermanent filter = new FilterControlledPermanent("Islands"); static { filter.add(new SubtypePredicate("Island")); diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/ArrowStorm.java b/Mage.Sets/src/mage/sets/khansoftarkir/ArrowStorm.java index 34b47877600..85ab14724f9 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/ArrowStorm.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/ArrowStorm.java @@ -61,7 +61,7 @@ public class ArrowStorm extends CardImpl { new DamageTargetEffect(5, false), RaidCondition.getInstance(), "

Raid - If you attacked with a creature this turn, instead {this} deals 5 damage to that creature or player and the damage can't be prevented")); - this.addWatcher(new PlayerAttackedWatcher()); + this.getSpellAbility().addWatcher(new PlayerAttackedWatcher()); } public ArrowStorm(final ArrowStorm card) { diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/BellowingSaddlebrute.java b/Mage.Sets/src/mage/sets/khansoftarkir/BellowingSaddlebrute.java index f1e4a4d002b..cd27fae968f 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/BellowingSaddlebrute.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/BellowingSaddlebrute.java @@ -60,8 +60,7 @@ public class BellowingSaddlebrute extends CardImpl { new EntersBattlefieldTriggeredAbility(new LoseLifeSourceControllerEffect(4)), new InvertCondition(RaidCondition.getInstance()), "Raid - When {this} enters the battlefield, you lose 4 life unless you attacked with a creature this turn" - )); - this.addWatcher(new PlayerAttackedWatcher()); + ), new PlayerAttackedWatcher()); } public BellowingSaddlebrute(final BellowingSaddlebrute card) { diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/BloodsoakedChampion.java b/Mage.Sets/src/mage/sets/khansoftarkir/BloodsoakedChampion.java index 62f34f52b16..80932c85023 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/BloodsoakedChampion.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/BloodsoakedChampion.java @@ -67,8 +67,7 @@ public class BloodsoakedChampion extends CardImpl { new ManaCostsImpl<>("{1}{B}"), RaidCondition.getInstance(), "Raid - {1}{B}: Return {this} from your graveyard to the battlefield. Activate this ability only if you attacked with a creature this turn"); - this.addAbility(ability); - this.addWatcher(new PlayerAttackedWatcher()); + this.addAbility(ability, new PlayerAttackedWatcher()); } public BloodsoakedChampion(final BloodsoakedChampion card) { diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/HowlOfTheHorde.java b/Mage.Sets/src/mage/sets/khansoftarkir/HowlOfTheHorde.java index ce82fdf96d6..f2c46a92407 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/HowlOfTheHorde.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/HowlOfTheHorde.java @@ -63,7 +63,7 @@ public class HowlOfTheHorde extends CardImpl { // Raid - If you attacked with a creature this turn, when you cast your next instant or sorcery spell this turn, copy that spell an additional time. You may choose new targets for the copy. this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new CreateDelayedTriggeredAbilityEffect(new HowlOfTheHordeDelayedTriggeredAbility()), RaidCondition.getInstance(), "

Raid - If you attacked with a creature this turn, when you cast your next instant or sorcery spell this turn, copy that spell an additional time. You may choose new targets for the copy.")); - this.addWatcher(new PlayerAttackedWatcher()); + this.getSpellAbility().addWatcher(new PlayerAttackedWatcher()); } public HowlOfTheHorde(final HowlOfTheHorde card) { diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/MarduHeartPiercer.java b/Mage.Sets/src/mage/sets/khansoftarkir/MarduHeartPiercer.java index f5c2ce8da7d..0756a81e815 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/MarduHeartPiercer.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/MarduHeartPiercer.java @@ -60,8 +60,7 @@ public class MarduHeartPiercer extends CardImpl { Ability ability = new ConditionalTriggeredAbility(new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(2)), RaidCondition.getInstance(), "Raid - When {this} enters the battlefield, if you attacked with a creature this turn, {this} deals 2 damage to target creature or player.", false); ability.addTarget(new TargetCreatureOrPlayer()); - this.addAbility(ability); - this.addWatcher(new PlayerAttackedWatcher()); + this.addAbility(ability, new PlayerAttackedWatcher()); } public MarduHeartPiercer(final MarduHeartPiercer card) { diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/MarduHordechief.java b/Mage.Sets/src/mage/sets/khansoftarkir/MarduHordechief.java index 1b06ea71c6f..5ce88e4d532 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/MarduHordechief.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/MarduHordechief.java @@ -57,9 +57,8 @@ public class MarduHordechief extends CardImpl { // Raid - When Mardu Hordechief enters the battlefield, if you attacked with a creature this turn, put a 1/1 white Warrior creature token onto the battlefield this.addAbility(new ConditionalTriggeredAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new MarduHordechiefToken())), RaidCondition.getInstance(), - "Raid - When {this} enters the battlefield, if you attacked with a creature this turn, put a 1/1 white Warrior creature token onto the battlefield.", false)); - - this.addWatcher(new PlayerAttackedWatcher()); + "Raid - When {this} enters the battlefield, if you attacked with a creature this turn, put a 1/1 white Warrior creature token onto the battlefield.", false), + new PlayerAttackedWatcher()); } public MarduHordechief(final MarduHordechief card) { diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/MarduSkullhunter.java b/Mage.Sets/src/mage/sets/khansoftarkir/MarduSkullhunter.java index e4681d1d23f..f8efd6d7449 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/MarduSkullhunter.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/MarduSkullhunter.java @@ -64,8 +64,7 @@ public class MarduSkullhunter extends CardImpl { Ability ability = new ConditionalTriggeredAbility(new EntersBattlefieldTriggeredAbility(new DiscardTargetEffect(1)), RaidCondition.getInstance(), "Raid - When {this} enters the battlefield, if you attacked with a creature this turn, target opponent discards a card.", false); ability.addTarget(new TargetOpponent()); - this.addAbility(ability); - this.addWatcher(new PlayerAttackedWatcher()); + this.addAbility(ability, new PlayerAttackedWatcher()); } public MarduSkullhunter(final MarduSkullhunter card) { diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/MarduWarshrieker.java b/Mage.Sets/src/mage/sets/khansoftarkir/MarduWarshrieker.java index 38ee5baae0a..1fbdd4bc6ec 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/MarduWarshrieker.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/MarduWarshrieker.java @@ -57,8 +57,8 @@ public class MarduWarshrieker extends CardImpl { // Raid - When Mardu Warshrieker enters the battlefield, if you attacked with a creature this turn, add {R}{W}{B} to your mana pool. this.addAbility(new ConditionalTriggeredAbility(new EntersBattlefieldTriggeredAbility(new AddManaToManaPoolSourceControllerEffect(new Mana(1,0,0,1,1,0,0))), RaidCondition.getInstance(), - "Raid - When {this} enters the battlefield, if you attacked with a creature this turn, add {R}{W}{B} to your mana pool.", false)); - this.addWatcher(new PlayerAttackedWatcher()); + "Raid - When {this} enters the battlefield, if you attacked with a creature this turn, add {R}{W}{B} to your mana pool.", false), + new PlayerAttackedWatcher()); } public MarduWarshrieker(final MarduWarshrieker card) { diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/TimelyHordemate.java b/Mage.Sets/src/mage/sets/khansoftarkir/TimelyHordemate.java index 8fe65e58db8..e0f0de85368 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/TimelyHordemate.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/TimelyHordemate.java @@ -69,8 +69,7 @@ public class TimelyHordemate extends CardImpl { Ability ability = new ConditionalTriggeredAbility(new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect()), RaidCondition.getInstance(), "Raid - When {this} enters the battlefield, if you attacked with a creature this turn, return target creature card with converted mana cost 2 or less from your graveyard to the battlefield.", false); ability.addTarget(new TargetCardInYourGraveyard(filter)); - this.addAbility(ability); - this.addWatcher(new PlayerAttackedWatcher()); + this.addAbility(ability, new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/WarNameAspirant.java b/Mage.Sets/src/mage/sets/khansoftarkir/WarNameAspirant.java index af213b941d3..e9c1fd2e716 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/WarNameAspirant.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/WarNameAspirant.java @@ -71,8 +71,8 @@ public class WarNameAspirant extends CardImpl { RaidCondition.getInstance(), true, "Raid - {this} enters the battlefield with a +1/+1 counter on it if you attacked with a creature this turn", - "{this} enters the battlefield with a +1/+1 counter")); - this.addWatcher(new PlayerAttackedWatcher()); + "{this} enters the battlefield with a +1/+1 counter"), + new PlayerAttackedWatcher()); // War-Name Aspirant can't be blocked by creatures with power 1 or less. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBeBlockedByCreaturesSourceEffect(filter, Duration.WhileOnBattlefield))); diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/WingmateRoc.java b/Mage.Sets/src/mage/sets/khansoftarkir/WingmateRoc.java index 322b75255fb..9549713ab45 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/WingmateRoc.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/WingmateRoc.java @@ -64,8 +64,8 @@ public class WingmateRoc extends CardImpl { // Raid - When Wingmate Roc enters the battlefield, if you attacked with a creature this turn, put a 3/4 white Bird creature token with flying onto the battlefield. this.addAbility(new ConditionalTriggeredAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WingmateRocToken())), RaidCondition.getInstance(), - "Raid - When {this} enters the battlefield, if you attacked with a creature this turn, put a 3/4 white Bird creature token with flying onto the battlefield.", false)); - this.addWatcher(new PlayerAttackedWatcher()); + "Raid - When {this} enters the battlefield, if you attacked with a creature this turn, put a 3/4 white Bird creature token with flying onto the battlefield.", false), + new PlayerAttackedWatcher()); // Whenever Wingmate Roc attacks, you gain 1 life for each attacking creature. Effect effect = new GainLifeEffect(new AttackingCreatureCount()); diff --git a/Mage.Sets/src/mage/sets/legends/ChainsOfMephistopheles.java b/Mage.Sets/src/mage/sets/legends/ChainsOfMephistopheles.java index 3144f10e59d..aefc9db49a8 100644 --- a/Mage.Sets/src/mage/sets/legends/ChainsOfMephistopheles.java +++ b/Mage.Sets/src/mage/sets/legends/ChainsOfMephistopheles.java @@ -60,8 +60,7 @@ public class ChainsOfMephistopheles extends CardImpl { this.color.setBlack(true); // If a player would draw a card except the first one he or she draws in his or her draw step each turn, that player discards a card instead. If the player discards a card this way, he or she draws a card. If the player doesn't discard a card this way, he or she puts the top card of his or her library into his or her graveyard. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ChainsOfMephistophelesReplacementEffect())); - this.addWatcher(new CardsDrawnDuringDrawStepWatcher()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ChainsOfMephistophelesReplacementEffect()), new CardsDrawnDuringDrawStepWatcher()); } public ChainsOfMephistopheles(final ChainsOfMephistopheles card) { diff --git a/Mage.Sets/src/mage/sets/limitedalpha/Berserk.java b/Mage.Sets/src/mage/sets/limitedalpha/Berserk.java index 2d50d593dbd..c59b8bb7eb5 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/Berserk.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/Berserk.java @@ -68,8 +68,7 @@ public class Berserk extends CardImpl { this.color.setGreen(true); // Cast Berserk only before the combat damage step. (Zone = all because it can be at least graveyard or hand) - this.addAbility(new SimpleStaticAbility(Zone.ALL, new BerserkReplacementEffect())); - this.addWatcher(new CombatDamageStepStartedWatcher()); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new BerserkReplacementEffect()), new CombatDamageStepStartedWatcher()); // Target creature gains trample and gets +X/+0 until end of turn, where X is its power. // At the beginning of the next end step, destroy that creature if it attacked this turn. @@ -81,7 +80,7 @@ public class Berserk extends CardImpl { this.getSpellAbility().addEffect(effect); this.getSpellAbility().addEffect(new BerserkDestroyEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.addWatcher(new AttackedThisTurnWatcher()); + this.getSpellAbility().addWatcher(new AttackedThisTurnWatcher()); } diff --git a/Mage.Sets/src/mage/sets/limitedalpha/Disintegrate.java b/Mage.Sets/src/mage/sets/limitedalpha/Disintegrate.java index ccd8986eea6..72488f9803d 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/Disintegrate.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/Disintegrate.java @@ -29,21 +29,17 @@ package mage.sets.limitedalpha; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.dynamicvalue.common.ManacostVariableValue; import mage.abilities.effects.Effect; -import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.replacement.DealtDamageToCreatureBySourceDies; import mage.abilities.effects.common.ruleModifying.CantRegenerateTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Rarity; -import mage.game.Game; -import mage.game.events.GameEvent; import mage.target.common.TargetCreatureOrPlayer; +import mage.watchers.common.DamagedByWatcher; /** * @@ -64,6 +60,7 @@ public class Disintegrate extends CardImpl { effect.setText("If the creature would die this turn, exile it instead"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); + this.getSpellAbility().addWatcher(new DamagedByWatcher()); } public Disintegrate(final Disintegrate card) { diff --git a/Mage.Sets/src/mage/sets/limitedalpha/ForceOfNature.java b/Mage.Sets/src/mage/sets/limitedalpha/ForceOfNature.java new file mode 100644 index 00000000000..a94af82d885 --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedalpha/ForceOfNature.java @@ -0,0 +1,52 @@ +/* + * 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.sets.limitedalpha; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class ForceOfNature extends mage.sets.fourthedition.ForceOfNature { + + public ForceOfNature(UUID ownerId) { + super(ownerId); + this.cardNumber = 103; + this.expansionSetCode = "LEA"; + } + + public ForceOfNature(final ForceOfNature card) { + super(card); + } + + @Override + public ForceOfNature copy() { + return new ForceOfNature(this); + } +} diff --git a/Mage.Sets/src/mage/sets/limitedalpha/Fork.java b/Mage.Sets/src/mage/sets/limitedalpha/Fork.java new file mode 100644 index 00000000000..a81c7fcfc90 --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedalpha/Fork.java @@ -0,0 +1,52 @@ +/* + * 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.sets.limitedalpha; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class Fork extends mage.sets.limitedbeta.Fork { + + public Fork(UUID ownerId) { + super(ownerId); + this.cardNumber = 153; + this.expansionSetCode = "LEA"; + } + + public Fork(final Fork card) { + super(card); + } + + @Override + public Fork copy() { + return new Fork(this); + } +} diff --git a/Mage.Sets/src/mage/sets/limitedbeta/ForceOfNature.java b/Mage.Sets/src/mage/sets/limitedbeta/ForceOfNature.java new file mode 100644 index 00000000000..f432c6f73ee --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedbeta/ForceOfNature.java @@ -0,0 +1,52 @@ +/* + * 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.sets.limitedbeta; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class ForceOfNature extends mage.sets.fourthedition.ForceOfNature { + + public ForceOfNature(UUID ownerId) { + super(ownerId); + this.cardNumber = 103; + this.expansionSetCode = "LEB"; + } + + public ForceOfNature(final ForceOfNature card) { + super(card); + } + + @Override + public ForceOfNature copy() { + return new ForceOfNature(this); + } +} diff --git a/Mage.Sets/src/mage/sets/limitedbeta/Fork.java b/Mage.Sets/src/mage/sets/limitedbeta/Fork.java new file mode 100644 index 00000000000..4db929c4711 --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedbeta/Fork.java @@ -0,0 +1,117 @@ +/* + * 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.sets.limitedbeta; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.TargetSpell; + +/** + * + * @author jeffwadsworth + */ +public class Fork extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("target instant or sorcery spell"); + + static { + filter.add(Predicates.or(new CardTypePredicate(CardType.INSTANT), + new CardTypePredicate(CardType.SORCERY))); + } + + public Fork(UUID ownerId) { + super(ownerId, 154, "Fork", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{R}{R}"); + this.expansionSetCode = "LEB"; + + // Copy target instant or sorcery spell, except that the copy is red. You may choose new targets for the copy. + this.getSpellAbility().addEffect(new ForkEffect()); + this.getSpellAbility().addTarget(new TargetSpell(filter)); + + } + + public Fork(final Fork card) { + super(card); + } + + @Override + public Fork copy() { + return new Fork(this); + } +} + +class ForkEffect extends OneShotEffect { + + public ForkEffect() { + super(Outcome.Copy); + staticText = "Copy target instant or sorcery spell, except that the copy is red. You may choose new targets for the copy"; + } + + public ForkEffect(final ForkEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); + if (spell != null) { + Spell copy = spell.copySpell(); + copy.getColor().setRed(true); + copy.setControllerId(controller.getId()); + copy.setCopiedSpell(true); + game.getStack().push(copy); + copy.chooseNewTargets(game, controller.getId()); + return true; + } + return false; + } + + @Override + public ForkEffect copy() { + return new ForkEffect(this); + } + + @Override + public String getText(Mode mode) { + StringBuilder sb = new StringBuilder(); + sb.append("Copy target ").append(mode.getTargets().get(0).getTargetName()).append(", except that the copy is red. You may choose new targets for the copy"); + return sb.toString(); + } +} diff --git a/Mage.Sets/src/mage/sets/lorwyn/SpinerockKnoll.java b/Mage.Sets/src/mage/sets/lorwyn/SpinerockKnoll.java index de86c9954d2..b4579162792 100644 --- a/Mage.Sets/src/mage/sets/lorwyn/SpinerockKnoll.java +++ b/Mage.Sets/src/mage/sets/lorwyn/SpinerockKnoll.java @@ -73,8 +73,7 @@ public class SpinerockKnoll extends CardImpl { new ColoredManaCost(ColoredManaSymbol.R), new SpinerockKnollCondition()); ability.addCost(new TapSourceCost()); - this.addAbility(ability); - this.addWatcher(new SpinerockKnollWatcher()); + this.addAbility(ability, new SpinerockKnollWatcher()); } public SpinerockKnoll(final SpinerockKnoll card) { diff --git a/Mage.Sets/src/mage/sets/lorwyn/WindbriskHeights.java b/Mage.Sets/src/mage/sets/lorwyn/WindbriskHeights.java index c84430d3e21..da4e1a83b5a 100644 --- a/Mage.Sets/src/mage/sets/lorwyn/WindbriskHeights.java +++ b/Mage.Sets/src/mage/sets/lorwyn/WindbriskHeights.java @@ -61,9 +61,7 @@ public class WindbriskHeights extends CardImpl { Ability ability = new ActivateIfConditionActivatedAbility( Zone.BATTLEFIELD, new HideawayPlayEffect(), new ManaCostsImpl("{W}"), WindbriskHeightsAttackersCondition.getInstance()); ability.addCost(new TapSourceCost()); - this.addAbility(ability); - - this.addWatcher(new PlayerAttackedWatcher()); + this.addAbility(ability, new PlayerAttackedWatcher()); } diff --git a/Mage.Sets/src/mage/sets/magic2010/TrumpetBlast.java b/Mage.Sets/src/mage/sets/magic2010/TrumpetBlast.java index 48dacad68c3..6ffffa88493 100644 --- a/Mage.Sets/src/mage/sets/magic2010/TrumpetBlast.java +++ b/Mage.Sets/src/mage/sets/magic2010/TrumpetBlast.java @@ -28,11 +28,11 @@ package mage.sets.magic2010; import java.util.UUID; +import mage.abilities.effects.common.continious.BoostAllEffect; +import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; -import mage.abilities.effects.common.continious.BoostAllEffect; -import mage.cards.CardImpl; import mage.filter.common.FilterAttackingCreature; /** @@ -47,8 +47,7 @@ public class TrumpetBlast extends CardImpl { super(ownerId, 160, "Trumpet Blast", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{R}"); this.expansionSetCode = "M10"; - this.color.setRed(true); - + // Attacking creatures get +2/+0 until end of turn. this.getSpellAbility().addEffect(new BoostAllEffect(2, 0, Duration.EndOfTurn, filter, false)); } diff --git a/Mage.Sets/src/mage/sets/magic2011/AngelicArbiter.java b/Mage.Sets/src/mage/sets/magic2011/AngelicArbiter.java index 735e1ca7695..8212488674b 100644 --- a/Mage.Sets/src/mage/sets/magic2011/AngelicArbiter.java +++ b/Mage.Sets/src/mage/sets/magic2011/AngelicArbiter.java @@ -72,8 +72,7 @@ public class AngelicArbiter extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AngelicArbiterCantAttackTargetEffect(Duration.WhileOnBattlefield))); // Each opponent who attacked with a creature this turn can't cast spells. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AngelicArbiterEffect2())); - this.addWatcher(new AngelicArbiterWatcher2()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AngelicArbiterEffect2()), new AngelicArbiterWatcher2()); } public AngelicArbiter(final AngelicArbiter card) { diff --git a/Mage.Sets/src/mage/sets/magic2012/VengefulPharaoh.java b/Mage.Sets/src/mage/sets/magic2012/VengefulPharaoh.java index 44bc3e9619a..10abb1aac2d 100644 --- a/Mage.Sets/src/mage/sets/magic2012/VengefulPharaoh.java +++ b/Mage.Sets/src/mage/sets/magic2012/VengefulPharaoh.java @@ -28,22 +28,24 @@ package mage.sets.magic2012; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.MageInt; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.DeathtouchAbility; import mage.cards.Card; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.game.Game; import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; +import mage.game.turn.Step; import mage.players.Player; import mage.target.common.TargetAttackingCreature; @@ -58,11 +60,13 @@ public class VengefulPharaoh extends CardImpl { this.expansionSetCode = "M12"; this.subtype.add("Zombie"); - this.color.setBlack(true); this.power = new MageInt(5); this.toughness = new MageInt(4); + // Deathtouch (Any amount of damage this deals to a creature is enough to destroy it.) this.addAbility(DeathtouchAbility.getInstance()); + + // Whenever combat damage is dealt to you or a planeswalker you control, if Vengeful Pharaoh is in your graveyard, destroy target attacking creature, then put Vengeful Pharaoh on top of your library. this.addAbility(new VengefulPharaohTriggeredAbility()); } @@ -78,6 +82,12 @@ public class VengefulPharaoh extends CardImpl { class VengefulPharaohTriggeredAbility extends TriggeredAbilityImpl { + Step stepTriggeredPlayer; + int turnTriggeredPlayer; + + Step stepTriggeredPlansewalker; + int turnTriggeredPlaneswalker; + public VengefulPharaohTriggeredAbility() { super(Zone.GRAVEYARD, new VengefulPharaohEffect(), false); this.addTarget(new TargetAttackingCreature()); @@ -85,6 +95,10 @@ class VengefulPharaohTriggeredAbility extends TriggeredAbilityImpl { public VengefulPharaohTriggeredAbility(final VengefulPharaohTriggeredAbility ability) { super(ability); + this.stepTriggeredPlansewalker = ability.stepTriggeredPlansewalker; + this.turnTriggeredPlaneswalker = ability.turnTriggeredPlaneswalker; + this.stepTriggeredPlayer = ability.stepTriggeredPlayer; + this.turnTriggeredPlayer = ability.turnTriggeredPlayer; } @Override @@ -92,16 +106,41 @@ class VengefulPharaohTriggeredAbility extends TriggeredAbilityImpl { return new VengefulPharaohTriggeredAbility(this); } + @Override + public boolean checkInterveningIfClause(Game game) { + // 9/22/2011 - If multiple creatures deal combat damage to you and to a planeswalker you control + // simultaneously, Vengeful Pharaoh will trigger twice. The first trigger will cause Vengeful Pharaoh + // to be put on top of your library. The second trigger will then do nothing, as Vengeful Pharaoh is + // no longer in your graveyard when it tries to resolve. Note that the second trigger will do nothing + // even if Vengeful Pharaoh is put back into your graveyard before it tries to resolve, as it's a + // different Vengeful Pharaoh than the one that was there before. + MageObjectReference mor = new MageObjectReference(getSourceId(), game); + return mor.refersTo(this.getSourceObject(game)); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.DAMAGED_PLAYER || event.getType() == EventType.DAMAGED_PLANESWALKER; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if ((event.getType() == EventType.DAMAGED_PLAYER && event.getTargetId().equals(this.getControllerId())) && ((DamagedEvent) event).isCombatDamage()) { - return true; + if (!game.getPhase().getStep().equals(stepTriggeredPlayer) || game.getTurnNum() != turnTriggeredPlayer) { + stepTriggeredPlayer = game.getPhase().getStep(); + turnTriggeredPlayer = game.getTurnNum(); + return true; + } } if (event.getType() == EventType.DAMAGED_PLANESWALKER && ((DamagedEvent) event).isCombatDamage()) { Permanent permanent = game.getPermanent(event.getTargetId()); if (permanent != null && permanent.getControllerId().equals(this.getControllerId())) { - return true; + if (!game.getPhase().getStep().equals(stepTriggeredPlansewalker) || game.getTurnNum() != turnTriggeredPlaneswalker) { + stepTriggeredPlansewalker = game.getPhase().getStep(); + turnTriggeredPlaneswalker = game.getTurnNum(); + return true; + } } } return false; @@ -131,19 +170,16 @@ class VengefulPharaohEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - boolean applied = false; - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - permanent.destroy(source.getSourceId(), game, false); - applied = true; - } - Player player = game.getPlayer(source.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); Card card = game.getCard(source.getSourceId()); - if (card != null && player != null) { - player.getGraveyard().remove(card); - card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); - applied = true; + if (card != null && controller != null) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent != null) { + permanent.destroy(source.getSourceId(), game, false); + } + controller.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.GRAVEYARD, true, true); + return true; } - return applied; + return false; } } diff --git a/Mage.Sets/src/mage/sets/magic2013/FaithsReward.java b/Mage.Sets/src/mage/sets/magic2013/FaithsReward.java index 4c401fd5c05..c40581bf663 100644 --- a/Mage.Sets/src/mage/sets/magic2013/FaithsReward.java +++ b/Mage.Sets/src/mage/sets/magic2013/FaithsReward.java @@ -54,7 +54,7 @@ public class FaithsReward extends CardImpl { // Return to the battlefield all permanent cards in your graveyard that were put there from the battlefield this turn. this.getSpellAbility().addEffect(new FaithsRewardEffect()); - this.addWatcher(new FaithsRewardWatcher()); + this.getSpellAbility().addWatcher(new FaithsRewardWatcher()); } public FaithsReward(final FaithsReward card) { diff --git a/Mage.Sets/src/mage/sets/magic2014/AngelicAccord.java b/Mage.Sets/src/mage/sets/magic2014/AngelicAccord.java index f7d1ba7717d..8df4cbc6f36 100644 --- a/Mage.Sets/src/mage/sets/magic2014/AngelicAccord.java +++ b/Mage.Sets/src/mage/sets/magic2014/AngelicAccord.java @@ -55,8 +55,7 @@ public class AngelicAccord extends CardImpl { this.color.setWhite(true); // At the beginning of each end step, if you gained 4 or more life this turn, put a 4/4 white Angel creature token with flying onto the battlefield. - this.addAbility(new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new AngelToken()), TargetController.ANY, new YouGainedLifeCondition(Condition.ComparisonType.GreaterThan, 3), false)); - this.addWatcher(new PlayerGainedLifeWatcher()); + this.addAbility(new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new AngelToken()), TargetController.ANY, new YouGainedLifeCondition(Condition.ComparisonType.GreaterThan, 3), false), new PlayerGainedLifeWatcher()); } public AngelicAccord(final AngelicAccord card) { diff --git a/Mage.Sets/src/mage/sets/magic2014/GrimReturn.java b/Mage.Sets/src/mage/sets/magic2014/GrimReturn.java index 9ab3bbf70ae..efc2d5604ab 100644 --- a/Mage.Sets/src/mage/sets/magic2014/GrimReturn.java +++ b/Mage.Sets/src/mage/sets/magic2014/GrimReturn.java @@ -63,7 +63,7 @@ public class GrimReturn extends CardImpl { effect.setText("Choose target creature card in a graveyard that was put there from the battlefield this turn. Put that card onto the battlefield under your control"); this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); this.getSpellAbility().addTarget(new TargetCardInGraveyard(new FilterCreatureCard(textFilter))); - this.addWatcher(new CardsPutIntoGraveyardWatcher()); + this.getSpellAbility().addWatcher(new CardsPutIntoGraveyardWatcher()); } public GrimReturn(final GrimReturn card) { diff --git a/Mage.Sets/src/mage/sets/magic2014/SavageSummoning.java b/Mage.Sets/src/mage/sets/magic2014/SavageSummoning.java index b80573d13c1..b6f26783e9e 100644 --- a/Mage.Sets/src/mage/sets/magic2014/SavageSummoning.java +++ b/Mage.Sets/src/mage/sets/magic2014/SavageSummoning.java @@ -76,7 +76,7 @@ public class SavageSummoning extends CardImpl { this.getSpellAbility().addEffect(new SavageSummoningAsThoughEffect()); this.getSpellAbility().addEffect(new SavageSummoningCantCounterEffect()); this.getSpellAbility().addEffect(new SavageSummoningEntersBattlefieldEffect()); - this.addWatcher(new SavageSummoningWatcher()); + this.getSpellAbility().addWatcher(new SavageSummoningWatcher()); } diff --git a/Mage.Sets/src/mage/sets/magic2014/TidebinderMage.java b/Mage.Sets/src/mage/sets/magic2014/TidebinderMage.java index 5d790d22e61..608a2d225c8 100644 --- a/Mage.Sets/src/mage/sets/magic2014/TidebinderMage.java +++ b/Mage.Sets/src/mage/sets/magic2014/TidebinderMage.java @@ -83,8 +83,7 @@ public class TidebinderMage extends CardImpl { ability.addEffect(new TidebinderMageEffect()); Target target = new TargetCreaturePermanent(filter); ability.addTarget(target); - this.addAbility(ability); - this.addWatcher(new TidebinderMageWatcher()); + this.addAbility(ability, new TidebinderMageWatcher()); } diff --git a/Mage.Sets/src/mage/sets/magic2014/VoraciousWurm.java b/Mage.Sets/src/mage/sets/magic2014/VoraciousWurm.java index 3167a3b5737..c2c98377caf 100644 --- a/Mage.Sets/src/mage/sets/magic2014/VoraciousWurm.java +++ b/Mage.Sets/src/mage/sets/magic2014/VoraciousWurm.java @@ -36,6 +36,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.counters.CounterType; +import mage.watchers.common.PlayerGainedLifeWatcher; /** * @@ -54,7 +55,8 @@ public class VoraciousWurm extends CardImpl { // Voracious Wurm enters the battlefield with X +1/+1 counters on it, where X is the amount of life you've gained this turn. this.addAbility(new EntersBattlefieldAbility( - new AddCountersSourceEffect(CounterType.P1P1.createInstance(0), ControllerGotLifeCount.getInstance(this), true))); + new AddCountersSourceEffect(CounterType.P1P1.createInstance(0), ControllerGotLifeCount.getInstance(), true)), + new PlayerGainedLifeWatcher()); } public VoraciousWurm(final VoraciousWurm card) { diff --git a/Mage.Sets/src/mage/sets/magic2015/AEtherspouts.java b/Mage.Sets/src/mage/sets/magic2015/AEtherspouts.java index a75ec54960a..e4bce4bc10a 100644 --- a/Mage.Sets/src/mage/sets/magic2015/AEtherspouts.java +++ b/Mage.Sets/src/mage/sets/magic2015/AEtherspouts.java @@ -106,6 +106,7 @@ class AEtherspoutsEffect extends OneShotEffect { PlayerList playerList = game.getPlayerList(); playerList.setCurrent(game.getActivePlayerId()); Player player = game.getPlayer(game.getActivePlayerId()); + Player activePlayer = player; do { ArrayList permanentsToTop = new ArrayList<>(); ArrayList permanentsToBottom = new ArrayList<>(); @@ -134,7 +135,10 @@ class AEtherspoutsEffect extends OneShotEffect { } } TargetCard target = new TargetCard(Zone.BATTLEFIELD, new FilterCard("order to put on the top of library (last choosen will be the top most)")); - while (player.isInGame() && cards.size() > 1) { + while (cards.size() > 1) { + if (!player.isInGame()) { + return false; + } player.choose(Outcome.Neutral, cards, target, game); Card card = cards.get(target.getFirstTarget(), game); if (card != null) { @@ -173,6 +177,7 @@ class AEtherspoutsEffect extends OneShotEffect { target = new TargetCard(Zone.BATTLEFIELD, new FilterCard("order to put on bottom of library (last choosen will be bottommost card)")); while (player.isInGame() && cards.size() > 1) { player.choose(Outcome.Neutral, cards, target, game); + Card card = cards.get(target.getFirstTarget(), game); if (card != null) { cards.remove(card); @@ -195,7 +200,7 @@ class AEtherspoutsEffect extends OneShotEffect { player.moveCardToLibraryWithInfo(permanent, source.getSourceId(), game, Zone.BATTLEFIELD, false, false); } player = playerList.getNext(game); - } while (player != null && !player.getId().equals(game.getActivePlayerId())); + } while (player != null && !player.getId().equals(game.getActivePlayerId()) && activePlayer.isInGame()); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/magic2015/FirstResponse.java b/Mage.Sets/src/mage/sets/magic2015/FirstResponse.java index 5f11aefc9a2..57c2a3cefe6 100644 --- a/Mage.Sets/src/mage/sets/magic2015/FirstResponse.java +++ b/Mage.Sets/src/mage/sets/magic2015/FirstResponse.java @@ -54,9 +54,7 @@ public class FirstResponse extends CardImpl { this.color.setWhite(true); // At the beginning of each upkeep, if you lost life last turn, put a 1/1 white Soldier creature token onto the battlefield. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new FirstResponseEffect(), TargetController.ANY, false)); - - this.addWatcher(new PlayerLostLifeWatcher()); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new FirstResponseEffect(), TargetController.ANY, false), new PlayerLostLifeWatcher()); } diff --git a/Mage.Sets/src/mage/sets/magic2015/GeneratorServant.java b/Mage.Sets/src/mage/sets/magic2015/GeneratorServant.java index e47437bf5db..d25998010ec 100644 --- a/Mage.Sets/src/mage/sets/magic2015/GeneratorServant.java +++ b/Mage.Sets/src/mage/sets/magic2015/GeneratorServant.java @@ -79,8 +79,7 @@ public class GeneratorServant extends CardImpl { ability.getEffects().get(0).setText("Add {2} to your mana pool. If that mana is spent on a creature spell, it gains haste until end of turn."); this.addAbility(ability); - this.addWatcher(new GeneratorServantWatcher()); - this.addAbility(new SimpleStaticAbility(Zone.ALL, new GeneratorServantHasteEffect())); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new GeneratorServantHasteEffect()), new GeneratorServantWatcher()); } public GeneratorServant(final GeneratorServant card) { diff --git a/Mage.Sets/src/mage/sets/magic2015/ReturnToTheRanks.java b/Mage.Sets/src/mage/sets/magic2015/ReturnToTheRanks.java index 1786632046e..2bd7ada215c 100644 --- a/Mage.Sets/src/mage/sets/magic2015/ReturnToTheRanks.java +++ b/Mage.Sets/src/mage/sets/magic2015/ReturnToTheRanks.java @@ -29,7 +29,6 @@ package mage.sets.magic2015; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.keyword.ConvokeAbility; @@ -58,8 +57,6 @@ public class ReturnToTheRanks extends CardImpl { super(ownerId, 29, "Return to the Ranks", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{X}{W}{W}"); this.expansionSetCode = "M15"; - this.color.setWhite(true); - // Convoke this.addAbility(new ConvokeAbility()); // Return X target creature cards with converted mana cost 2 or less from your graveyard to the battlefield. diff --git a/Mage.Sets/src/mage/sets/magic2015/TheChainVeil.java b/Mage.Sets/src/mage/sets/magic2015/TheChainVeil.java index 02e8912dbb1..069d940f978 100644 --- a/Mage.Sets/src/mage/sets/magic2015/TheChainVeil.java +++ b/Mage.Sets/src/mage/sets/magic2015/TheChainVeil.java @@ -66,8 +66,7 @@ public class TheChainVeil extends CardImpl { this.supertype.add("Legendary"); // At the beginning of your end step, if you didn't activate a loyalty ability of a planeswalker this turn, you lose 2 life. - this.addAbility(new BeginningOfEndStepTriggeredAbility(new TheChainVeilTriggeredEffect(), TargetController.YOU, false)); - this.addWatcher(new ActivatedLoyaltyAbilityWatcher()); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new TheChainVeilTriggeredEffect(), TargetController.YOU, false), new ActivatedLoyaltyAbilityWatcher()); // {4}, {T}: For each planeswalker you control, you may activate one of its loyalty abilities once this turn as though none of its loyalty abilities had been activated this turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TheChainVeilIncreaseLoyaltyUseEffect(), diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/Pulverize.java b/Mage.Sets/src/mage/sets/mercadianmasques/Pulverize.java index 7c59715dd64..42ae20754d5 100644 --- a/Mage.Sets/src/mage/sets/mercadianmasques/Pulverize.java +++ b/Mage.Sets/src/mage/sets/mercadianmasques/Pulverize.java @@ -34,8 +34,8 @@ import mage.abilities.effects.common.DestroyAllEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.FilterPermanent; import mage.filter.common.FilterArtifactPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.target.common.TargetControlledPermanent; @@ -45,7 +45,7 @@ import mage.target.common.TargetControlledPermanent; */ public class Pulverize extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("two Mountains"); + private static final FilterControlledPermanent filter = new FilterControlledPermanent("two Mountains"); static { filter.add(new SubtypePredicate("Mountain")); } diff --git a/Mage.Sets/src/mage/sets/mirage/AsmiraHolyAvenger.java b/Mage.Sets/src/mage/sets/mirage/AsmiraHolyAvenger.java index 99ff6bbea57..82e7c8f7bed 100644 --- a/Mage.Sets/src/mage/sets/mirage/AsmiraHolyAvenger.java +++ b/Mage.Sets/src/mage/sets/mirage/AsmiraHolyAvenger.java @@ -70,8 +70,7 @@ public class AsmiraHolyAvenger extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // At the beginning of each end step, put a +1/+1 counter on Asmira, Holy Avenger for each creature put into your graveyard from the battlefield this turn. - this.addWatcher(new AsmiraHolyAvengerWatcher()); - this.addAbility(new BeginningOfEndStepTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(0), new AsmiraHolyAvengerDynamicValue(), true), TargetController.ANY, false)); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(0), new AsmiraHolyAvengerDynamicValue(), true), TargetController.ANY, false), new AsmiraHolyAvengerWatcher()); } public AsmiraHolyAvenger(final AsmiraHolyAvenger card) { diff --git a/Mage.Sets/src/mage/sets/mirage/DwarvenMiner.java b/Mage.Sets/src/mage/sets/mirage/DwarvenMiner.java new file mode 100644 index 00000000000..a62127cea2a --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirage/DwarvenMiner.java @@ -0,0 +1,55 @@ +/* + * 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.sets.mirage; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.Target; +import mage.target.common.TargetNonBasicLandPermanent; + +/** + * + * @author nick.myers + */ +public class DwarvenMiner extends CardImpl { + + public DwarvenMiner(UUID ownerId) { + super(ownerId, 169, "Dwarven Miner", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{R}"); + this.expansionSetCode = "MIR"; + this.subtype.add("Dwarf"); + + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // {2}{R}, {tap}: Destroy target nonbasic land + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{2}{R}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetNonBasicLandPermanent()); + this.addAbility(ability); + + } + + public DwarvenMiner(final DwarvenMiner card) { + super(card); + } + + @Override + public DwarvenMiner copy() { + return new DwarvenMiner(this); + } + + + +} diff --git a/Mage.Sets/src/mage/sets/mirrodin/ExtraplanarLens.java b/Mage.Sets/src/mage/sets/mirrodin/ExtraplanarLens.java new file mode 100644 index 00000000000..dc703c4389b --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirrodin/ExtraplanarLens.java @@ -0,0 +1,178 @@ +/* + * 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.sets.mirrodin; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AddManaOfAnyTypeProducedEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ManaEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +/** + * + * @author jeffwadsworth + */ +public class ExtraplanarLens extends CardImpl { + + public ExtraplanarLens(UUID ownerId) { + super(ownerId, 169, "Extraplanar Lens", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "MRD"; + + // Imprint - When Extraplanar Lens enters the battlefield, you may exile target land you control. + this.addAbility(new EntersBattlefieldTriggeredAbility(new ExtraplanarLensImprintEffect(), true, "Imprint - ")); + + // Whenever a land with the same name as the exiled card is tapped for mana, its controller adds one mana to his or her mana pool of any type that land produced. + this.addAbility(new ExtraplanarLensTriggeredAbility()); + + } + + public ExtraplanarLens(final ExtraplanarLens card) { + super(card); + } + + @Override + public ExtraplanarLens copy() { + return new ExtraplanarLens(this); + } +} + +class ExtraplanarLensImprintEffect extends OneShotEffect { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("land you control"); + + static { + filter.add(new CardTypePredicate(CardType.LAND)); + } + + public ExtraplanarLensImprintEffect() { + super(Outcome.Neutral); + staticText = "you may exile target land you control"; + } + + public ExtraplanarLensImprintEffect(ExtraplanarLensImprintEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent extraplanarLens = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (controller != null) { + if (game.getBattlefield().countAll(filter, controller.getId(), game) > 0) { + TargetPermanent target = new TargetPermanent(1, filter); + if (target.canChoose(source.getSourceId(), source.getControllerId(), game) + && controller.choose(Outcome.Neutral, target, source.getSourceId(), game)) { + Permanent targetLand = game.getPermanent(target.getFirstTarget()); + if (targetLand != null) { + targetLand.moveToExile(null, extraplanarLens.getLogName() + " (Imprint)", source.getSourceId(), game); + extraplanarLens.imprint(targetLand.getId(), game); + extraplanarLens.addInfo("imprint", CardUtil.addToolTipMarkTags("[Imprinted card - " + targetLand.getLogName() + "]")); + } + } + return true; + } + } + return false; + } + + @Override + public ExtraplanarLensImprintEffect copy() { + return new ExtraplanarLensImprintEffect(this); + } + +} + +class ExtraplanarLensTriggeredAbility extends TriggeredAbilityImpl { + + public ExtraplanarLensTriggeredAbility() { + super(Zone.BATTLEFIELD, new AddManaOfAnyTypeProducedEffect()); + this.usesStack = false; + } + + public ExtraplanarLensTriggeredAbility(final ExtraplanarLensTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TAPPED_FOR_MANA; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent landTappedForMana = game.getPermanentOrLKIBattlefield(event.getSourceId()); + Permanent extraplanarLens = game.getPermanent(getSourceId()); + if (extraplanarLens != null + && landTappedForMana != null + && !extraplanarLens.getImprinted().isEmpty()) { + Card imprinted = game.getCard(extraplanarLens.getImprinted().get(0)); + if (imprinted != null + && game.getState().getZone(imprinted.getId()).equals(Zone.EXILED)) { + if (landTappedForMana.getName().equals(imprinted.getName()) + && landTappedForMana.getCardType().contains(CardType.LAND)) { + ManaEvent mEvent = (ManaEvent) event; + for (Effect effect : getEffects()) { + effect.setValue("mana", mEvent.getMana()); + } + getEffects().get(0).setTargetPointer(new FixedTarget(landTappedForMana.getId())); + return true; + } + } + } + return false; + } + + @Override + public String getRule() { + return new StringBuilder("Whenever a land with the same name as the exiled card is tapped for mana, ").append(super.getRule()).toString(); + } + + @Override + public ExtraplanarLensTriggeredAbility copy() { + return new ExtraplanarLensTriggeredAbility(this); + } + +} diff --git a/Mage.Sets/src/mage/sets/mirrodin/SecondSunrise.java b/Mage.Sets/src/mage/sets/mirrodin/SecondSunrise.java index 06389159a51..9f293f791de 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/SecondSunrise.java +++ b/Mage.Sets/src/mage/sets/mirrodin/SecondSunrise.java @@ -54,7 +54,7 @@ public class SecondSunrise extends CardImpl { // Each player returns to the battlefield all artifact, creature, enchantment, and land cards in his or her graveyard that were put there from the battlefield this turn. this.getSpellAbility().addEffect(new SecondSunriseEffect()); - this.addWatcher(new SecondSunriseWatcher()); + this.getSpellAbility().addWatcher(new SecondSunriseWatcher()); } public SecondSunrise(final SecondSunrise card) { diff --git a/Mage.Sets/src/mage/sets/mirrodin/SoulFoundry.java b/Mage.Sets/src/mage/sets/mirrodin/SoulFoundry.java new file mode 100644 index 00000000000..21843f6da21 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirrodin/SoulFoundry.java @@ -0,0 +1,188 @@ +/* + * 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.sets.mirrodin; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.VariableCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.EmptyToken; +import mage.players.Player; +import mage.target.TargetCard; +import mage.util.CardUtil; + +/** + * + * @author jeffwadsworth + */ +public class SoulFoundry extends CardImpl { + + public SoulFoundry(UUID ownerId) { + super(ownerId, 246, "Soul Foundry", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{4}"); + this.expansionSetCode = "MRD"; + + // Imprint - When Soul Foundry enters the battlefield, you may exile a creature card from your hand. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SoulFoundryImprintEffect(), true, "Imprint - ")); + + // {X}, {tap}: Put a token that's a copy of the exiled card onto the battlefield. X is the converted mana cost of that card. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SoulFoundryEffect(), new ManaCostsImpl("{X}")); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + } + + public SoulFoundry(final SoulFoundry card) { + super(card); + } + + @Override + public void adjustCosts(Ability ability, Game game) { + Permanent card = game.getPermanent(ability.getSourceId()); + if (card != null) { + if (card.getImprinted().size() > 0) { + Card imprinted = game.getCard(card.getImprinted().get(0)); + if (imprinted != null) { + ability.getManaCostsToPay().add(0, new GenericManaCost(imprinted.getManaCost().convertedManaCost())); + } + } + } + + // no {X} anymore as we already have imprinted the card with defined manacost + for (ManaCost cost : ability.getManaCostsToPay()) { + if (cost instanceof VariableCost) { + cost.setPaid(); + } + } + } + + @Override + public SoulFoundry copy() { + return new SoulFoundry(this); + } +} + +class SoulFoundryImprintEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard("creature card from your hand"); + + static { + filter.add(new CardTypePredicate(CardType.CREATURE)); + } + + public SoulFoundryImprintEffect() { + super(Outcome.Neutral); + staticText = "you may exile a creature card from your hand"; + } + + public SoulFoundryImprintEffect(SoulFoundryImprintEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (controller != null) { + if (controller.getHand().size() > 0) { + TargetCard target = new TargetCard(Zone.HAND, filter); + if (target.canChoose(source.getSourceId(), source.getControllerId(), game) + && controller.choose(Outcome.Benefit, controller.getHand(), target, game)) { + Card card = controller.getHand().get(target.getFirstTarget(), game); + if (card != null) { + controller.moveCardToExileWithInfo(card, source.getSourceId(), sourcePermanent.getLogName() + " (Imprint)", source.getSourceId(), game, Zone.HAND); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + permanent.imprint(card.getId(), game); + permanent.addInfo("imprint", CardUtil.addToolTipMarkTags("[Imprinted card - " + card.getLogName() + "]")); + } + } + } + } + return true; + } + return false; + } + + @Override + public SoulFoundryImprintEffect copy() { + return new SoulFoundryImprintEffect(this); + } +} + +class SoulFoundryEffect extends OneShotEffect { + + public SoulFoundryEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "Put a token that's a copy of the exiled card onto the battlefield. X is the converted mana cost of that card"; + } + + public SoulFoundryEffect(final SoulFoundryEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Permanent soulFoundry = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (soulFoundry != null + && soulFoundry.getImprinted() != null + && !soulFoundry.getImprinted().isEmpty()) { + Card imprinted = game.getCard(soulFoundry.getImprinted().get(0)); + if (imprinted != null + && game.getState().getZone(imprinted.getId()).equals(Zone.EXILED)) { + EmptyToken token = new EmptyToken(); + CardUtil.copyTo(token).from(imprinted); + return token.putOntoBattlefield(1, game, source.getSourceId(), controller.getId()); + } + } + } + return false; + } + + @Override + public SoulFoundryEffect copy() { + return new SoulFoundryEffect(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/RedSunsZenith.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/RedSunsZenith.java index 654ad5caa02..8902296827a 100644 --- a/Mage.Sets/src/mage/sets/mirrodinbesieged/RedSunsZenith.java +++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/RedSunsZenith.java @@ -54,7 +54,7 @@ public class RedSunsZenith extends CardImpl { this.getSpellAbility().addEffect(new DamageTargetEffect(new ManacostVariableValue())); this.getSpellAbility().addEffect(new DealtDamageToCreatureBySourceDies(this, Duration.EndOfTurn)); this.getSpellAbility().addEffect(ShuffleSpellEffect.getInstance()); - this.addWatcher(new DamagedByWatcher()); + this.getSpellAbility().addWatcher(new DamagedByWatcher()); } public RedSunsZenith(final RedSunsZenith card) { diff --git a/Mage.Sets/src/mage/sets/modernmasters/Epochrasite.java b/Mage.Sets/src/mage/sets/modernmasters/Epochrasite.java index 5d2066df8a3..335aaa10ad0 100644 --- a/Mage.Sets/src/mage/sets/modernmasters/Epochrasite.java +++ b/Mage.Sets/src/mage/sets/modernmasters/Epochrasite.java @@ -60,13 +60,12 @@ public class Epochrasite extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addWatcher(new CastFromHandWatcher()); - // Epochrasite enters the battlefield with three +1/+1 counters on it if you didn't cast it from your hand. this.addAbility(new EntersBattlefieldAbility( new AddCountersSourceEffect(CounterType.P1P1.createInstance(3)), new InvertCondition(new CastFromHandCondition()), true, - "{this} enters the battlefield with three +1/+1 counters on it if you didn't cast it from your hand","")); + "{this} enters the battlefield with three +1/+1 counters on it if you didn't cast it from your hand",""), + new CastFromHandWatcher()); // When Epochrasite dies, exile it with three time counters on it and it gains suspend. Ability ability = new DiesTriggeredAbility(new ExileSourceEffect()); diff --git a/Mage.Sets/src/mage/sets/modernmasters/JhoiraOfTheGhitu.java b/Mage.Sets/src/mage/sets/modernmasters/JhoiraOfTheGhitu.java index 111b094beaa..4670aafb5e6 100644 --- a/Mage.Sets/src/mage/sets/modernmasters/JhoiraOfTheGhitu.java +++ b/Mage.Sets/src/mage/sets/modernmasters/JhoiraOfTheGhitu.java @@ -142,12 +142,14 @@ class JhoiraOfTheGhituSuspendEffect extends OneShotEffect { for (Ability ability :card.getAbilities()) { if (!oldAbilities.contains(ability)) { ability.setControllerId(source.getControllerId()); - game.getState().addAbility(ability, card.getId(), card); + ability.setSourceId(source.getSourceId()); + ability.setSourceObject(source.getSourceObject(game)); + game.getState().addAbility(ability, card); } } } - game.informPlayers(new StringBuilder(controller.getName()).append(" suspends (").append(4).append(") ").append(card.getName()).toString()); + game.informPlayers(controller.getName() + " suspends 4 - " + card.getName()); return true; } } diff --git a/Mage.Sets/src/mage/sets/modernmasters/RiftElemental.java b/Mage.Sets/src/mage/sets/modernmasters/RiftElemental.java index 0c97786fbea..99a2b265f93 100644 --- a/Mage.Sets/src/mage/sets/modernmasters/RiftElemental.java +++ b/Mage.Sets/src/mage/sets/modernmasters/RiftElemental.java @@ -150,16 +150,16 @@ class RemoveCounterFromCardCost extends CostImpl { for (UUID targetId: (List)target.getTargets()) { Card card = game.getCard(targetId); if (card != null) { - if (card.getCounters().size() > 0 && (counterTypeToRemove == null || card.getCounters().containsKey(counterTypeToRemove))) { + if (card.getCounters(game).size() > 0 && (counterTypeToRemove == null || card.getCounters(game).containsKey(counterTypeToRemove))) { String counterName = null; if (counterTypeToRemove != null) { counterName = counterTypeToRemove.getName(); } else { - if (card.getCounters().size() > 1 && counterTypeToRemove == null) { + if (card.getCounters(game).size() > 1 && counterTypeToRemove == null) { Choice choice = new ChoiceImpl(true); Set choices = new HashSet(); - for (Counter counter : card.getCounters().values()) { - if (card.getCounters().getCount(counter.getName()) > 0) { + for (Counter counter : card.getCounters(game).values()) { + if (card.getCounters(game).getCount(counter.getName()) > 0) { choices.add(counter.getName()); } } @@ -168,7 +168,7 @@ class RemoveCounterFromCardCost extends CostImpl { controller.choose(Outcome.UnboostCreature, choice, game); counterName = choice.getChoice(); } else { - for (Counter counter : card.getCounters().values()) { + for (Counter counter : card.getCounters(game).values()) { if (counter.getCount() > 0) { counterName = counter.getName(); } @@ -177,8 +177,8 @@ class RemoveCounterFromCardCost extends CostImpl { } if (counterName != null) { card.removeCounters(counterName, 1, game); - if (card.getCounters().getCount(counterName) == 0 ){ - card.getCounters().removeCounter(counterName); + if (card.getCounters(game).getCount(counterName) == 0 ){ + card.getCounters(game).removeCounter(counterName); } this.paid = true; game.informPlayers(new StringBuilder(controller.getName()).append(" removes a ").append(counterName).append(" counter from ").append(card.getName()).toString()); diff --git a/Mage.Sets/src/mage/sets/modernmasters/Riftsweeper.java b/Mage.Sets/src/mage/sets/modernmasters/Riftsweeper.java index 8e54a022d97..1e0a8933ef9 100644 --- a/Mage.Sets/src/mage/sets/modernmasters/Riftsweeper.java +++ b/Mage.Sets/src/mage/sets/modernmasters/Riftsweeper.java @@ -103,7 +103,7 @@ class RiftsweeperEffect extends OneShotEffect { Card card = game.getCard(targetPointer.getFirst(game, source)); if (card != null) { // remove exiting suspend counters - card.getCounters().clear(); + card.getCounters(game).clear(); // move to exile card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); game.getPlayer(card.getOwnerId()).shuffleLibrary(game); diff --git a/Mage.Sets/src/mage/sets/morningtide/BramblewoodParagon.java b/Mage.Sets/src/mage/sets/morningtide/BramblewoodParagon.java index ce2e58e89a1..419dbca04d7 100644 --- a/Mage.Sets/src/mage/sets/morningtide/BramblewoodParagon.java +++ b/Mage.Sets/src/mage/sets/morningtide/BramblewoodParagon.java @@ -106,7 +106,7 @@ class BramblewoodParagonReplacementEffect extends ReplacementEffectImpl { Permanent creature = game.getPermanent(event.getTargetId()); if (creature != null && creature.getControllerId().equals(source.getControllerId()) && creature.getCardType().contains(CardType.CREATURE) - && creature.getSubtype().contains("Warrior") + && creature.hasSubtype("Warrior") && !event.getTargetId().equals(source.getSourceId())) { return true; } diff --git a/Mage.Sets/src/mage/sets/morningtide/DeclarationOfNaught.java b/Mage.Sets/src/mage/sets/morningtide/DeclarationOfNaught.java new file mode 100644 index 00000000000..7f91e4a9af4 --- /dev/null +++ b/Mage.Sets/src/mage/sets/morningtide/DeclarationOfNaught.java @@ -0,0 +1,87 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.morningtide; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.effects.common.NameACardEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.game.Game; +import mage.target.TargetSpell; + +/** + * + * @author jeffwadsworth + */ +public class DeclarationOfNaught extends CardImpl { + + static final private FilterSpell filter = new FilterSpell("spell with the chosen name"); + + public DeclarationOfNaught(UUID ownerId) { + super(ownerId, 29, "Declaration of Naught", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{U}{U}"); + this.expansionSetCode = "MOR"; + + // As Declaration of Naught enters the battlefield, name a card. + this.addAbility(new AsEntersBattlefieldAbility(new NameACardEffect(NameACardEffect.TypeOfName.ALL))); + + // {U}: Counter target spell with the chosen name. + SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterTargetEffect(), new ManaCostsImpl("{U}")); + ability.addTarget(new TargetSpell(filter)); + this.addAbility(ability); + + } + + @Override + public void adjustTargets(Ability ability, Game game) { + if (ability instanceof SimpleActivatedAbility) { + ability.getTargets().clear(); + FilterSpell filter2 = new FilterSpell("spell with the chosen name"); + filter2.add(new NamePredicate((String) game.getState().getValue(ability.getSourceId().toString() + NameACardEffect.INFO_KEY))); + TargetSpell target = new TargetSpell(1, filter2); + ability.addTarget(target); + } + } + + public DeclarationOfNaught(final DeclarationOfNaught card) { + super(card); + } + + @Override + public DeclarationOfNaught copy() { + return new DeclarationOfNaught(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/newphyrexia/CathedralMembrane.java b/Mage.Sets/src/mage/sets/newphyrexia/CathedralMembrane.java index f0adc7beb34..6b9ded4e10b 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/CathedralMembrane.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/CathedralMembrane.java @@ -62,8 +62,7 @@ public class CathedralMembrane extends CardImpl { this.addAbility(DefenderAbility.getInstance()); // When Cathedral Membrane dies during combat, it deals 6 damage to each creature it blocked this combat. - this.addWatcher(new CathedralMembraneWatcher()); - this.addAbility(new CathedralMembraneAbility()); + this.addAbility(new CathedralMembraneAbility(), new CathedralMembraneWatcher()); } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/FreshMeat.java b/Mage.Sets/src/mage/sets/newphyrexia/FreshMeat.java index 661d5a01f72..6c352248ab1 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/FreshMeat.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/FreshMeat.java @@ -59,7 +59,7 @@ public class FreshMeat extends CardImpl { this.color.setGreen(true); - this.addWatcher(new FreshMeatWatcher()); + this.getSpellAbility().addWatcher(new FreshMeatWatcher()); this.getSpellAbility().addEffect(new CreateTokenEffect(new BeastToken(), new FreshMeatDynamicValue())); } diff --git a/Mage.Sets/src/mage/sets/ninthedition/ForceOfNature.java b/Mage.Sets/src/mage/sets/ninthedition/ForceOfNature.java new file mode 100644 index 00000000000..c3c06e4d9b8 --- /dev/null +++ b/Mage.Sets/src/mage/sets/ninthedition/ForceOfNature.java @@ -0,0 +1,52 @@ +/* + * 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.sets.ninthedition; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class ForceOfNature extends mage.sets.fourthedition.ForceOfNature { + + public ForceOfNature(UUID ownerId) { + super(ownerId); + this.cardNumber = 242; + this.expansionSetCode = "9ED"; + } + + public ForceOfNature(final ForceOfNature card) { + super(card); + } + + @Override + public ForceOfNature copy() { + return new ForceOfNature(this); + } +} diff --git a/Mage.Sets/src/mage/sets/odyssey/Dreamwinder.java b/Mage.Sets/src/mage/sets/odyssey/Dreamwinder.java index d9a583d09a3..a4991f9c39a 100644 --- a/Mage.Sets/src/mage/sets/odyssey/Dreamwinder.java +++ b/Mage.Sets/src/mage/sets/odyssey/Dreamwinder.java @@ -41,7 +41,9 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; import mage.constants.Zone; +import mage.filter.common.FilterControlledLandPermanent; import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; import mage.target.Target; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetLandPermanent; @@ -57,7 +59,6 @@ public class Dreamwinder extends CardImpl { this.expansionSetCode = "ODY"; this.subtype.add("Serpent"); - this.color.setBlue(true); this.power = new MageInt(4); this.toughness = new MageInt(3); @@ -67,7 +68,9 @@ public class Dreamwinder extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesBasicLandTargetEffect(Duration.EndOfTurn, "Island"), new ManaCostsImpl("{U}")); Target target = new TargetLandPermanent(); ability.addTarget(target); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(1, 1, new FilterLandPermanent("Island","an Island"), true))); + FilterControlledLandPermanent filter = new FilterControlledLandPermanent("an Island"); + filter.add(new SubtypePredicate("Island")); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(1, 1, filter, true))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/onslaught/ReadTheRunes.java b/Mage.Sets/src/mage/sets/onslaught/ReadTheRunes.java index 4b2a5e54b6b..ea7e7998ce4 100644 --- a/Mage.Sets/src/mage/sets/onslaught/ReadTheRunes.java +++ b/Mage.Sets/src/mage/sets/onslaught/ReadTheRunes.java @@ -34,7 +34,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; -import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -85,11 +85,11 @@ class ReadTheRunesEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - int drawnCards = player.drawCards(source.getManaCostsToPay().getX(), game); - Target target = new TargetControlledPermanent(0, drawnCards, new FilterPermanent(), true); - target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + int drawnCards = controller.drawCards(source.getManaCostsToPay().getX(), game); + Target target = new TargetControlledPermanent(0, drawnCards, new FilterControlledPermanent(), true); + controller.chooseTarget(Outcome.Sacrifice, target, source, game); int sacrificedPermanents = 0; for (UUID permanentId : target.getTargets()) { Permanent permanent = game.getPermanent(permanentId); @@ -99,7 +99,7 @@ class ReadTheRunesEffect extends OneShotEffect { } } } - player.discard(drawnCards - sacrificedPermanents, source, game); + controller.discard(drawnCards - sacrificedPermanents, false, source, game); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/planeshift/SunkenHope.java b/Mage.Sets/src/mage/sets/planeshift/SunkenHope.java index 5d03d7eeba2..255b4d31e4c 100644 --- a/Mage.Sets/src/mage/sets/planeshift/SunkenHope.java +++ b/Mage.Sets/src/mage/sets/planeshift/SunkenHope.java @@ -38,8 +38,7 @@ import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.ControllerPredicate; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -97,10 +96,7 @@ class ReturnToHandEffect extends OneShotEffect { return false; } - FilterCreaturePermanent filter = new FilterCreaturePermanent(); - filter.add(new ControllerPredicate(TargetController.YOU)); - Target target = new TargetControlledPermanent(1, 1, filter, true); - + Target target = new TargetControlledPermanent(1, 1, new FilterControlledCreaturePermanent(), true); if (target.canChoose(player.getId(), game)) { while (player.isInGame() && !target.isChosen() && target.canChoose(player.getId(), game)) { player.chooseTarget(Outcome.ReturnToHand, target, source, game); diff --git a/Mage.Sets/src/mage/sets/returntoravnica/AvengingArrow.java b/Mage.Sets/src/mage/sets/returntoravnica/AvengingArrow.java index 4a035e105e9..2f1b7f9fb74 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/AvengingArrow.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/AvengingArrow.java @@ -56,7 +56,7 @@ public class AvengingArrow extends CardImpl { // Destroy target creature that dealt damage this turn. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addTarget(new AvengingArrowTarget()); - this.addWatcher(new SourceDidDamageWatcher()); + this.getSpellAbility().addWatcher(new SourceDidDamageWatcher()); } public AvengingArrow(final AvengingArrow card) { diff --git a/Mage.Sets/src/mage/sets/revisededition/ForceOfNature.java b/Mage.Sets/src/mage/sets/revisededition/ForceOfNature.java new file mode 100644 index 00000000000..92c3cfc57a2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/revisededition/ForceOfNature.java @@ -0,0 +1,52 @@ +/* + * 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.sets.revisededition; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class ForceOfNature extends mage.sets.fourthedition.ForceOfNature { + + public ForceOfNature(UUID ownerId) { + super(ownerId); + this.cardNumber = 103; + this.expansionSetCode = "3ED"; + } + + public ForceOfNature(final ForceOfNature card) { + super(card); + } + + @Override + public ForceOfNature copy() { + return new ForceOfNature(this); + } +} diff --git a/Mage.Sets/src/mage/sets/revisededition/Fork.java b/Mage.Sets/src/mage/sets/revisededition/Fork.java new file mode 100644 index 00000000000..146227cf017 --- /dev/null +++ b/Mage.Sets/src/mage/sets/revisededition/Fork.java @@ -0,0 +1,52 @@ +/* + * 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.sets.revisededition; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class Fork extends mage.sets.limitedbeta.Fork { + + public Fork(UUID ownerId) { + super(ownerId); + this.cardNumber = 152; + this.expansionSetCode = "3ED"; + } + + public Fork(final Fork card) { + super(card); + } + + @Override + public Fork copy() { + return new Fork(this); + } +} diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/CastThroughTime.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/CastThroughTime.java index 872e73aac48..6de6995cc95 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/CastThroughTime.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/CastThroughTime.java @@ -68,8 +68,7 @@ public class CastThroughTime extends CardImpl { // Instant and sorcery spells you control have rebound. // (Exile the spell as it resolves if you cast it from your hand. At the beginning of your next upkeep, you may cast that card from exile without paying its mana cost.) - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainReboundEffect())); - this.addWatcher(new LeavesBattlefieldWatcher()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainReboundEffect()), new LeavesBattlefieldWatcher()); } public CastThroughTime(final CastThroughTime card) { diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/TrainingGrounds.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/TrainingGrounds.java index 2e27dfd92cf..0022b2defbc 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/TrainingGrounds.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/TrainingGrounds.java @@ -35,9 +35,12 @@ import mage.Mana; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; import mage.abilities.effects.common.cost.CostModificationEffectImpl; import mage.cards.CardImpl; import mage.choices.ChoiceImpl; +import mage.constants.AbilityType; import mage.constants.CardType; import mage.constants.CostModificationType; import mage.constants.Duration; @@ -48,6 +51,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.util.CardUtil; /** * @@ -76,10 +80,7 @@ public class TrainingGrounds extends CardImpl { class TrainingGroundsEffect extends CostModificationEffectImpl { private static final String effectText = "Activated abilities of creatures you control cost up to {2} less to activate. This effect can't reduce the amount of mana an ability costs to activate to less than one mana"; - private static final FilterControlledCreaturePermanent filter; - static { - filter = new FilterControlledCreaturePermanent(); - } + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(); public TrainingGroundsEffect() { super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST); @@ -113,8 +114,7 @@ class TrainingGroundsEffect extends CostModificationEffectImpl { choice.setMessage("Reduce ability cost"); if(controller.choose(Outcome.Benefit, choice, game)){ int reduce = Integer.parseInt(choice.getChoice()); - mana.setColorless(mana.getColorless() - reduce); - abilityToModify.getManaCostsToPay().load(mana.toString()); + CardUtil.reduceCost(abilityToModify, reduce); } } return true; @@ -124,7 +124,7 @@ class TrainingGroundsEffect extends CostModificationEffectImpl { @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof ActivatedAbility) { + if (abilityToModify.getAbilityType().equals(AbilityType.ACTIVATED)) { //Activated abilities of creatures you control Permanent permanent = game.getPermanent(abilityToModify.getSourceId()); if (permanent != null && filter.match(permanent, source.getSourceId(), source.getControllerId(), game)) { diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/Vengevine.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/Vengevine.java index 864aeeb3dab..9271f85b1e9 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/Vengevine.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/Vengevine.java @@ -59,8 +59,7 @@ public class Vengevine extends CardImpl { this.toughness = new MageInt(3); this.addAbility(HasteAbility.getInstance()); - this.addAbility(new VengevineAbility()); - this.addWatcher(new VengevineWatcher()); + this.addAbility(new VengevineAbility(), new VengevineWatcher()); } public Vengevine(final Vengevine card) { diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/WorldAtWar.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/WorldAtWar.java index 4cd12718831..97cf8ffc2de 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/WorldAtWar.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/WorldAtWar.java @@ -61,9 +61,7 @@ public class WorldAtWar extends CardImpl { this.getSpellAbility().addEffect(new WorldAtWarEffect()); // Rebound - this.addAbility(new ReboundAbility()); - - this.addWatcher(new AttackedThisTurnWatcher()); + this.addAbility(new ReboundAbility(), new AttackedThisTurnWatcher()); } public WorldAtWar(final WorldAtWar card) { diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/ChoiceOfDamnations.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/ChoiceOfDamnations.java index 2d784a574d5..b28bd2b7923 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/ChoiceOfDamnations.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/ChoiceOfDamnations.java @@ -36,6 +36,7 @@ import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -54,8 +55,6 @@ public class ChoiceOfDamnations extends CardImpl { this.expansionSetCode = "SOK"; this.subtype.add("Arcane"); - this.color.setBlack(true); - // Target opponent chooses a number. You may have that player lose that much life. If you don't, that player sacrifices all but that many permanents. this.getSpellAbility().addEffect(new ChoiceOfDamnationsEffect()); this.getSpellAbility().addTarget(new TargetOpponent()); @@ -94,14 +93,14 @@ class ChoiceOfDamnationsEffect extends OneShotEffect { int amount = targetPlayer.getAmount(0, Integer.MAX_VALUE, "Chooses a number", game); Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - StringBuilder sb = new StringBuilder("Shall ").append(targetPlayer.getName()).append(" lose ").append(amount).append(" life?"); - if (controller.chooseUse(outcome, sb.toString(), game)) { + String sb = String.valueOf("Shall " + targetPlayer.getName() + " lose ") + Integer.toString(amount) + " life?"; + if (controller.chooseUse(outcome, sb, game)) { targetPlayer.loseLife(amount, game); } else { int numberPermanents = game.getState().getBattlefield().countAll(new FilterPermanent(), targetPlayer.getId(), game); if (numberPermanents > amount) { int numberToSacrifice = numberPermanents - amount; - Target target = new TargetControlledPermanent(numberToSacrifice, numberToSacrifice, new FilterPermanent(), false); + Target target = new TargetControlledPermanent(numberToSacrifice, numberToSacrifice, new FilterControlledPermanent(), false); targetPlayer.chooseTarget(Outcome.Sacrifice, target, source, game); for (UUID uuid : target.getTargets()) { Permanent permanent = game.getPermanent(uuid); diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/InameAsOne.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/InameAsOne.java index c48a4509917..79a257de5d2 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/InameAsOne.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/InameAsOne.java @@ -76,8 +76,7 @@ public class InameAsOne extends CardImpl { Ability ability = new EntersBattlefieldTriggeredAbility( new ConditionalOneShotEffect(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 1, filter), false), new CastFromHandCondition())); - this.addAbility(ability); - this.addWatcher(new CastFromHandWatcher()); + this.addAbility(ability, new CastFromHandWatcher()); // When Iname as One dies, you may exile it. If you do, return target Spirit permanent card from your graveyard to the battlefield. ability = new DiesTriggeredAbility(new InameAsOneEffect(), false); diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/KuonOgreAscendant.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/KuonOgreAscendant.java index eeb8bc91b2c..37b87fb708d 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/KuonOgreAscendant.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/KuonOgreAscendant.java @@ -70,8 +70,8 @@ public class KuonOgreAscendant extends CardImpl { Zone.BATTLEFIELD, new FlipSourceEffect(new KuonsEssenceToken()), TargetController.ANY, - new KuonOgreAscendantCondition(), false)); - this.addWatcher(new CreaturesDiedWatcher()); + new KuonOgreAscendantCondition(), false), + new CreaturesDiedWatcher()); } public KuonOgreAscendant(final KuonOgreAscendant card) { diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/FleshAllergy.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/FleshAllergy.java index 17337cb995f..d62b747d66b 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/FleshAllergy.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/FleshAllergy.java @@ -65,7 +65,7 @@ public class FleshAllergy extends CardImpl { this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new FleshAllergyEffect()); - this.addWatcher(new FleshAllergyWatcher()); + this.getSpellAbility().addWatcher(new FleshAllergyWatcher()); } public FleshAllergy (final FleshAllergy card) { diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/KuldothaForgemaster.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/KuldothaForgemaster.java index 9e8149a88c9..052bc47d1aa 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/KuldothaForgemaster.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/KuldothaForgemaster.java @@ -38,7 +38,7 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; import mage.cards.CardImpl; import mage.filter.common.FilterArtifactCard; -import mage.filter.common.FilterArtifactPermanent; +import mage.filter.common.FilterControlledArtifactPermanent; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetControlledPermanent; @@ -49,7 +49,7 @@ import mage.target.common.TargetControlledPermanent; public class KuldothaForgemaster extends CardImpl { private static final FilterArtifactCard filterArtifactCard = new FilterArtifactCard(); - private static final FilterArtifactPermanent filterArtifactPermanent = new FilterArtifactPermanent(); + private static final FilterControlledArtifactPermanent filterArtifactPermanent = new FilterControlledArtifactPermanent(); public KuldothaForgemaster(UUID ownerId) { super(ownerId, 169, "Kuldotha Forgemaster", Rarity.RARE, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}"); diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/MoltenPsyche.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/MoltenPsyche.java index f3a7c906f55..244cf5be5a0 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/MoltenPsyche.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/MoltenPsyche.java @@ -60,7 +60,7 @@ public class MoltenPsyche extends CardImpl { // Each player shuffles the cards from his or her hand into his or her library, then draws that many cards. // Metalcraft - If you control three or more artifacts, Molten Psyche deals damage to each opponent equal to the number of cards that player has drawn this turn. this.getSpellAbility().addEffect(new MoltenPsycheEffect()); - this.addWatcher(new MoltenPsycheWatcher()); + this.getSpellAbility().addWatcher(new MoltenPsycheWatcher()); } public MoltenPsyche(final MoltenPsyche card) { diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/TunnelIgnus.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/TunnelIgnus.java index dbe5bd751f1..56265845a21 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/TunnelIgnus.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/TunnelIgnus.java @@ -61,8 +61,8 @@ public class TunnelIgnus extends CardImpl { this.color.setRed(true); this.power = new MageInt(2); this.toughness = new MageInt(1); - this.addWatcher(new TunnelIgnusWatcher()); - this.addAbility(new TunnelIgnusTriggeredAbility()); + + this.addAbility(new TunnelIgnusTriggeredAbility(), new TunnelIgnusWatcher()); } public TunnelIgnus(final TunnelIgnus card) { diff --git a/Mage.Sets/src/mage/sets/scourge/DecreeOfAnnihilation.java b/Mage.Sets/src/mage/sets/scourge/DecreeOfAnnihilation.java new file mode 100644 index 00000000000..8f1efa909a3 --- /dev/null +++ b/Mage.Sets/src/mage/sets/scourge/DecreeOfAnnihilation.java @@ -0,0 +1,130 @@ + /* + * 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.sets.scourge; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.CycleTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyAllEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author fireshoes + */ +public class DecreeOfAnnihilation extends CardImpl { + + public DecreeOfAnnihilation(UUID ownerId) { + super(ownerId, 85, "Decree of Annihilation", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{8}{R}{R}"); + this.expansionSetCode = "SCG"; + + // Exile all artifacts, creatures, and lands from the battlefield, all cards from all graveyards, and all cards from all hands. + this.getSpellAbility().addEffect(new DecreeOfAnnihilationEffect()); + + // Cycling {5}{R}{R} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{5}{R}{R}"))); + + // When you cycle Decree of Annihilation, destroy all lands. + Ability ability = new CycleTriggeredAbility(new DestroyAllEffect(new FilterLandPermanent("lands")), false); + this.addAbility(ability); + } + + public DecreeOfAnnihilation(final DecreeOfAnnihilation card) { + super(card); + } + + @Override + public DecreeOfAnnihilation copy() { + return new DecreeOfAnnihilation(this); + } +} + +class DecreeOfAnnihilationEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterPermanent(""); + + static { + filter.add(Predicates.or( + new CardTypePredicate(CardType.ARTIFACT), + new CardTypePredicate(CardType.CREATURE), + new CardTypePredicate(CardType.LAND))); + } + + public DecreeOfAnnihilationEffect() { + super(Outcome.Detriment); + staticText = "Exile all artifacts, creatures, and lands from the battlefield, all cards from all graveyards, and all cards from all hands"; + } + + public DecreeOfAnnihilationEffect(final DecreeOfAnnihilationEffect effect) { + super(effect); + } + + @Override + public DecreeOfAnnihilationEffect copy() { + return new DecreeOfAnnihilationEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + permanent.moveToExile(id, "all artifacts, creatures, and land", id, game); + } + for (UUID playerId : game.getPlayerList()) { + Player player = game.getPlayer(playerId); + if (player != null) { + for (UUID cid : player.getHand().copy()) { + Card c = game.getCard(cid); + if (c != null) { + c.moveToExile(null, null, source.getSourceId(), game); + } + } + for (UUID cid : player.getGraveyard().copy()) { + Card c = game.getCard(cid); + if (c != null) { + c.moveToExile(null, null, source.getSourceId(), game); + } + } + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/seventhedition/RelentlessAssault.java b/Mage.Sets/src/mage/sets/seventhedition/RelentlessAssault.java index 034f48f18be..96516b21b99 100644 --- a/Mage.Sets/src/mage/sets/seventhedition/RelentlessAssault.java +++ b/Mage.Sets/src/mage/sets/seventhedition/RelentlessAssault.java @@ -58,7 +58,7 @@ public class RelentlessAssault extends CardImpl { this.color.setRed(true); // Untap all creatures that attacked this turn. After this main phase, there is an additional combat phase followed by an additional main phase. - this.addWatcher(new AttackedThisTurnWatcher()); + this.getSpellAbility().addWatcher(new AttackedThisTurnWatcher()); this.getSpellAbility().addEffect(new RelentlessAssaultUntapEffect()); this.getSpellAbility().addEffect(new RelentlessAssaultAddPhasesEffect()); } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/AdviceFromTheFae.java b/Mage.Sets/src/mage/sets/shadowmoor/AdviceFromTheFae.java new file mode 100644 index 00000000000..ae8ad26b0b3 --- /dev/null +++ b/Mage.Sets/src/mage/sets/shadowmoor/AdviceFromTheFae.java @@ -0,0 +1,134 @@ +/* + * 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.sets.shadowmoor; + +import java.util.List; +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; + +/** + * + * @author jeffwadsworth + */ +public class AdviceFromTheFae extends CardImpl { + + public AdviceFromTheFae(UUID ownerId) { + super(ownerId, 28, "Advice from the Fae", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{2/U}{2/U}{2/U}"); + this.expansionSetCode = "SHM"; + + // ({2U} can be paid with any two mana or with {U}. This card's converted mana cost is 6.) + // Look at the top five cards of your library. If you control more creatures than each other player, put two of those cards into your hand. Otherwise, put one of them into your hand. Then put the rest on the bottom of your library in any order. + this.getSpellAbility().addEffect(new AdviceFromTheFaeEffect()); + + } + + public AdviceFromTheFae(final AdviceFromTheFae card) { + super(card); + } + + @Override + public AdviceFromTheFae copy() { + return new AdviceFromTheFae(this); + } +} + +class AdviceFromTheFaeEffect extends OneShotEffect { + + public AdviceFromTheFaeEffect() { + super(Outcome.DrawCard); + this.staticText = "Look at the top five cards of your library. If you control more creatures than each other player, put two of those cards into your hand. Otherwise, put one of them into your hand. Then put the rest on the bottom of your library in any order"; + } + + public AdviceFromTheFaeEffect(final AdviceFromTheFaeEffect effect) { + super(effect); + } + + @Override + public AdviceFromTheFaeEffect copy() { + return new AdviceFromTheFaeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject mageObject = game.getObject(source.getSourceId()); + if (controller != null) { + List cardsFromTopLibrary = controller.getLibrary().getTopCards(game, 5); + Cards cards = new CardsImpl(Zone.LIBRARY); + for (Card card : cardsFromTopLibrary) { + cards.add(card); + } + controller.lookAtCards(mageObject.getLogName(), cards, game); + int max = 0; + for (UUID playerId : controller.getInRange()) { + FilterCreaturePermanent filter = new FilterCreaturePermanent(); + filter.add(new ControllerIdPredicate(playerId)); + if (playerId != controller.getId()) { + if (max < game.getBattlefield().countAll(filter, playerId, game)) { + max = game.getBattlefield().countAll(filter, playerId, game); + } + } + } + if (game.getBattlefield().countAll(new FilterControlledCreaturePermanent(), controller.getId(), game) > max) { + TargetCard target = new TargetCard(2, Zone.LIBRARY, new FilterCard()); + if (controller.choose(Outcome.DrawCard, cards, target, game)) { + controller.moveCardToHandWithInfo(game.getCard(target.getFirstTarget()), source.getSourceId(), game, Zone.LIBRARY); + cards.remove(game.getCard(target.getFirstTarget())); + controller.moveCardToHandWithInfo(game.getCard(target.getTargets().get(1)), source.getSourceId(), game, Zone.LIBRARY); + cards.remove(game.getCard(target.getTargets().get(1))); + } + } else { + TargetCard target = new TargetCard(1, Zone.LIBRARY, new FilterCard()); + if (controller.choose(Outcome.DrawCard, cards, target, game)) { + controller.moveCardToHandWithInfo(game.getCard(target.getFirstTarget()), source.getSourceId(), game, Zone.LIBRARY); + cards.remove(game.getCard(target.getFirstTarget())); + } + } + controller.putCardsOnBottomOfLibrary(cards, game, source, true); + return true; + } + return false; + } +} diff --git a/Mage/src/mage/watchers/common/PermanentTappedForManaWatcher.java b/Mage.Sets/src/mage/sets/shadowmoor/BurnTrail.java similarity index 59% rename from Mage/src/mage/watchers/common/PermanentTappedForManaWatcher.java rename to Mage.Sets/src/mage/sets/shadowmoor/BurnTrail.java index 24065772ada..0e4a193204d 100644 --- a/Mage/src/mage/watchers/common/PermanentTappedForManaWatcher.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/BurnTrail.java @@ -25,52 +25,40 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ +package mage.sets.shadowmoor; -package mage.watchers.common; - -import java.util.ArrayList; -import java.util.List; import java.util.UUID; -import mage.constants.WatcherScope; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.watchers.Watcher; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.ConspireAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.target.common.TargetCreatureOrPlayer; /** + * * @author jeffwadsworth */ +public class BurnTrail extends CardImpl { -public class PermanentTappedForManaWatcher extends Watcher { + public BurnTrail(UUID ownerId) { + super(ownerId, 86, "Burn Trail", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{3}{R}"); + this.expansionSetCode = "SHM"; - public List permanentId = new ArrayList<>(); - - public PermanentTappedForManaWatcher() { - super("PermanentTappedForMana", WatcherScope.GAME); + // Burn Trail deals 3 damage to target creature or player. + this.getSpellAbility().addEffect(new DamageTargetEffect(3)); + this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); + + // Conspire + this.addAbility(new ConspireAbility(this)); } - public PermanentTappedForManaWatcher(final PermanentTappedForManaWatcher watcher) { - super(watcher); + public BurnTrail(final BurnTrail card) { + super(card); } @Override - public PermanentTappedForManaWatcher copy() { - return new PermanentTappedForManaWatcher(this); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.TAPPED_FOR_MANA) { - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); - if (permanent != null) { - permanentId.add(permanent.getId()); - } - } - } - - @Override - public void reset() { - super.reset(); - permanentId.clear(); + public BurnTrail copy() { + return new BurnTrail(this); } } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/DawnglowInfusion.java b/Mage.Sets/src/mage/sets/shadowmoor/DawnglowInfusion.java index eab04ec0ffa..27723c73509 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/DawnglowInfusion.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/DawnglowInfusion.java @@ -63,7 +63,7 @@ public class DawnglowInfusion extends CardImpl { new GainLifeEffect(xValue), new ManaWasSpentCondition(ColoredManaSymbol.W), " And X life if {W} was spent to cast it")); this.addInfo("Info1", "(Do both if {G}{W} was spent.)"); - this.addWatcher(new ManaSpentToCastWatcher()); + this.getSpellAbility().addWatcher(new ManaSpentToCastWatcher()); } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/DemigodOfRevenge.java b/Mage.Sets/src/mage/sets/shadowmoor/DemigodOfRevenge.java index 64790ffdd93..6a6e4977938 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/DemigodOfRevenge.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/DemigodOfRevenge.java @@ -106,7 +106,7 @@ class DemigodOfRevengeTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "When you cast Demigod of Revenge, " + super.getRule(); + return "When you cast {this}, " + super.getRule(); } } @@ -133,9 +133,9 @@ class DemigodOfRevengeReturnEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - for (Card creature : player.getGraveyard().getCards(filter, game)) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + for (Card creature : controller.getGraveyard().getCards(filter, game)) { creature.putOntoBattlefield(game, Zone.GRAVEYARD, source.getSourceId(), source.getControllerId()); } return true; diff --git a/Mage.Sets/src/mage/sets/shadowmoor/DreamSalvage.java b/Mage.Sets/src/mage/sets/shadowmoor/DreamSalvage.java index 89efee118f2..0b1d64a61e8 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/DreamSalvage.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/DreamSalvage.java @@ -60,7 +60,7 @@ public class DreamSalvage extends CardImpl { // Draw cards equal to the number of cards target opponent discarded this turn. this.getSpellAbility().addEffect(new DreamSalvageEffect()); this.getSpellAbility().addTarget(new TargetOpponent()); - this.addWatcher(new CardsDiscardedThisTurnWatcher()); + this.getSpellAbility().addWatcher(new CardsDiscardedThisTurnWatcher()); } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/Firespout.java b/Mage.Sets/src/mage/sets/shadowmoor/Firespout.java index 2fca287e7dd..61d128aeb59 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/Firespout.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/Firespout.java @@ -69,7 +69,7 @@ public class Firespout extends CardImpl { new DamageAllEffect(3, filter2), new ManaWasSpentCondition(ColoredManaSymbol.G), " And 3 damage to each creature with flying if {G} was spent to cast it")); this.addInfo("Info1", "(Do both if {R}{G} was spent.)"); - this.addWatcher(new ManaSpentToCastWatcher()); + this.getSpellAbility().addWatcher(new ManaSpentToCastWatcher()); diff --git a/Mage.Sets/src/mage/sets/shadowmoor/FurystokeGiant.java b/Mage.Sets/src/mage/sets/shadowmoor/FurystokeGiant.java new file mode 100644 index 00000000000..9bdee8e1753 --- /dev/null +++ b/Mage.Sets/src/mage/sets/shadowmoor/FurystokeGiant.java @@ -0,0 +1,81 @@ +/* + * 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.sets.shadowmoor; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.continious.GainAbilityAllEffect; +import mage.abilities.keyword.PersistAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.target.common.TargetCreatureOrPlayer; + +/** + * + * @author jeffwadsworth + */ +public class FurystokeGiant extends CardImpl { + + public FurystokeGiant(UUID ownerId) { + super(ownerId, 93, "Furystoke Giant", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); + this.expansionSetCode = "SHM"; + this.subtype.add("Giant"); + this.subtype.add("Warrior"); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Furystoke Giant enters the battlefield, other creatures you control gain "{tap}: This creature deals 2 damage to target creature or player" until end of turn. + SimpleActivatedAbility FurystokeGiantAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(2), new TapSourceCost()); + FurystokeGiantAbility.addTarget(new TargetCreatureOrPlayer()); + Effect effect = new GainAbilityAllEffect(FurystokeGiantAbility, Duration.EndOfTurn, new FilterControlledCreaturePermanent("other creatures"), true); + effect.setText("other creatures you control gain \"{T}: This creature deals 2 damage to target creature or player.\" until end of turn."); + this.addAbility(new EntersBattlefieldTriggeredAbility(effect)); + + // Persist + this.addAbility(new PersistAbility()); + + } + + public FurystokeGiant(final FurystokeGiant card) { + super(card); + } + + @Override + public FurystokeGiant copy() { + return new FurystokeGiant(this); + } +} diff --git a/Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java b/Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java new file mode 100644 index 00000000000..82b79bb4866 --- /dev/null +++ b/Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java @@ -0,0 +1,188 @@ +/* + * 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.sets.shadowmoor; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.RevealTargetFromHandCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInHand; + +/** + * + * @author jeffwadsworth + */ +public class IlluminatedFolio extends CardImpl { + + public IlluminatedFolio(UUID ownerId) { + super(ownerId, 254, "Illuminated Folio", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT}, "{5}"); + this.expansionSetCode = "SHM"; + + // {1}, {tap}, Reveal two cards from your hand that share a color: Draw a card. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{1}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new RevealTwoCardsSharedColorFromHandCost()); + this.addAbility(ability); + + } + + public IlluminatedFolio(final IlluminatedFolio card) { + super(card); + } + + @Override + public IlluminatedFolio copy() { + return new IlluminatedFolio(this); + } +} + +class RevealTwoCardsSharedColorFromHandCost extends RevealTargetFromHandCost { + + public RevealTwoCardsSharedColorFromHandCost() { + super(new TargetTwoCardsWithTheSameColorInHand()); + } + + public RevealTwoCardsSharedColorFromHandCost(RevealTwoCardsSharedColorFromHandCost cost) { + super(cost); + } + + @Override + public RevealTwoCardsSharedColorFromHandCost copy() { + return new RevealTwoCardsSharedColorFromHandCost(this); + } + +} + +class TargetTwoCardsWithTheSameColorInHand extends TargetCardInHand { + + public TargetTwoCardsWithTheSameColorInHand() { + super(2, 2, new FilterCard("two cards from your hand that share a color")); + } + + public TargetTwoCardsWithTheSameColorInHand(final TargetTwoCardsWithTheSameColorInHand target) { + super(target); + } + + @Override + public Set possibleTargets(UUID sourceControllerId, Game game) { + Set newPossibleTargets = new HashSet<>(); + Set possibleTargets = new HashSet<>(); + Player player = game.getPlayer(sourceControllerId); + for (Card card : player.getHand().getCards(filter, game)) { + possibleTargets.add(card.getId()); + } + + Cards cardsToCheck = new CardsImpl(); + cardsToCheck.addAll(possibleTargets); + if (targets.size() == 1) { + // first target is already choosen, now only targets with the shared color are selectable + for (Map.Entry entry : targets.entrySet()) { + Card chosenCard = cardsToCheck.get(entry.getKey(), game); + if (chosenCard != null) { + for (UUID cardToCheck : cardsToCheck) { + if (!cardToCheck.equals(chosenCard.getId()) && chosenCard.getColor().equals(game.getCard(cardToCheck).getColor())) { + newPossibleTargets.add(cardToCheck); + } + } + } + } + } else { + for (UUID cardToCheck : cardsToCheck) { + FilterCard colorFilter = new FilterCard(); + colorFilter.add(new ColorPredicate(game.getCard(cardToCheck).getColor())); + if (cardsToCheck.count(colorFilter, game) > 1) { + newPossibleTargets.add(cardToCheck); + } + } + } + return newPossibleTargets; + } + + @Override + public boolean canChoose(UUID sourceControllerId, Game game) { + Cards cardsToCheck = new CardsImpl(); + Player player = game.getPlayer(sourceControllerId); + for (Card card : player.getHand().getCards(filter, game)) { + cardsToCheck.add(card.getId()); + } + int possibleCards = 0; + for (UUID cardToCheck : cardsToCheck) { + FilterCard colorFilter = new FilterCard(); + colorFilter.add(new ColorPredicate(game.getCard(cardToCheck).getColor())); + if (cardsToCheck.count(colorFilter, game) > 1) { + ++possibleCards; + } + } + return possibleCards > 0; + } + + @Override + public boolean canTarget(UUID id, Game game) { + if (super.canTarget(id, game)) { + Card card = game.getCard(id); + if (card != null) { + if (targets.size() == 1) { + Card card2 = game.getCard(targets.entrySet().iterator().next().getKey()); + if (card2 != null && card2.getColor().equals(card.getColor())) { + return true; + } + } else { + FilterCard colorFilter = new FilterCard(); + colorFilter.add(new ColorPredicate(card.getColor())); + Player player = game.getPlayer(card.getOwnerId()); + if (player.getHand().getCards(colorFilter, game).size() > 1) { + return true; + } + } + } + } + return false; + } + + @Override + public TargetTwoCardsWithTheSameColorInHand copy() { + return new TargetTwoCardsWithTheSameColorInHand(this); + } +} diff --git a/Mage.Sets/src/mage/sets/shadowmoor/InescapableBrute.java b/Mage.Sets/src/mage/sets/shadowmoor/InescapableBrute.java new file mode 100644 index 00000000000..b379a9fd112 --- /dev/null +++ b/Mage.Sets/src/mage/sets/shadowmoor/InescapableBrute.java @@ -0,0 +1,70 @@ +/* + * 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.sets.shadowmoor; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.combat.MustBeBlockedByAtLeastOneSourceEffect; +import mage.abilities.keyword.WitherAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author jeffwadsworth + */ +public class InescapableBrute extends CardImpl { + + public InescapableBrute(UUID ownerId) { + super(ownerId, 95, "Inescapable Brute", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{5}{R}"); + this.expansionSetCode = "SHM"; + this.subtype.add("Giant"); + this.subtype.add("Warrior"); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Wither + this.addAbility(WitherAbility.getInstance()); + + // Inescapable Brute must be blocked if able. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MustBeBlockedByAtLeastOneSourceEffect())); + + } + + public InescapableBrute(final InescapableBrute card) { + super(card); + } + + @Override + public InescapableBrute copy() { + return new InescapableBrute(this); + } +} diff --git a/Mage.Sets/src/mage/sets/shadowmoor/ManaReflection.java b/Mage.Sets/src/mage/sets/shadowmoor/ManaReflection.java index 52a3efc1f76..ba1e87ed85f 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/ManaReflection.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/ManaReflection.java @@ -42,7 +42,6 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ManaEvent; -import mage.watchers.common.PermanentTappedForManaWatcher; /** * @@ -58,7 +57,6 @@ public class ManaReflection extends CardImpl { // If you tap a permanent for mana, it produces twice as much of that mana instead. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ManaReflectionReplacementEffect())); - this.addWatcher(new PermanentTappedForManaWatcher()); } @@ -92,41 +90,35 @@ class ManaReflectionReplacementEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Mana mana = ((ManaEvent) event).getMana(); if (mana.getBlack() > 0) { - ((ManaEvent) event).getMana().set(ManaType.BLACK, mana.count() * 2); + ((ManaEvent) event).getMana().set(ManaType.BLACK, mana.getBlack()* 2); } if (mana.getBlue() > 0) { - ((ManaEvent) event).getMana().set(ManaType.BLUE, mana.count() * 2); + ((ManaEvent) event).getMana().set(ManaType.BLUE, mana.getBlue() * 2); } if (mana.getWhite() > 0) { - ((ManaEvent) event).getMana().set(ManaType.WHITE, mana.count() * 2); + ((ManaEvent) event).getMana().set(ManaType.WHITE, mana.getWhite() * 2); } if (mana.getGreen() > 0) { - ((ManaEvent) event).getMana().set(ManaType.GREEN, mana.count() * 2); + ((ManaEvent) event).getMana().set(ManaType.GREEN, mana.getGreen() * 2); } if (mana.getRed() > 0) { - ((ManaEvent) event).getMana().set(ManaType.RED, mana.count() * 2); + ((ManaEvent) event).getMana().set(ManaType.RED, mana.getRed() * 2); } if (mana.getColorless() > 0) { - ((ManaEvent) event).getMana().set(ManaType.COLORLESS, mana.count() * 2); + ((ManaEvent) event).getMana().set(ManaType.COLORLESS, mana.getColorless() * 2); } return false; } @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.ADD_MANA - && event.getPlayerId().equals(source.getControllerId()) - && game.getPermanentOrLKIBattlefield(event.getSourceId()) != null) { - UUID permanentId = game.getPermanentOrLKIBattlefield(event.getSourceId()).getId(); - PermanentTappedForManaWatcher watcher = (PermanentTappedForManaWatcher) game.getState().getWatchers().get("PermanentTappedForMana"); - if (watcher != null) { - if (watcher.permanentId.contains(permanentId)) { - watcher.permanentId.remove(permanentId); - return true; - } - } - } - return false; + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TAPPED_FOR_MANA; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getPlayerId().equals(source.getControllerId()) + && game.getPermanentOrLKIBattlefield(event.getSourceId()) != null; } @Override diff --git a/Mage.Sets/src/mage/sets/shadowmoor/PucasMischief.java b/Mage.Sets/src/mage/sets/shadowmoor/PucasMischief.java new file mode 100644 index 00000000000..a7901d94bbc --- /dev/null +++ b/Mage.Sets/src/mage/sets/shadowmoor/PucasMischief.java @@ -0,0 +1,170 @@ +/* + * 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.sets.shadowmoor; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.common.continious.ExchangeControlTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author jeffwadsworth + */ +public class PucasMischief extends CardImpl { + + private static final String rule = "you may exchange control of target nonland permanent you control and target nonland permanent an opponent controls with an equal or lesser converted mana cost"; + + public PucasMischief(UUID ownerId) { + super(ownerId, 47, "Puca's Mischief", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); + this.expansionSetCode = "SHM"; + + // At the beginning of your upkeep, you may exchange control of target nonland permanent you control and target nonland permanent an opponent controls with an equal or lesser converted mana cost. + Ability ability = new BeginningOfUpkeepTriggeredAbility(new ExchangeControlTargetEffect(Duration.EndOfGame, rule, false, true), TargetController.YOU, true); + ability.addTarget(new TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent()); + ability.addTarget(new PucasMischiefSecondTarget()); + this.addAbility(ability); + + } + + public PucasMischief(final PucasMischief card) { + super(card); + } + + @Override + public PucasMischief copy() { + return new PucasMischief(this); + } +} + +class TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent extends TargetControlledPermanent { + + public TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent() { + super(); + filter.add(Predicates.not(new CardTypePredicate(CardType.LAND))); + setTargetName("nonland permanent you control"); + } + + public TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent(final TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent target) { + super(target); + } + + @Override + public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { + return super.canTarget(controllerId, id, source, game); + } + + @Override + public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { + Set possibleTargets = new HashSet<>(); + MageObject targetSource = game.getObject(sourceId); + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, sourceId, game)) { + if (!targets.containsKey(permanent.getId()) && permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) { + possibleTargets.add(permanent.getId()); + } + } + return possibleTargets; + } + + @Override + public TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent copy() { + return new TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent(this); + } +} + +class PucasMischiefSecondTarget extends TargetPermanent { + + private Permanent firstTarget = null; + + public PucasMischiefSecondTarget() { + super(); + filter.add(new ControllerPredicate(TargetController.OPPONENT)); + filter.add(Predicates.not(new CardTypePredicate(CardType.LAND))); + setTargetName("permanent an opponent controls with an equal or lesser converted mana cost"); + } + + public PucasMischiefSecondTarget(final PucasMischiefSecondTarget target) { + super(target); + this.firstTarget = target.firstTarget; + } + + @Override + public boolean canTarget(UUID id, Ability source, Game game) { + if (super.canTarget(id, source, game)) { + Permanent target1 = game.getPermanent(source.getFirstTarget()); + Permanent opponentPermanent = game.getPermanent(id); + if (target1 != null && opponentPermanent != null) { + return target1.getManaCost().convertedManaCost() >= opponentPermanent.getManaCost().convertedManaCost(); + } + } + return false; + } + + @Override + public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { + Set possibleTargets = new HashSet<>(); + if (firstTarget != null) { + MageObject targetSource = game.getObject(sourceId); + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, sourceId, game)) { + if (!targets.containsKey(permanent.getId()) && permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) { + if (firstTarget.getManaCost().convertedManaCost() >= permanent.getManaCost().convertedManaCost()) { + possibleTargets.add(permanent.getId()); + } + } + } + } + return possibleTargets; + } + + @Override + public boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game) { + firstTarget = game.getPermanent(source.getFirstTarget()); + return super.chooseTarget(Outcome.GainControl, playerId, source, game); + } + + @Override + public PucasMischiefSecondTarget copy() { + return new PucasMischiefSecondTarget(this); + } +} diff --git a/Mage.Sets/src/mage/sets/shadowmoor/RepelIntruders.java b/Mage.Sets/src/mage/sets/shadowmoor/RepelIntruders.java index a3d86d4bcd3..3eaec05a997 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/RepelIntruders.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/RepelIntruders.java @@ -65,7 +65,7 @@ public class RepelIntruders extends CardImpl { new ManaWasSpentCondition(ColoredManaSymbol.U), " Counter up to one target creature spell if {U} was spent to cast {this}")); this.getSpellAbility().addTarget(target); this.addInfo("Info1", "(Do both if {W}{U} was spent.)"); - this.addWatcher(new ManaSpentToCastWatcher()); + this.getSpellAbility().addWatcher(new ManaSpentToCastWatcher()); } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/RiversGrasp.java b/Mage.Sets/src/mage/sets/shadowmoor/RiversGrasp.java index 5fa05293835..408316bce89 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/RiversGrasp.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/RiversGrasp.java @@ -75,7 +75,7 @@ public class RiversGrasp extends CardImpl { this.getSpellAbility().addTarget(targetPlayer); this.addInfo("Info1", "(Do both if {U}{B} was spent.)"); - this.addWatcher(new ManaSpentToCastWatcher()); + this.getSpellAbility().addWatcher(new ManaSpentToCastWatcher()); } public RiversGrasp(final RiversGrasp card) { diff --git a/Mage.Sets/src/mage/sets/shadowmoor/TwilightShepherd.java b/Mage.Sets/src/mage/sets/shadowmoor/TwilightShepherd.java index e3a58b7a2ec..81b4ee53ed8 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/TwilightShepherd.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/TwilightShepherd.java @@ -68,8 +68,7 @@ public class TwilightShepherd extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // When Twilight Shepherd enters the battlefield, return to your hand all cards in your graveyard that were put there from the battlefield this turn. - this.addWatcher(new CardsPutIntoGraveyardWatcher()); - this.addAbility(new EntersBattlefieldTriggeredAbility(new TwilightShepherdEffect(), false)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new TwilightShepherdEffect(), false), new CardsPutIntoGraveyardWatcher()); // Persist this.addAbility(new PersistAbility()); diff --git a/Mage.Sets/src/mage/sets/shadowmoor/Valleymaker.java b/Mage.Sets/src/mage/sets/shadowmoor/Valleymaker.java index 235b5a204aa..3bf42bef6b4 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/Valleymaker.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/Valleymaker.java @@ -74,7 +74,7 @@ public class Valleymaker extends CardImpl { this.addAbility(ability); // {tap}, Sacrifice a Forest: Choose a player. That player adds {G}{G}{G} to his or her mana pool. - Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddManaToManaPoolTargetControllerEffect(new Mana(0,3,0,0,0,0,0), "chosen player"), new TapSourceCost()); + Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddManaToManaPoolTargetControllerEffect(new Mana(0,3,0,0,0,0,0), "chosen player"), new TapSourceCost(), false); ability2.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter2))); ability2.addTarget(new TargetPlayer(1, 1, true)); this.addAbility(ability2); diff --git a/Mage.Sets/src/mage/sets/shadowmoor/WingrattleScarecrow.java b/Mage.Sets/src/mage/sets/shadowmoor/WingrattleScarecrow.java new file mode 100644 index 00000000000..a3a57bbded3 --- /dev/null +++ b/Mage.Sets/src/mage/sets/shadowmoor/WingrattleScarecrow.java @@ -0,0 +1,89 @@ +/* + * 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.sets.shadowmoor; + +import java.util.UUID; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinousEffect; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.common.continious.GainAbilitySourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.PersistAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.ColorPredicate; + +/** + * + * @author jeffwadsworth + */ +public class WingrattleScarecrow extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a blue creature"); + private static final FilterControlledCreaturePermanent filter2 = new FilterControlledCreaturePermanent("a black creature"); + static { + filter.add(new ColorPredicate(ObjectColor.BLUE)); + filter2.add(new ColorPredicate(ObjectColor.BLACK)); + } + + private static final String rule = "{this} has flying as long as you control a blue creature"; + private static final String rule2 = "{this} has persist as long as you control a black creature"; + + public WingrattleScarecrow(UUID ownerId) { + super(ownerId, 270, "Wingrattle Scarecrow", Rarity.COMMON, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); + this.expansionSetCode = "SHM"; + this.subtype.add("Scarecrow"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Wingrattle Scarecrow has flying as long as you control a blue creature. + ContinuousEffect effect = new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinousEffect(effect, new PermanentsOnTheBattlefieldCondition(filter), rule))); + + // Wingrattle Scarecrow has persist as long as you control a black creature. + ContinuousEffect effect2 = new GainAbilitySourceEffect(new PersistAbility(), Duration.WhileOnBattlefield); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinousEffect(effect2, new PermanentsOnTheBattlefieldCondition(filter2), rule2))); + + } + + public WingrattleScarecrow(final WingrattleScarecrow card) { + super(card); + } + + @Override + public WingrattleScarecrow copy() { + return new WingrattleScarecrow(this); + } +} diff --git a/Mage.Sets/src/mage/sets/shardsofalara/EtherswornCanonist.java b/Mage.Sets/src/mage/sets/shardsofalara/EtherswornCanonist.java index e3e773bbacd..c2276900057 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/EtherswornCanonist.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/EtherswornCanonist.java @@ -63,8 +63,7 @@ public class EtherswornCanonist extends CardImpl { this.toughness = new MageInt(2); // Each player who has cast a nonartifact spell this turn can't cast additional nonartifact spells. - this.addWatcher(new EtherswornCanonistWatcher()); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new EtherswornCanonistReplacementEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new EtherswornCanonistReplacementEffect()), new EtherswornCanonistWatcher()); } public EtherswornCanonist(final EtherswornCanonist card) { diff --git a/Mage.Sets/src/mage/sets/shardsofalara/MagmaSpray.java b/Mage.Sets/src/mage/sets/shardsofalara/MagmaSpray.java index 7ebf97d7791..09ef47f242b 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/MagmaSpray.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/MagmaSpray.java @@ -36,6 +36,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.DamagedByWatcher; /** * @@ -56,6 +57,7 @@ public class MagmaSpray extends CardImpl { Effect effect = new DealtDamageToCreatureBySourceDies(this, Duration.EndOfTurn); effect.setText("If that creature would die this turn, exile it instead"); this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addWatcher(new DamagedByWatcher()); } public MagmaSpray(final MagmaSpray card) { diff --git a/Mage.Sets/src/mage/sets/shardsofalara/Skeletonize.java b/Mage.Sets/src/mage/sets/shardsofalara/Skeletonize.java index 792addde23f..f33a546bb80 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/Skeletonize.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/Skeletonize.java @@ -69,7 +69,7 @@ public class Skeletonize extends CardImpl { this.getSpellAbility().addTarget(new TargetCreaturePermanent()); // When a creature dealt damage this way dies this turn, put a 1/1 black Skeleton creature token onto the battlefield with "{B}: Regenerate this creature." this.getSpellAbility().addEffect(new SkeletonizeEffect()); - this.addWatcher(new DamagedByWatcher()); + this.getSpellAbility().addWatcher(new DamagedByWatcher()); } public Skeletonize(final Skeletonize card) { diff --git a/Mage.Sets/src/mage/sets/sorinvstibalt/TorrentOfSouls.java b/Mage.Sets/src/mage/sets/sorinvstibalt/TorrentOfSouls.java index a192ee01133..44d09194bfc 100644 --- a/Mage.Sets/src/mage/sets/sorinvstibalt/TorrentOfSouls.java +++ b/Mage.Sets/src/mage/sets/sorinvstibalt/TorrentOfSouls.java @@ -77,7 +77,7 @@ public class TorrentOfSouls extends CardImpl { this.getSpellAbility().addTarget(targetPlayer); this.addInfo("Info1", "(Do both if {B}{R} was spent.)"); - this.addWatcher(new ManaSpentToCastWatcher()); + this.getSpellAbility().addWatcher(new ManaSpentToCastWatcher()); } diff --git a/Mage.Sets/src/mage/sets/tempest/MoggConscripts.java b/Mage.Sets/src/mage/sets/tempest/MoggConscripts.java index f0a64dc8de4..41636036084 100644 --- a/Mage.Sets/src/mage/sets/tempest/MoggConscripts.java +++ b/Mage.Sets/src/mage/sets/tempest/MoggConscripts.java @@ -62,8 +62,7 @@ public class MoggConscripts extends CardImpl { this.toughness = new MageInt(2); // Mogg Conscripts can't attack unless you've cast a creature spell this turn. - this.addWatcher(new PlayerCastCreatureWatcher()); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MoggConscriptsEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MoggConscriptsEffect()), new PlayerCastCreatureWatcher()); } public MoggConscripts(final MoggConscripts card) { diff --git a/Mage.Sets/src/mage/sets/tenth/Incinerate.java b/Mage.Sets/src/mage/sets/tenth/Incinerate.java index ceef626402d..1ac18556be8 100644 --- a/Mage.Sets/src/mage/sets/tenth/Incinerate.java +++ b/Mage.Sets/src/mage/sets/tenth/Incinerate.java @@ -57,7 +57,7 @@ public class Incinerate extends CardImpl { this.getSpellAbility().addEffect(new DamageTargetEffect(3)); this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); this.getSpellAbility().addEffect(new IncinerateEffect()); - this.addWatcher(new DamagedByWatcher()); + this.getSpellAbility().addWatcher(new DamagedByWatcher()); } public Incinerate(final Incinerate card) { diff --git a/Mage.Sets/src/mage/sets/tenth/PhageTheUntouchable.java b/Mage.Sets/src/mage/sets/tenth/PhageTheUntouchable.java index ad41b8cc139..88e1629a13b 100644 --- a/Mage.Sets/src/mage/sets/tenth/PhageTheUntouchable.java +++ b/Mage.Sets/src/mage/sets/tenth/PhageTheUntouchable.java @@ -65,8 +65,7 @@ public class PhageTheUntouchable extends CardImpl { new EntersBattlefieldTriggeredAbility(new LoseGameSourceControllerEffect(), false), new InvertCondition(new CastFromHandCondition()), "When {this} enters the battlefield, if you didn't cast it from your hand, you lose the game" - )); - this.addWatcher(new CastFromHandWatcher()); + ), new CastFromHandWatcher()); // Whenever Phage deals combat damage to a creature, destroy that creature. It can't be regenerated. this.addAbility(new DealsCombatDamageToACreatureTriggeredAbility(new DestroyTargetEffect(true), false, true)); diff --git a/Mage.Sets/src/mage/sets/theros/AngerOfTheGods.java b/Mage.Sets/src/mage/sets/theros/AngerOfTheGods.java index b7704bcb6b5..5c2a208ea0a 100644 --- a/Mage.Sets/src/mage/sets/theros/AngerOfTheGods.java +++ b/Mage.Sets/src/mage/sets/theros/AngerOfTheGods.java @@ -35,6 +35,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; import mage.filter.common.FilterCreaturePermanent; +import mage.watchers.common.DamagedByWatcher; /** * @@ -53,6 +54,7 @@ public class AngerOfTheGods extends CardImpl { //If a creature dealt damage this way would die this turn, exile it instead. this.getSpellAbility().addEffect(new DealtDamageToCreatureBySourceDies(this, Duration.EndOfTurn)); + this.getSpellAbility().addWatcher(new DamagedByWatcher()); } public AngerOfTheGods(final AngerOfTheGods card) { diff --git a/Mage.Sets/src/mage/sets/theros/AnthousaSetessanHero.java b/Mage.Sets/src/mage/sets/theros/AnthousaSetessanHero.java index b00e0b4742b..2ae80e616c6 100644 --- a/Mage.Sets/src/mage/sets/theros/AnthousaSetessanHero.java +++ b/Mage.Sets/src/mage/sets/theros/AnthousaSetessanHero.java @@ -36,7 +36,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; -import mage.filter.common.FilterLandPermanent; +import mage.filter.common.FilterControlledLandPermanent; import mage.game.permanent.token.Token; import mage.target.common.TargetControlledPermanent; @@ -59,7 +59,7 @@ public class AnthousaSetessanHero extends CardImpl { // Heroic - Whenever you cast a spell that targets Anthousa, Setessan Hero, up to three target lands you control each become 2/2 Warrior creatures until end of turn. They're still lands. Ability ability = new HeroicAbility(new BecomesCreatureTargetEffect(new AnthousaWarriorToken(), false, true, Duration.EndOfTurn)); - ability.addTarget(new TargetControlledPermanent(0,3,new FilterLandPermanent("lands"), false)); + ability.addTarget(new TargetControlledPermanent(0,3,new FilterControlledLandPermanent("lands"), false)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/theros/RageOfPurphoros.java b/Mage.Sets/src/mage/sets/theros/RageOfPurphoros.java index 66afe5c6b4e..b328085bad0 100644 --- a/Mage.Sets/src/mage/sets/theros/RageOfPurphoros.java +++ b/Mage.Sets/src/mage/sets/theros/RageOfPurphoros.java @@ -56,7 +56,7 @@ public class RageOfPurphoros extends CardImpl { this.getSpellAbility().addEffect(new CantRegenerateTargetEffect(Duration.EndOfTurn, "It")); this.getSpellAbility().addEffect(new ScryEffect(1)); - this.addWatcher(new DamagedByWatcher()); + this.getSpellAbility().addWatcher(new DamagedByWatcher()); } public RageOfPurphoros(final RageOfPurphoros card) { diff --git a/Mage.Sets/src/mage/sets/theros/ShipbreakerKraken.java b/Mage.Sets/src/mage/sets/theros/ShipbreakerKraken.java index a62a31ad8b0..2d64b00c329 100644 --- a/Mage.Sets/src/mage/sets/theros/ShipbreakerKraken.java +++ b/Mage.Sets/src/mage/sets/theros/ShipbreakerKraken.java @@ -70,8 +70,7 @@ public class ShipbreakerKraken extends CardImpl { Ability ability = new BecomesMonstrousSourceTriggeredAbility(new TapTargetEffect()); ability.addTarget(new TargetCreaturePermanent(0,4)); ability.addEffect(new ShipbreakerKrakenReplacementEffect()); - this.addAbility(ability); - this.addWatcher(new ShipbreakerKrakenWatcher()); + this.addAbility(ability, new ShipbreakerKrakenWatcher()); } public ShipbreakerKraken(final ShipbreakerKraken card) { diff --git a/Mage.Sets/src/mage/sets/theros/TritonTactics.java b/Mage.Sets/src/mage/sets/theros/TritonTactics.java index 7ec540333c3..adc48470e27 100644 --- a/Mage.Sets/src/mage/sets/theros/TritonTactics.java +++ b/Mage.Sets/src/mage/sets/theros/TritonTactics.java @@ -77,7 +77,7 @@ public class TritonTactics extends CardImpl { this.getSpellAbility().addEffect(new TritonTacticsUntapTargetEffect()); this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new TritonTacticsTriggeredAbility())); - this.addWatcher(new BlockedCreaturesWatcher()); + this.getSpellAbility().addWatcher(new BlockedCreaturesWatcher()); } diff --git a/Mage.Sets/src/mage/sets/timespiral/ChronatogTotem.java b/Mage.Sets/src/mage/sets/timespiral/ChronatogTotem.java index a56594d2b3d..49a036eee2f 100644 --- a/Mage.Sets/src/mage/sets/timespiral/ChronatogTotem.java +++ b/Mage.Sets/src/mage/sets/timespiral/ChronatogTotem.java @@ -81,8 +81,7 @@ public class ChronatogTotem extends CardImpl { new ChronatogTotemCondition(), "{0}: {this} gets +3/+3 until end of turn. You skip your next turn. Activate this ability only once each turn and only if {this} is a creature"); ability.addEffect(new SkipNextTurnSourceEffect()); - this.addAbility(ability); - this.addWatcher(new ActivatedAbilityUsedThisTurnWatcher()); + this.addAbility(ability, new ActivatedAbilityUsedThisTurnWatcher()); } public ChronatogTotem(final ChronatogTotem card) { diff --git a/Mage.Sets/src/mage/sets/timespiral/GreaterGargadon.java b/Mage.Sets/src/mage/sets/timespiral/GreaterGargadon.java index 5efc1b49a7b..317a6273a74 100644 --- a/Mage.Sets/src/mage/sets/timespiral/GreaterGargadon.java +++ b/Mage.Sets/src/mage/sets/timespiral/GreaterGargadon.java @@ -104,7 +104,7 @@ class GreaterGargadonAbility extends ActivatedAbilityImpl { @Override public boolean canActivate(UUID playerId, Game game) { Card card = game.getCard(this.getSourceId()); - if(super.canActivate(playerId, game) && card != null && card.getCounters().getCount(CounterType.TIME) > 0){ + if(super.canActivate(playerId, game) && card != null && card.getCounters(game).getCount(CounterType.TIME) > 0){ return true; } return false; diff --git a/Mage.Sets/src/mage/sets/timespiral/JayaBallardTaskMage.java b/Mage.Sets/src/mage/sets/timespiral/JayaBallardTaskMage.java index fb12b3fedfe..e63d811fbca 100644 --- a/Mage.Sets/src/mage/sets/timespiral/JayaBallardTaskMage.java +++ b/Mage.Sets/src/mage/sets/timespiral/JayaBallardTaskMage.java @@ -90,8 +90,7 @@ public class JayaBallardTaskMage extends CardImpl { ability.addCost(new TapSourceCost()); ability.addCost(new DiscardCardCost()); ability.addEffect(new CantRegenerateEffect()); - this.addAbility(ability); - this.addWatcher(new DamagedByWatcher()); + this.addAbility(ability, new DamagedByWatcher()); // {5}{R}{R}, {tap}, Discard a card: Jaya Ballard deals 6 damage to each creature and each player. ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageEverythingEffect(6), new ManaCostsImpl("{5}{R}{R}")); diff --git a/Mage.Sets/src/mage/sets/timespiral/PitKeeper.java b/Mage.Sets/src/mage/sets/timespiral/PitKeeper.java new file mode 100644 index 00000000000..a6e1d955a87 --- /dev/null +++ b/Mage.Sets/src/mage/sets/timespiral/PitKeeper.java @@ -0,0 +1,95 @@ +/* + * 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.sets.timespiral; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.common.FilterCreatureCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +/** + * + * @author fireshoes + */ +public class PitKeeper extends CardImpl { + + public PitKeeper(UUID ownerId) { + super(ownerId, 123, "Pit Keeper", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{B}"); + this.expansionSetCode = "TSP"; + this.subtype.add("Human"); + this.subtype.add("Wizard"); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // When Pit Keeper enters the battlefield, if you have four or more creature cards in your graveyard, you may return target creature card from your graveyard to your hand. + TriggeredAbility triggeredAbility = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(), true); + triggeredAbility.addTarget(new TargetCardInYourGraveyard(new FilterCreatureCard("creature card from your graveyard"))); + this.addAbility(new ConditionalTriggeredAbility( + triggeredAbility, + new CreatureCardsInControllerGraveCondition(4), + "When {this} enters the battlefield, if you have four or more creature cards in your graveyard, you may return target creature card from your graveyard to your hand.")); + } + + public PitKeeper(final PitKeeper card) { + super(card); + } + + @Override + public PitKeeper copy() { + return new PitKeeper(this); + } +} + +class CreatureCardsInControllerGraveCondition implements Condition { + private int value; + + public CreatureCardsInControllerGraveCondition(int value) { + this.value = value; + } + + @Override + public boolean apply(Game game, Ability source) { + Player p = game.getPlayer(source.getControllerId()); + if (p != null && p.getGraveyard().count(new FilterCreatureCard(), game) >= value) + { + return true; + } + return false; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/timespiral/WalkTheAeons.java b/Mage.Sets/src/mage/sets/timespiral/WalkTheAeons.java index c91545836ca..b9fbde54213 100644 --- a/Mage.Sets/src/mage/sets/timespiral/WalkTheAeons.java +++ b/Mage.Sets/src/mage/sets/timespiral/WalkTheAeons.java @@ -36,7 +36,7 @@ import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.BuybackAbility; import mage.cards.CardImpl; -import mage.filter.common.FilterLandPermanent; +import mage.filter.common.FilterControlledLandPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.game.Game; import mage.game.turn.TurnMod; @@ -49,7 +49,7 @@ import mage.target.common.TargetControlledPermanent; */ public class WalkTheAeons extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent("three Islands"); + private static final FilterControlledLandPermanent filter = new FilterControlledLandPermanent("three Islands"); static { filter.add(new SubtypePredicate("Island")); diff --git a/Mage.Sets/src/mage/sets/torment/Overmaster.java b/Mage.Sets/src/mage/sets/torment/Overmaster.java index f92d64de40d..46a3a2a61ce 100644 --- a/Mage.Sets/src/mage/sets/torment/Overmaster.java +++ b/Mage.Sets/src/mage/sets/torment/Overmaster.java @@ -58,7 +58,7 @@ public class Overmaster extends CardImpl { // The next instant or sorcery spell you cast this turn can't be countered by spells or abilities. this.getSpellAbility().addEffect(new OvermasterEffect()); - this.addWatcher(new OvermasterWatcher()); + this.getSpellAbility().addWatcher(new OvermasterWatcher()); // Draw a card. Effect effect = new DrawCardSourceControllerEffect(1); diff --git a/Mage.Sets/src/mage/sets/unlimitededition/ForceOfNature.java b/Mage.Sets/src/mage/sets/unlimitededition/ForceOfNature.java new file mode 100644 index 00000000000..8090c9cdac1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/unlimitededition/ForceOfNature.java @@ -0,0 +1,52 @@ +/* + * 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.sets.unlimitededition; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class ForceOfNature extends mage.sets.fourthedition.ForceOfNature { + + public ForceOfNature(UUID ownerId) { + super(ownerId); + this.cardNumber = 103; + this.expansionSetCode = "2ED"; + } + + public ForceOfNature(final ForceOfNature card) { + super(card); + } + + @Override + public ForceOfNature copy() { + return new ForceOfNature(this); + } +} diff --git a/Mage.Sets/src/mage/sets/unlimitededition/Fork.java b/Mage.Sets/src/mage/sets/unlimitededition/Fork.java new file mode 100644 index 00000000000..2bf29d69bb8 --- /dev/null +++ b/Mage.Sets/src/mage/sets/unlimitededition/Fork.java @@ -0,0 +1,52 @@ +/* + * 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.sets.unlimitededition; + +import java.util.UUID; + +/** + * + * @author jeffwadsworth + */ +public class Fork extends mage.sets.limitedbeta.Fork { + + public Fork(UUID ownerId) { + super(ownerId); + this.cardNumber = 153; + this.expansionSetCode = "2ED"; + } + + public Fork(final Fork card) { + super(card); + } + + @Override + public Fork copy() { + return new Fork(this); + } +} diff --git a/Mage.Sets/src/mage/sets/urzasdestiny/AuraThief.java b/Mage.Sets/src/mage/sets/urzasdestiny/AuraThief.java new file mode 100644 index 00000000000..6eec8702b01 --- /dev/null +++ b/Mage.Sets/src/mage/sets/urzasdestiny/AuraThief.java @@ -0,0 +1,86 @@ +/* + * 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.sets.urzasdestiny; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continious.GainControlTargetEffect; +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.filter.common.FilterEnchantmentPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author nick.myers + */ + +public class AuraThief extends CardImpl { + + public AuraThief(UUID ownerId) { + super(ownerId, 26, "Aura Thief", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{U}"); + this.expansionSetCode = "UDS"; + this.subtype.add("Illusion"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Aura Thief dies, you gain control of all enchantments. You don't get + // to move Auras. + this.addAbility(new DiesTriggeredAbility(new AuraThiefDiesTriggeredEffect())); + } + + public AuraThief(final AuraThief card) { + super(card); + } + + @Override + public AuraThief copy() { + return new AuraThief(this); + } + +} + +class AuraThiefDiesTriggeredEffect extends OneShotEffect { + + public AuraThiefDiesTriggeredEffect() { + super(Outcome.Benefit); + this.staticText = "gain control of all enchantments. (You don't get to move Auras.)"; + } + + public AuraThiefDiesTriggeredEffect(final AuraThiefDiesTriggeredEffect effect) { + super(effect); + } + + @Override + public AuraThiefDiesTriggeredEffect copy() { + return new AuraThiefDiesTriggeredEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + boolean ret = false; + for(Permanent enchantment : game.getBattlefield().getActivePermanents(new FilterEnchantmentPermanent(), source.getControllerId(), source.getControllerId(), game)) { + ContinuousEffect gainControl = new GainControlTargetEffect(Duration.EndOfGame); + gainControl.setTargetPointer(new FixedTarget(enchantment.getId())); + game.addEffect(gainControl, source); + ret = true; + } + return ret; + } +} diff --git a/Mage.Sets/src/mage/sets/urzassaga/Contamination.java b/Mage.Sets/src/mage/sets/urzassaga/Contamination.java index c4ebedb2ada..d4ccf5e8ec8 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/Contamination.java +++ b/Mage.Sets/src/mage/sets/urzassaga/Contamination.java @@ -114,7 +114,7 @@ class ContaminationReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - MageObject mageObject = source.getSourceObject(game); + MageObject mageObject = game.getObject(event.getSourceId()); return mageObject != null && mageObject.getCardType().contains(CardType.LAND); } } diff --git a/Mage.Sets/src/mage/sets/urzassaga/Raze.java b/Mage.Sets/src/mage/sets/urzassaga/Raze.java index b3bb48bf80b..8b45e3946c2 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/Raze.java +++ b/Mage.Sets/src/mage/sets/urzassaga/Raze.java @@ -33,7 +33,7 @@ import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.common.FilterLandPermanent; +import mage.filter.common.FilterControlledLandPermanent; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetLandPermanent; @@ -50,7 +50,7 @@ public class Raze extends CardImpl { this.color.setRed(true); // As an additional cost to cast Raze, sacrifice a land. - this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledPermanent(1,1, new FilterLandPermanent(), true))); + this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledPermanent(1,1, new FilterControlledLandPermanent(), true))); // Destroy target land. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addTarget(new TargetLandPermanent()); diff --git a/Mage.Sets/src/mage/sets/vintagemasters/BragoKingEternal.java b/Mage.Sets/src/mage/sets/vintagemasters/BragoKingEternal.java index 3e2dce1821c..8e44b78f785 100644 --- a/Mage.Sets/src/mage/sets/vintagemasters/BragoKingEternal.java +++ b/Mage.Sets/src/mage/sets/vintagemasters/BragoKingEternal.java @@ -39,7 +39,10 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; import mage.target.common.TargetControlledPermanent; /** @@ -65,7 +68,9 @@ public class BragoKingEternal extends CardImpl { Effect effect = new ExileTargetEffect(this.getId(), this.getName(), Zone.BATTLEFIELD); effect.setText("exile any number of target nonland permanents you control"); Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(effect, false); - ability.addTarget(new TargetControlledPermanent(0, Integer.MAX_VALUE, new FilterNonlandPermanent(), false)); + FilterControlledPermanent filterControlledNonlandPermanent = new FilterControlledPermanent(); + filterControlledNonlandPermanent.add(Predicates.not(new CardTypePredicate(CardType.LAND))); + ability.addTarget(new TargetControlledPermanent(0, Integer.MAX_VALUE, filterControlledNonlandPermanent, false)); ability.addEffect(new ReturnFromExileEffect(this.getId(), Zone.BATTLEFIELD, ", then return those cards to the battlefield under their owner's control")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/visions/Fireblast.java b/Mage.Sets/src/mage/sets/visions/Fireblast.java index d53130cd225..131ddc9c722 100644 --- a/Mage.Sets/src/mage/sets/visions/Fireblast.java +++ b/Mage.Sets/src/mage/sets/visions/Fireblast.java @@ -30,15 +30,12 @@ package mage.sets.visions; import java.util.UUID; import mage.constants.CardType; import mage.constants.Rarity; -import mage.constants.TargetController; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; -import mage.filter.predicate.permanent.ControllerPredicate; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreatureOrPlayer; @@ -49,7 +46,7 @@ import mage.target.common.TargetCreatureOrPlayer; */ public class Fireblast extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("Mountain"); + private static final FilterControlledPermanent filter = new FilterControlledPermanent("Mountain"); static { filter.add(new SubtypePredicate("Mountain")); diff --git a/Mage.Sets/src/mage/sets/worldwake/CometStorm.java b/Mage.Sets/src/mage/sets/worldwake/CometStorm.java index 4bcf99dbe54..c34084ceb02 100644 --- a/Mage.Sets/src/mage/sets/worldwake/CometStorm.java +++ b/Mage.Sets/src/mage/sets/worldwake/CometStorm.java @@ -53,8 +53,6 @@ public class CometStorm extends CardImpl { super(ownerId, 76, "Comet Storm", Rarity.MYTHIC, new CardType[]{CardType.INSTANT}, "{X}{R}{R}"); this.expansionSetCode = "WWK"; - this.color.setRed(true); - // Multikicker {1} this.addAbility(new MultikickerAbility("{1}")); @@ -96,8 +94,8 @@ class CometStormEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { int damage = source.getManaCostsToPay().getX(); - Player you = game.getPlayer(source.getControllerId()); - if (you != null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { for (UUID uuid : this.getTargetPointer().getTargets(game, source)) { Permanent permanent = game.getPermanent(uuid); Player player = game.getPlayer(uuid); @@ -105,7 +103,7 @@ class CometStormEffect extends OneShotEffect { permanent.damage(damage, source.getSourceId(), game, false, true); } if (player != null) { - player.damage(damage, source.getSourceId(), game, true, false); + player.damage(damage, source.getSourceId(), game, false, true); } } return true; diff --git a/Mage.Sets/src/mage/sets/worldwake/CreepingTarPit.java b/Mage.Sets/src/mage/sets/worldwake/CreepingTarPit.java index 34d1a15c0f4..f07ec74d0fb 100644 --- a/Mage.Sets/src/mage/sets/worldwake/CreepingTarPit.java +++ b/Mage.Sets/src/mage/sets/worldwake/CreepingTarPit.java @@ -53,9 +53,15 @@ public class CreepingTarPit extends CardImpl { public CreepingTarPit (UUID ownerId) { super(ownerId, 134, "Creeping Tar Pit", Rarity.RARE, new CardType[]{CardType.LAND}, null); this.expansionSetCode = "WWK"; + + // Creeping Tar Pit enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {U} or {B} to your mana pool. this.addAbility(new BlueManaAbility()); this.addAbility(new BlackManaAbility()); + + // {1}{U}{B}: Until end of turn, Creeping Tar Pit becomes a 3/2 blue and black Elemental creature and is unblockable. It's still a land. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new CreepingTarPitToken(), "land", Duration.EndOfTurn), new ManaCostsImpl("{1}{U}{B}"))); } diff --git a/Mage.Sets/src/mage/sets/worldwake/Groundswell.java b/Mage.Sets/src/mage/sets/worldwake/Groundswell.java index 4e1f387359c..6bff573e174 100644 --- a/Mage.Sets/src/mage/sets/worldwake/Groundswell.java +++ b/Mage.Sets/src/mage/sets/worldwake/Groundswell.java @@ -59,7 +59,7 @@ public class Groundswell extends CardImpl { this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new GroundswellEffect(Duration.EndOfTurn)); - this.addWatcher(new LandfallWatcher()); + this.getSpellAbility().addWatcher(new LandfallWatcher()); } public Groundswell(final Groundswell card) { diff --git a/Mage.Sets/src/mage/sets/worldwake/MysteriesOfTheDeep.java b/Mage.Sets/src/mage/sets/worldwake/MysteriesOfTheDeep.java index 8d24949481e..c1297bdb4b2 100644 --- a/Mage.Sets/src/mage/sets/worldwake/MysteriesOfTheDeep.java +++ b/Mage.Sets/src/mage/sets/worldwake/MysteriesOfTheDeep.java @@ -50,7 +50,7 @@ public class MysteriesOfTheDeep extends CardImpl { // Draw two cards. // Landfall - If you had a land enter the battlefield under your control this turn, draw three cards instead. - this.addWatcher(new LandfallWatcher()); + this.getSpellAbility().addWatcher(new LandfallWatcher()); this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new DrawCardSourceControllerEffect(3), new DrawCardSourceControllerEffect(2), LandfallCondition.getInstance(), "Draw 2 cards. Landfall - If you had a land enter the battlefield under your control this turn, draw three cards instead")); } diff --git a/Mage.Sets/src/mage/sets/worldwake/PermafrostTrap.java b/Mage.Sets/src/mage/sets/worldwake/PermafrostTrap.java index 85225c6658e..fdabe4a2e0e 100644 --- a/Mage.Sets/src/mage/sets/worldwake/PermafrostTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/PermafrostTrap.java @@ -67,7 +67,7 @@ public class PermafrostTrap extends CardImpl { this.getSpellAbility().addTarget(target); this.getSpellAbility().addEffect(new PermafrostTrapEffect()); - this.addWatcher(new PermafrostTrapWatcher()); + this.getSpellAbility().addWatcher(new PermafrostTrapWatcher()); } public PermafrostTrap(final PermafrostTrap card) { diff --git a/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java b/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java index 1f447395b96..c169fb0906a 100644 --- a/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java @@ -70,7 +70,7 @@ public class RefractionTrap extends CardImpl { this.getSpellAbility().addTarget(new TargetSource()); this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); - this.addWatcher(new RefractionTrapWatcher()); + this.getSpellAbility().addWatcher(new RefractionTrapWatcher()); } public RefractionTrap(final RefractionTrap card) { diff --git a/Mage.Sets/src/mage/sets/worldwake/RestForTheWeary.java b/Mage.Sets/src/mage/sets/worldwake/RestForTheWeary.java index de447844887..89345c036e1 100644 --- a/Mage.Sets/src/mage/sets/worldwake/RestForTheWeary.java +++ b/Mage.Sets/src/mage/sets/worldwake/RestForTheWeary.java @@ -51,7 +51,7 @@ public class RestForTheWeary extends CardImpl { // Target player gains 4 life. // Landfall - If you had a land enter the battlefield under your control this turn, that player gains 8 life instead. - this.addWatcher(new LandfallWatcher()); + this.getSpellAbility().addWatcher(new LandfallWatcher()); this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new GainLifeTargetEffect(8), new GainLifeTargetEffect(4), LandfallCondition.getInstance(), "Target player gains 4 life.
Landfall - If you had a land enter the battlefield under your control this turn, that player gains 8 life instead")); this.getSpellAbility().addTarget(new TargetPlayer()); } diff --git a/Mage.Sets/src/mage/sets/worldwake/RicochetTrap.java b/Mage.Sets/src/mage/sets/worldwake/RicochetTrap.java index 583b1534cdd..3dfe8b14603 100644 --- a/Mage.Sets/src/mage/sets/worldwake/RicochetTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/RicochetTrap.java @@ -73,7 +73,7 @@ public class RicochetTrap extends CardImpl { this.getSpellAbility().addEffect(new ChooseNewTargetsTargetEffect(true, true)); this.getSpellAbility().addTarget(new TargetSpell(filter)); - this.addWatcher(new RicochetTrapWatcher()); + this.getSpellAbility().addWatcher(new RicochetTrapWatcher()); } public RicochetTrap(final RicochetTrap card) { diff --git a/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java b/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java index c0bf9c26677..6f6f606532a 100644 --- a/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java +++ b/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java @@ -65,7 +65,7 @@ public class SearingBlaze extends CardImpl { this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addTarget(new SearingBlazeTarget()); this.getSpellAbility().addEffect(new SearingBlazeEffect()); - this.addWatcher(new LandfallWatcher()); + this.getSpellAbility().addWatcher(new LandfallWatcher()); } public SearingBlaze(final SearingBlaze card) { diff --git a/Mage.Sets/src/mage/sets/worldwake/TombHex.java b/Mage.Sets/src/mage/sets/worldwake/TombHex.java index 8e56e332e16..68a318a48ed 100644 --- a/Mage.Sets/src/mage/sets/worldwake/TombHex.java +++ b/Mage.Sets/src/mage/sets/worldwake/TombHex.java @@ -54,7 +54,7 @@ public class TombHex extends CardImpl { // Target creature gets -2/-2 until end of turn. // Landfall - If you had a land enter the battlefield under your control this turn, that creature gets -4/-4 until end of turn instead. - this.addWatcher(new LandfallWatcher()); + this.getSpellAbility().addWatcher(new LandfallWatcher()); this.getSpellAbility().addEffect(new ConditionalContinousEffect(new BoostTargetEffect(-4, -4, Duration.EndOfTurn), new BoostTargetEffect(-2, -2, Duration.EndOfTurn), new LockedInCondition(LandfallCondition.getInstance()), "Target creature gets -2/-2 until end of turn. Landfall - If you had a land enter the battlefield under your control this turn, that creature gets -4/-4 until end of turn instead")); diff --git a/Mage.Sets/src/mage/sets/worldwake/TuktukScrapper.java b/Mage.Sets/src/mage/sets/worldwake/TuktukScrapper.java index 0840c653c5b..f1476ca3aee 100644 --- a/Mage.Sets/src/mage/sets/worldwake/TuktukScrapper.java +++ b/Mage.Sets/src/mage/sets/worldwake/TuktukScrapper.java @@ -28,17 +28,17 @@ package mage.sets.worldwake; import java.util.UUID; - -import mage.constants.*; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; -import mage.filter.predicate.permanent.ControllerPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -58,7 +58,6 @@ public class TuktukScrapper extends CardImpl { this.subtype.add("Artificer"); this.subtype.add("Ally"); - this.color.setRed(true); this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -80,6 +79,7 @@ class TuktukScrapperTriggeredAbility extends TriggeredAbilityImpl { public TuktukScrapperTriggeredAbility() { super(Zone.BATTLEFIELD, new TuktukScrapperEffect(), true); + this.addTarget(new TargetArtifactPermanent()); } public TuktukScrapperTriggeredAbility(final TuktukScrapperTriggeredAbility ability) { @@ -91,15 +91,19 @@ class TuktukScrapperTriggeredAbility extends TriggeredAbilityImpl { return new TuktukScrapperTriggeredAbility(this); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.getId() == this.getSourceId()) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null) { + if (permanent.getId() == this.getSourceId()) { return true; } - if (permanent != null - && permanent.hasSubtype("Ally") + if (permanent.hasSubtype("Ally") && permanent.getControllerId().equals(this.getControllerId())) { return true; } @@ -115,11 +119,10 @@ class TuktukScrapperTriggeredAbility extends TriggeredAbilityImpl { class TuktukScrapperEffect extends OneShotEffect { - private static final FilterPermanent filter = new FilterPermanent(); + private static final FilterControlledPermanent filter = new FilterControlledPermanent(); static { filter.add(new SubtypePredicate("Ally")); - filter.add(new ControllerPredicate(TargetController.YOU)); } public TuktukScrapperEffect() { @@ -137,22 +140,18 @@ class TuktukScrapperEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - TargetArtifactPermanent target = new TargetArtifactPermanent(); - Player you = game.getPlayer(source.getControllerId()); - if (you != null) { - if (target.canChoose(source.getControllerId(), game) && target.choose(Outcome.DestroyPermanent, source.getControllerId(), source.getSourceId(), game)) { - Permanent targetedArtifact = game.getPermanent(target.getFirstTarget()); - if (targetedArtifact != null) { - Card artifact = game.getCard(targetedArtifact.getId()); - Player controller = game.getPlayer(targetedArtifact.getControllerId()); - targetedArtifact.destroy(id, game, true); - if (controller.getGraveyard().contains(artifact.getId())) { - int alliesControlled = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game); - controller.damage(alliesControlled, id, game, false, true); - return true; - } + Permanent targetArtifact = game.getPermanent(getTargetPointer().getFirst(game, source)); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null && targetArtifact != null) { + targetArtifact.destroy(source.getSourceId(), game, false); + Player targetController = game.getPlayer(targetArtifact.getControllerId()); + if (targetController != null && game.getState().getZone(targetArtifact.getId()).equals(Zone.GRAVEYARD)) { + int alliesControlled = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game); + if (alliesControlled > 0) { + targetController.damage(alliesControlled, source.getSourceId(), game, false, true); } } + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/zendikar/ArchiveTrap.java b/Mage.Sets/src/mage/sets/zendikar/ArchiveTrap.java index df87f8e4d60..9b8e24f3c96 100644 --- a/Mage.Sets/src/mage/sets/zendikar/ArchiveTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/ArchiveTrap.java @@ -60,8 +60,7 @@ public class ArchiveTrap extends CardImpl { this.color.setBlue(true); // If an opponent searched his or her library this turn, you may pay {0} rather than pay Archive Trap's mana cost. - this.addAbility(new AlternativeCostSourceAbility(new GenericManaCost(0), OpponentSearchesLibCondition.getInstance())); - this.addWatcher(new ArchiveTrapWatcher()); + this.addAbility(new AlternativeCostSourceAbility(new GenericManaCost(0), OpponentSearchesLibCondition.getInstance()), new ArchiveTrapWatcher()); // Target opponent puts the top thirteen cards of his or her library into his or her graveyard. this.getSpellAbility().addTarget(new TargetOpponent()); diff --git a/Mage.Sets/src/mage/sets/zendikar/ArchmageAscension.java b/Mage.Sets/src/mage/sets/zendikar/ArchmageAscension.java index 7270d36c9bc..b1133fa496f 100644 --- a/Mage.Sets/src/mage/sets/zendikar/ArchmageAscension.java +++ b/Mage.Sets/src/mage/sets/zendikar/ArchmageAscension.java @@ -59,8 +59,7 @@ public class ArchmageAscension extends CardImpl { this.color.setBlue(true); // At the beginning of each end step, if you drew two or more cards this turn, you may put a quest counter on Archmage Ascension. - this.addAbility(new ArchmageAscensionTriggeredAbility()); - this.addWatcher(new CardsDrawnControllerWatcher()); + this.addAbility(new ArchmageAscensionTriggeredAbility(), new CardsDrawnControllerWatcher()); // As long as Archmage Ascension has six or more quest counters on it, if you would draw a card, you may instead search your library for a card, put that card into your hand, then shuffle your library. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ArchmageAscensionReplacementEffect())); diff --git a/Mage.Sets/src/mage/sets/zendikar/BalothCageTrap.java b/Mage.Sets/src/mage/sets/zendikar/BalothCageTrap.java index d2e0699346a..3a36a09599a 100644 --- a/Mage.Sets/src/mage/sets/zendikar/BalothCageTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/BalothCageTrap.java @@ -60,7 +60,7 @@ public class BalothCageTrap extends CardImpl { // If an opponent had an artifact enter the battlefield under his or her control this turn, you may pay {1}{G} rather than pay Baloth Cage Trap's mana cost. this.getSpellAbility().addAlternativeCost(new BalothCageTrapAlternativeCost()); - this.addWatcher(new BalothCageTrapWatcher()); + this.getSpellAbility().addWatcher(new BalothCageTrapWatcher()); // Put a 4/4 green Beast creature token onto the battlefield. this.getSpellAbility().addEffect(new CreateTokenEffect(new BeastGreenToken())); diff --git a/Mage.Sets/src/mage/sets/zendikar/CobraTrap.java b/Mage.Sets/src/mage/sets/zendikar/CobraTrap.java index 00f7c04027a..fa224904334 100644 --- a/Mage.Sets/src/mage/sets/zendikar/CobraTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/CobraTrap.java @@ -63,7 +63,7 @@ public class CobraTrap extends CardImpl { // If a noncreature permanent under your control was destroyed this turn by a spell or ability an opponent controlled, you may pay {G} rather than pay Cobra Trap's mana cost. this.getSpellAbility().addAlternativeCost( new CobraTrapAlternativeCost()); - this.addWatcher(new CobraTrapWatcher()); + this.getSpellAbility().addWatcher(new CobraTrapWatcher()); // Put four 1/1 green Snake creature tokens onto the battlefield. this.getSpellAbility().addEffect(new CreateTokenEffect(new SnakeToken(), 4)); } diff --git a/Mage.Sets/src/mage/sets/zendikar/Gomazoa.java b/Mage.Sets/src/mage/sets/zendikar/Gomazoa.java index 9e11761f5bc..31c14fb894b 100644 --- a/Mage.Sets/src/mage/sets/zendikar/Gomazoa.java +++ b/Mage.Sets/src/mage/sets/zendikar/Gomazoa.java @@ -71,8 +71,7 @@ public class Gomazoa extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // {tap}: Put Gomazoa and each creature it's blocking on top of their owners' libraries, then those players shuffle their libraries. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GomazoaEffect(), new TapSourceCost())); - this.addWatcher(new BlockedByWatcher()); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GomazoaEffect(), new TapSourceCost()), new BlockedByWatcher()); } diff --git a/Mage.Sets/src/mage/sets/zendikar/InfernoTrap.java b/Mage.Sets/src/mage/sets/zendikar/InfernoTrap.java index e494a9f9c52..821a34bfdb9 100644 --- a/Mage.Sets/src/mage/sets/zendikar/InfernoTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/InfernoTrap.java @@ -58,7 +58,7 @@ public class InfernoTrap extends CardImpl { // If you've been dealt damage by two or more creatures this turn, you may pay {R} rather than pay Inferno Trap's mana cost. this.getSpellAbility().addAlternativeCost(new InfernoTrapAlternativeCost()); - this.addWatcher(new ControllerDamagedByCreatureWatcher()); + this.getSpellAbility().addWatcher(new ControllerDamagedByCreatureWatcher()); // Inferno Trap deals 4 damage to target creature. this.getSpellAbility().addEffect(new DamageTargetEffect(4)); diff --git a/Mage.Sets/src/mage/sets/zendikar/LavaballTrap.java b/Mage.Sets/src/mage/sets/zendikar/LavaballTrap.java index 96737265ec1..9630fa052e5 100644 --- a/Mage.Sets/src/mage/sets/zendikar/LavaballTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/LavaballTrap.java @@ -62,7 +62,7 @@ public class LavaballTrap extends CardImpl { // If an opponent had two or more lands enter the battlefield under his or her control this turn, you may pay {3}{R}{R} rather than pay Lavaball Trap's mana cost. this.getSpellAbility().addAlternativeCost(new LavaballTrapAlternativeCost()); - this.addWatcher(new LavaballTrapWatcher()); + this.getSpellAbility().addWatcher(new LavaballTrapWatcher()); // Destroy two target lands. Lavaball Trap deals 4 damage to each creature. this.getSpellAbility().addEffect(new DestroyTargetEffect()); diff --git a/Mage.Sets/src/mage/sets/zendikar/LullmageMentor.java b/Mage.Sets/src/mage/sets/zendikar/LullmageMentor.java index aa5c2c714c8..ec4f11a5d1a 100644 --- a/Mage.Sets/src/mage/sets/zendikar/LullmageMentor.java +++ b/Mage.Sets/src/mage/sets/zendikar/LullmageMentor.java @@ -79,8 +79,7 @@ public class LullmageMentor extends CardImpl { this.toughness = new MageInt(2); // Whenever a spell or ability you control counters a spell, you may put a 1/1 blue Merfolk creature token onto the battlefield. - this.addAbility(new LullmageMentorTriggeredAbility()); - this.addWatcher(new CastedSpellsWithSpellTarget()); + this.addAbility(new LullmageMentorTriggeredAbility(), new CastedSpellsWithSpellTarget()); // Tap seven untapped Merfolk you control: Counter target spell. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterTargetEffect(), new TapTargetCost(new TargetControlledCreaturePermanent(7, 7, filter, true))); ability.addTarget(new TargetSpell()); diff --git a/Mage.Sets/src/mage/sets/zendikar/MindbreakTrap.java b/Mage.Sets/src/mage/sets/zendikar/MindbreakTrap.java index a1deb161b8a..9942326adf8 100644 --- a/Mage.Sets/src/mage/sets/zendikar/MindbreakTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/MindbreakTrap.java @@ -68,7 +68,7 @@ public class MindbreakTrap extends CardImpl { // If an opponent cast three or more spells this turn, you may pay {0} rather than pay Mindbreak Trap's mana cost. this.getSpellAbility().addAlternativeCost( new MindbreakTrapAlternativeCost()); - this.addWatcher(new MindbreakTrapWatcher()); + this.getSpellAbility().addWatcher(new MindbreakTrapWatcher()); // Exile any number of target spells. this.getSpellAbility().addTarget(new TargetSpell(0, Integer.MAX_VALUE, filter)); this.getSpellAbility().addEffect(new MindbreakEffect()); diff --git a/Mage.Sets/src/mage/sets/zendikar/NeedlebiteTrap.java b/Mage.Sets/src/mage/sets/zendikar/NeedlebiteTrap.java index 861eef51997..84cd7367564 100644 --- a/Mage.Sets/src/mage/sets/zendikar/NeedlebiteTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/NeedlebiteTrap.java @@ -58,7 +58,7 @@ public class NeedlebiteTrap extends CardImpl { // If an opponent gained life this turn, you may pay {B} rather than pay Needlebite Trap's mana cost. this.getSpellAbility().addAlternativeCost( new NeedlebiteTrapAlternativeCost()); - this.addWatcher(new PlayerGainedLifeWatcher()); + this.getSpellAbility().addWatcher(new PlayerGainedLifeWatcher()); // Target player loses 5 life and you gain 5 life. this.getSpellAbility().addEffect(new LoseLifeTargetEffect(5)); diff --git a/Mage.Sets/src/mage/sets/zendikar/RavenousTrap.java b/Mage.Sets/src/mage/sets/zendikar/RavenousTrap.java index 1a690766e22..120edcf6c68 100644 --- a/Mage.Sets/src/mage/sets/zendikar/RavenousTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/RavenousTrap.java @@ -61,7 +61,7 @@ public class RavenousTrap extends CardImpl { // If an opponent had three or more cards put into his or her graveyard from anywhere this turn, you may pay {0} rather than pay Ravenous Trap's mana cost. this.getSpellAbility().addAlternativeCost( new RavenousTrapAlternativeCost()); - this.addWatcher(new CardsPutIntoGraveyardWatcher()); + this.getSpellAbility().addWatcher(new CardsPutIntoGraveyardWatcher()); // Exile all cards from target player's graveyard. this.getSpellAbility().addEffect(new ExileGraveyardAllTargetPlayerEffect()); diff --git a/Mage.Sets/src/mage/sets/zendikar/RuneflareTrap.java b/Mage.Sets/src/mage/sets/zendikar/RuneflareTrap.java index 68deafa62a4..71621ce4083 100644 --- a/Mage.Sets/src/mage/sets/zendikar/RuneflareTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/RuneflareTrap.java @@ -60,7 +60,7 @@ public class RuneflareTrap extends CardImpl { // If an opponent drew three or more cards this turn, you may pay {R} rather than pay Runeflare Trap's mana cost. this.getSpellAbility().addAlternativeCost(new RuneflareTrapAlternativeCost()); - this.addWatcher(new CardsDrawnOpponentWatcher()); + this.getSpellAbility().addWatcher(new CardsDrawnOpponentWatcher()); // Runeflare Trap deals damage to target player equal to the number of cards in that player's hand. this.getSpellAbility().addEffect(new DamageTargetEffect(new TargetPlayerCardsInHandCount())); diff --git a/Mage.Sets/src/mage/sets/zendikar/SummoningTrap.java b/Mage.Sets/src/mage/sets/zendikar/SummoningTrap.java index e92f984f9cc..c5319ca860b 100644 --- a/Mage.Sets/src/mage/sets/zendikar/SummoningTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/SummoningTrap.java @@ -71,7 +71,7 @@ public class SummoningTrap extends CardImpl { // Summoning Trap's mana cost. this.getSpellAbility().addAlternativeCost( new SummoningTrapAlternativeCost()); - this.addWatcher(new SummoningTrapWatcher()); + this.getSpellAbility().addWatcher(new SummoningTrapWatcher()); // Look at the top seven cards of your library. You may put a creature // card from among them onto the battlefield. Put the rest on the bottom // of your library in any order. diff --git a/Mage.Sets/src/mage/sets/zendikar/WhiplashTrap.java b/Mage.Sets/src/mage/sets/zendikar/WhiplashTrap.java index 15bb414d30e..7a7d6c91712 100644 --- a/Mage.Sets/src/mage/sets/zendikar/WhiplashTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/WhiplashTrap.java @@ -60,7 +60,7 @@ public class WhiplashTrap extends CardImpl { // If an opponent had two or more creatures enter the battlefield under his or her control this turn, you may pay {U} rather than pay Whiplash Trap's mana cost. this.getSpellAbility().addAlternativeCost(new WhiplashAlternativeCost()); - this.addWatcher(new WhiplashTrapWatcher()); + this.getSpellAbility().addWatcher(new WhiplashTrapWatcher()); // Return two target creatures to their owners' hands. this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); diff --git a/Mage.Sets/src/mage/sets/zendikar/WorldQueller.java b/Mage.Sets/src/mage/sets/zendikar/WorldQueller.java index 20f5f4debf2..a7952dad323 100644 --- a/Mage.Sets/src/mage/sets/zendikar/WorldQueller.java +++ b/Mage.Sets/src/mage/sets/zendikar/WorldQueller.java @@ -143,7 +143,7 @@ class WorldQuellerEffect extends OneShotEffect { type = CardType.TRIBAL; } if (type != null) { - FilterPermanent filter = new FilterControlledPermanent(new StringBuilder("permanent you control of type ").append(type.toString()).toString()); + FilterControlledPermanent filter = new FilterControlledPermanent(new StringBuilder("permanent you control of type ").append(type.toString()).toString()); filter.add(new CardTypePredicate(type)); TargetPermanent target = new TargetControlledPermanent(1, 1, filter, false); @@ -168,7 +168,7 @@ class WorldQuellerEffect extends OneShotEffect { Player player = game.getPlayer(playerId); if (target.canChoose(playerId, game)) { while (!target.isChosen() && target.canChoose(playerId, game)) { - player.choose(Outcome.Sacrifice, target, source.getSourceId(), game); + player.chooseTarget(Outcome.Sacrifice, target, source, game); } Permanent permanent = game.getPermanent(target.getFirstTarget()); if (permanent != null) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java index f466f34a33b..9b34753da42 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java @@ -192,6 +192,9 @@ public class CursesTest extends CardTestPlayerBase { @Test public void testCurseOfMisfortune1() { removeAllCardsFromLibrary(playerA); + + // At the beginning of your upkeep, you may search your library for a Curse card that doesn't have the same name as a + // Curse attached to enchanted player, put it onto the battlefield attached to that player, then shuffle your library. addCard(Zone.LIBRARY, playerA, "Curse of Misfortunes", 2); addCard(Zone.HAND, playerA, "Curse of Misfortunes"); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CascadeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CascadeTest.java new file mode 100644 index 00000000000..afdc4d69538 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CascadeTest.java @@ -0,0 +1,73 @@ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author BetaSteward + */ +public class CascadeTest extends CardTestPlayerBase { + + /* + * Maelstrom Nexus {WUBRG} + * Enchantment + * The first spell you cast each turn has cascade. (When you cast your first + * spell, exile cards from the top of your library until you exile a nonland + * card that costs less. You may cast it without paying its mana cost. Put + * the exiled cards on the bottom in a random order.) + * + * Predatory Advantage {3RG} + * Enchantment + * At the beginning of each opponent's end step, if that player didn't cast + * a creature spell this turn, put a 2/2 green Lizard creature token onto + * the battlefield. + */ + + // test that Predatory Advantage gains Cascade when cast + @Test + public void testGainsCascade() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + addCard(Zone.BATTLEFIELD, playerA, "Maelstrom Nexus"); + addCard(Zone.HAND, playerA, "Predatory Advantage"); + addCard(Zone.LIBRARY, playerA, "Sejiri Merfolk"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Predatory Advantage"); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Predatory Advantage", 1); + assertPermanentCount(playerA, "Sejiri Merfolk", 1); + assertPermanentCount(playerA, "Lizard", 1); + + } + + // test that 2nd spell cast (Nacatl Outlander) does not gain Cascade + @Test + public void testLosesCascade() { + skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); + addCard(Zone.BATTLEFIELD, playerA, "Maelstrom Nexus"); + addCard(Zone.HAND, playerA, "Predatory Advantage"); + addCard(Zone.HAND, playerA, "Nacatl Outlander"); + addCard(Zone.LIBRARY, playerA, "Arbor Elf"); + addCard(Zone.LIBRARY, playerA, "Sejiri Merfolk"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Predatory Advantage"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Nacatl Outlander"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Predatory Advantage", 1); + assertPermanentCount(playerA, "Sejiri Merfolk", 1); + assertPermanentCount(playerA, "Nacatl Outlander", 1); + assertPermanentCount(playerA, "Arbor Elf", 0); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EchoTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EchoTest.java new file mode 100644 index 00000000000..7188e0a989e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EchoTest.java @@ -0,0 +1,85 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class EchoTest extends CardTestPlayerBase { + + /* + * I flickered an Avalanche Riders with its Echo trigger on the stack with Restoration Angel. + * When the trigger resolved, my Riders was sacrificed, even though it should have been + * considered a new permanent. + */ + + @Test + public void testEchoTriggerChecksIdentity() { + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // Avalanche Riders Creature - Human Nomad 2/2 + // Haste + // Echo (At the beginning of your upkeep, if this came under your control since the beginning of your last upkeep, sacrifice it unless you pay its echo cost.) + // When Avalanche Riders enters the battlefield, destroy target land. + addCard(Zone.HAND, playerA, "Avalanche Riders"); + + // Restoration Angel {3}{W} + // Flash + // Flying + // When Restoration Angel enters the battlefield, you may exile target non-Angel creature you control, + // then return that card to the battlefield under your control. + addCard(Zone.HAND, playerA, "Restoration Angel"); + + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Avalanche Riders"); + + castSpell(3, PhaseStep.UPKEEP, playerA, "Restoration Angel", null, "Echo {3}{R} (At the beginning of your upkeep, if this came under your control since the beginning of your last upkeep, sacrifice it unless you pay its echo cost.)"); + addTarget(playerA, "Avalanche Riders"); + setStopAt(3, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertPermanentCount(playerA, "Avalanche Riders", 1); + assertPermanentCount(playerA, "Restoration Angel", 1); + + assertPermanentCount(playerB, "Mountain", 0); + } + + +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java index e4e861283ce..3f827caec13 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java @@ -76,4 +76,109 @@ public class UndyingTest extends CardTestPlayerBase { assertPowerToughness(playerA, "Elite Vanguard", 3, 2); } + + /** + * Tests "Threads of Disloyalty enchanting Strangleroot Geist: after geist died it returns to the bf under opponent's control." + */ + @Test + public void testUndyingControlledReturnsToOwner() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + // Strangleroot Geist 2/1 + // Haste + // Undying + // (When it dies, if it had no +1/+1 counters on it, return it to the battlefield under its owner's control with a +1/+1 counter on it.) + addCard(Zone.HAND, playerA, "Strangleroot Geist"); + addCard(Zone.HAND, playerA, "Lightning Bolt"); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 3); + // Threads of Disloyalty {1}{U}{U} + // Enchant creature with converted mana cost 2 or less + // You control enchanted creature. + addCard(Zone.HAND, playerB, "Threads of Disloyalty"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Strangleroot Geist"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Threads of Disloyalty", "Strangleroot Geist"); + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Strangleroot Geist"); + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, "Threads of Disloyalty", 1); + assertGraveyardCount(playerA, "Lightning Bolt",1); + assertPermanentCount(playerB, "Strangleroot Geist", 0); + assertPermanentCount(playerA, "Strangleroot Geist", 1); + assertPowerToughness(playerA, "Strangleroot Geist", 3, 2); + } + + /** + * Tests "Target creature with Undying will be exiled by Anafenza before it returns to battlefield + * + * Anafenza the foremost doesn't exile an undying creature when dying at the same time as + * that undying one. The undying comes back to the field when he shouldn't. + */ + @Test + public void testReplacementEffectPreventsReturnOfUndying() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // Butcher Ghoul + // Creature - Zombie, 1/1 {1}{B} + // Undying (When this creature dies, if it had no +1/+1 counters on it, return it to the battlefield under its owner's control with a +1/+1 counter on it.) + addCard(Zone.HAND, playerA, "Butcher Ghoul"); + + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.HAND, playerB, "Lightning Bolt"); + // Anafenza, the Foremost + // Whenever Anafenza, the Foremost attacks, put a +1/+1 counter on another target tapped creature you control. + // If a creature card would be put into an opponent's graveyard from anywhere, exile it instead. + addCard(Zone.BATTLEFIELD, playerB, "Anafenza, the Foremost"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Butcher Ghoul"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", "Butcher Ghoul"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerB, "Anafenza, the Foremost", 1); + assertGraveyardCount(playerB, "Lightning Bolt", 1); + + assertPermanentCount(playerA, "Butcher Ghoul", 0); + assertExileCount("Butcher Ghoul", 1); + } + + /** + * Tests "Target creature with Undying will be exiled by Anafenza before it returns to battlefield + * if both leave the battlefield at the same time + * + * Anafenza the foremost doesn't exile an undying creature when dying at the same time as + * that undying one. The undying comes back to the field when he shouldn't. + */ + @Test + public void testReplacementEffectPreventsReturnOfUndyingWrath() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // Butcher Ghoul + // Creature - Zombie, 1/1 {1}{B} + // Undying (When this creature dies, if it had no +1/+1 counters on it, return it to the battlefield under its owner's control with a +1/+1 counter on it.) + addCard(Zone.HAND, playerA, "Butcher Ghoul"); + + addCard(Zone.BATTLEFIELD, playerB, "Plains", 4); + // Destroy all creatures. They can't be regenerated. + addCard(Zone.HAND, playerB, "Wrath of God"); + // Anafenza, the Foremost + // Whenever Anafenza, the Foremost attacks, put a +1/+1 counter on another target tapped creature you control. + // If a creature card would be put into an opponent's graveyard from anywhere, exile it instead. + addCard(Zone.BATTLEFIELD, playerB, "Anafenza, the Foremost"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Butcher Ghoul"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Wrath of God"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, "Anafenza, the Foremost", 1); + assertGraveyardCount(playerB, "Wrath of God", 1); + + assertPermanentCount(playerA, "Butcher Ghoul", 0); + assertExileCount("Butcher Ghoul", 1); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/sacrifice/MorticianBeetleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/sacrifice/MorticianBeetleTest.java index c6e1535f5d1..9d109b6a759 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/sacrifice/MorticianBeetleTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/sacrifice/MorticianBeetleTest.java @@ -11,7 +11,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase; public class MorticianBeetleTest extends CardTestPlayerBase { /** - * Checks that pro black can still be sacrificed + * Checks that creature with protection black can still be sacrificed */ @Test public void testSacrifice() { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java index 88dc2964198..4b2fc04a518 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java @@ -185,5 +185,42 @@ public class CavernOfSoulsTest extends CardTestPlayerBase { // Check Horror on the Battlefield // assertPermanentCount(playerA, "Fume Spitter", 1); } - + + /** + * Return to the Ranks cannot be countered if mana produced by Cavern of Souls + * was used to pay X. Can be bug also for all other spells with X in their cost, not sure. + * + */ + @Test + public void testCastWithColorlessManaCanBeCountered() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.HAND, playerA, "Cavern of Souls"); + // Sorcery {X}{W}{W} + // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for or one mana of that creature's color.) + // Return X target creature cards with converted mana cost 2 or less from your graveyard to the battlefield. + addCard(Zone.HAND, playerA, "Return to the Ranks"); + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); + + // {1}{U} Remove Soul - Counter target creature spell. + addCard(Zone.HAND, playerB, "Counterspell"); + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cavern of Souls"); + setChoice(playerA, "Drake"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Return to the Ranks", "Silvercoat Lion"); + setChoice(playerA, "X=1"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Counterspell", "Return to the Ranks"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + // check it was countered + assertGraveyardCount(playerA, "Return to the Ranks", 1); + assertGraveyardCount(playerB, "Counterspell", 1); + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertPermanentCount(playerA, "Silvercoat Lion", 0); + + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/AggravateTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/AggravateTest.java new file mode 100644 index 00000000000..ecc5c05bdb0 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/AggravateTest.java @@ -0,0 +1,61 @@ +package org.mage.test.cards.watchers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author BetaSteward + */ +public class AggravateTest extends CardTestPlayerBase { + /* + * Aggravate + * Instant, 3RR (5) + * Aggravate deals 1 damage to each creature target player controls. Each + * creature dealt damage this way attacks this turn if able. + * + */ + + // test that creatures damaged by Aggravate attack + @Test + public void testDamagedCreaturesAttack() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerB, "Craw Wurm"); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Roughrider"); + addCard(Zone.HAND, playerA, "Aggravate"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Aggravate", playerB); + + setStopAt(2, PhaseStep.DECLARE_BLOCKERS); + execute(); + + assertAttacking("Craw Wurm", true); + assertAttacking("Goblin Roughrider", true); + + } + + // test that creatures not damaged by Aggravate don't attack + @Test + public void testUndamagedCreaturesDontAttack() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerB, "Craw Wurm"); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Roughrider"); + addCard(Zone.HAND, playerA, "Aggravate"); + addCard(Zone.HAND, playerB, "Raging Goblin"); + + castSpell(2, PhaseStep.UPKEEP, playerA, "Aggravate", playerB); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Raging Goblin"); + + setStopAt(2, PhaseStep.DECLARE_BLOCKERS); + execute(); + + assertAttacking("Craw Wurm", true); + assertAttacking("Goblin Roughrider", true); + assertAttacking("Raging Goblin", false); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/AzoriusHeraldTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/AzoriusHeraldTest.java new file mode 100644 index 00000000000..2791389e3ae --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/AzoriusHeraldTest.java @@ -0,0 +1,55 @@ +package org.mage.test.cards.watchers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class AzoriusHeraldTest extends CardTestPlayerBase { + + /* + * Azorius Herald + * Creature — Spirit 2/1, 2W (3) + * Azorius Herald can't be blocked. + * When Azorius Herald enters the battlefield, you gain 4 life. + * When Azorius Herald enters the battlefield, sacrifice it unless {U} was spent to cast it. + * + */ + + // When Azorius Herald enters the battlefield, sacrifice it unless {U} was spent to cast it. + @Test + public void testBlueManaWasPaid() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Island"); + addCard(Zone.HAND, playerA, "Azorius Herald"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Azorius Herald"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Azorius Herald", 1); + + } + + @Test + public void testNoBlueManaWasPaid() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.HAND, playerA, "Azorius Herald"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Azorius Herald"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Azorius Herald", 0); + assertGraveyardCount(playerA, "Azorius Herald", 1); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/BoseijuTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/BoseijuTest.java new file mode 100644 index 00000000000..63340f54f37 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/BoseijuTest.java @@ -0,0 +1,60 @@ +package org.mage.test.cards.watchers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author BetaSteward + */ +public class BoseijuTest extends CardTestPlayerBase { + /* + * Boseiju, Who Shelters All + * Legendary Land + * Boseiju, Who Shelters All enters the battlefield tapped. + * {T}, Pay 2 life: Add {1} to your mana pool. If that mana is spent on an + * instant or sorcery spell, that spell can't be countered by spells or abilities. + * + */ + + // test that instants and soceries can't be countered when Boseiju mana is used + @Test + public void testCantCounter() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 6); + addCard(Zone.BATTLEFIELD, playerA, "Boseiju, Who Shelters All"); + addCard(Zone.HAND, playerA, "Brilliant Plan"); + addCard(Zone.HAND, playerA, "Counterspell"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Brilliant Plan"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Counterspell", "Brilliant Plan"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + this.assertHandCount(playerA, 3); + this.assertGraveyardCount(playerA, "Counterspell", 1); + + } + + // test that instants and soceries can be countered when Boseiju mana is not used + @Test + public void testCanCounter() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 6); + addCard(Zone.BATTLEFIELD, playerA, "Boseiju, Who Shelters All"); + addCard(Zone.HAND, playerA, "Mental Note"); + addCard(Zone.HAND, playerA, "Counterspell"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mental Note"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Counterspell", "Mental Note"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + this.assertHandCount(playerA, 0); + this.assertGraveyardCount(playerA, 2); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/DiesExiledTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/DiesExiledTest.java new file mode 100644 index 00000000000..a3c53d22beb --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/DiesExiledTest.java @@ -0,0 +1,141 @@ +package org.mage.test.cards.watchers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author BetaSteward + */ +public class DiesExiledTest extends CardTestPlayerBase { + /* + * Kumano's Blessing + * Enchantment — Aura, 2R (3) + * Flash + * Enchant creature + * If a creature dealt damage by enchanted creature this turn would die, exile it instead. + * + */ + + // test that when creature damaged by enchanted creature dies it is exiled + @Test + public void testKumanosBlessing() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.HAND, playerA, "Kumano's Blessing"); + addCard(Zone.BATTLEFIELD, playerA, "Prodigal Pyromancer"); + addCard(Zone.BATTLEFIELD, playerB, "Sejiri Merfolk"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kumano's Blessing", "Prodigal Pyromancer"); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: {source} deals 1 damage to target creature or player.", "Sejiri Merfolk"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerB, "Sejiri Merfolk", 0); + assertExileCount("Sejiri Merfolk", 1); + + } + + /* + * Frostwielder + * Creature — Human Shaman 1/2, 2RR (4) + * {T}: Frostwielder deals 1 damage to target creature or player. + * If a creature dealt damage by Frostwielder this turn would die, exile it instead. + * + */ + + // test that when creature damaged by Frostwielder dies it is exiled + @Test + public void testFrostwielder() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, "Frostwielder"); + addCard(Zone.BATTLEFIELD, playerB, "Sejiri Merfolk"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals 1 damage to target creature or player.", "Sejiri Merfolk"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerB, "Sejiri Merfolk", 0); + assertExileCount("Sejiri Merfolk", 1); + + } + + /* + * Kumano, Master Yamabushi + * Legendary Creature — Human Shaman 4/4, 3RR (5) + * {1}{R}: Kumano, Master Yamabushi deals 1 damage to target creature or player. + * If a creature dealt damage by Kumano this turn would die, exile it instead. + * + */ + + // test that when creature damaged by Kumano, Master Yamabushi dies it is exiled + @Test + public void testKumanoMasterYamabushi() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, "Kumano, Master Yamabushi"); + addCard(Zone.BATTLEFIELD, playerB, "Sejiri Merfolk"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{R}: {source} deals 1 damage to target creature or player.", "Sejiri Merfolk"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerB, "Sejiri Merfolk", 0); + assertExileCount("Sejiri Merfolk", 1); + + } + + /* + * Yamabushi's Flame + * Instant, 2R (3) + * Yamabushi's Flame deals 3 damage to target creature or player. If a + * creature dealt damage this way would die this turn, exile it instead. + * + */ + + // test that when creature damaged by Yamabushi's Flame dies it is exiled + @Test + public void testYamabushisFlame() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.HAND, playerA, "Yamabushi's Flame"); + addCard(Zone.BATTLEFIELD, playerB, "Sejiri Merfolk"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Yamabushi's Flame", "Sejiri Merfolk"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerB, "Sejiri Merfolk", 0); + assertExileCount("Sejiri Merfolk", 1); + + } + + /* + * Yamabushi's Storm + * Sorcery, 1R (2) + * Yamabushi's Storm deals 1 damage to each creature. If a creature dealt + * damage this way would die this turn, exile it instead. + * + */ + + // test that when creatures damaged by Yamabushi's Storm die they are exiled + @Test + public void testYamabushisStorm() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.HAND, playerA, "Yamabushi's Storm"); + addCard(Zone.BATTLEFIELD, playerB, "Sejiri Merfolk", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Yamabushi's Storm"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerB, "Sejiri Merfolk", 0); + assertExileCount("Sejiri Merfolk", 2); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/EpharaGodOfThePolisTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/EpharaGodOfThePolisTest.java new file mode 100644 index 00000000000..24c886f8d0f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/EpharaGodOfThePolisTest.java @@ -0,0 +1,53 @@ +package org.mage.test.cards.watchers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author BetaSteward + */ +public class EpharaGodOfThePolisTest extends CardTestPlayerBase { + /* + * Ephara, God of the Polis + * Legendary Enchantment Creature — God 6/5, 2WU (4) + * Indestructible + * As long as your devotion to white and blue is less than seven, Ephara + * isn't a creature. + * At the beginning of each upkeep, if you had another creature enter the + * battlefield under your control last turn, draw a card. + * + */ + + // test that an extra card is drawn + @Test + public void testDrawCard() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerA, "Ephara, God of the Polis"); + addCard(Zone.HAND, playerA, "Goblin Roughrider"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Goblin Roughrider"); + + setStopAt(3, PhaseStep.UPKEEP); + execute(); + + this.assertHandCount(playerA, 1); + + } + + // test that an extra card is not drawn + @Test + public void testNotDrawCard() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerA, "Ephara, God of the Polis"); + + setStopAt(3, PhaseStep.UPKEEP); + execute(); + + this.assertHandCount(playerA, 0); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/FellShepherdTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/FellShepherdTest.java new file mode 100644 index 00000000000..160f68428f0 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/FellShepherdTest.java @@ -0,0 +1,46 @@ +package org.mage.test.cards.watchers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author BetaSteward + */ +public class FellShepherdTest extends CardTestPlayerBase { + /* + * Fell Shepherd + * Creature — Avatar 8/6, 5BB (7) + * Whenever Fell Shepherd deals combat damage to a player, you may return to + * your hand all creature cards that were put into your graveyard from the battlefield this turn. + * {B}, Sacrifice another creature: Target creature gets -2/-2 until end of turn. + * + */ + + // test that creatures are returned to hand + @Test + public void testCreaturesReturn() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Craw Wurm"); + addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin"); + addCard(Zone.BATTLEFIELD, playerA, "Goblin Roughrider"); + addCard(Zone.BATTLEFIELD, playerA, "Fell Shepherd"); + + playerA.addChoice("Craw Wurm"); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{B},Sacrifice another creature: Target creature gets -2/-2 until end of turn.", "Raging Goblin"); + attack(3, playerA, "Fell Shepherd"); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + assertLife(playerB, 12); + assertPermanentCount(playerA, "Goblin Roughrider", 1); + assertPermanentCount(playerA, "Raging Goblin", 0); + assertPermanentCount(playerA, "Craw Wurm", 0); + assertHandCount(playerA, "Craw Wurm", 1); + assertHandCount(playerA, "Raging Goblin", 1); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/FuryOfTheHordeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/FuryOfTheHordeTest.java new file mode 100644 index 00000000000..99286d2cdd7 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/FuryOfTheHordeTest.java @@ -0,0 +1,78 @@ +package org.mage.test.cards.watchers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author BetaSteward + */ +public class FuryOfTheHordeTest extends CardTestPlayerBase { + /* + * Fury of the Horde + * Sorcery, 5RR (7) + * You may exile two red cards from your hand rather than pay Fury of the + * Horde's mana cost. + * Untap all creatures that attacked this turn. After this main phase, there + * is an additional combat phase followed by an additional main phase. + * + */ + + // test that creatures attack twice + @Test + public void testCreaturesAttackTwice() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 7); + addCard(Zone.BATTLEFIELD, playerA, "Craw Wurm"); + addCard(Zone.BATTLEFIELD, playerA, "Goblin Roughrider"); + addCard(Zone.HAND, playerA, "Fury of the Horde"); + + attack(3, playerA, "Craw Wurm"); + attack(3, playerA, "Goblin Roughrider"); + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Fury of the Horde"); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + this.assertLife(playerB, 2); + + } + + // test that creatures attack once + @Test + public void testCreaturesAttackOnce() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 7); + addCard(Zone.BATTLEFIELD, playerA, "Craw Wurm"); + addCard(Zone.BATTLEFIELD, playerA, "Goblin Roughrider"); + addCard(Zone.HAND, playerA, "Fury of the Horde"); + + attack(3, playerA, "Craw Wurm"); + attack(3, playerA, "Goblin Roughrider"); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + this.assertLife(playerB, 11); + + } + + // test that only creatures that attacked attack twice + @Test + public void testCreaturesThatAttacked() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 7); + addCard(Zone.BATTLEFIELD, playerA, "Craw Wurm"); + addCard(Zone.BATTLEFIELD, playerA, "Goblin Roughrider"); + addCard(Zone.HAND, playerA, "Fury of the Horde"); + + attack(3, playerA, "Craw Wurm"); + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Fury of the Horde"); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + this.assertLife(playerB, 8); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/GoblinCohortTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/GoblinCohortTest.java new file mode 100644 index 00000000000..bb26ca0f2af --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/GoblinCohortTest.java @@ -0,0 +1,53 @@ +package org.mage.test.cards.watchers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author BetaSteward + */ +public class GoblinCohortTest extends CardTestPlayerBase { + /* + * Goblin Cohort + * Creature — Goblin Warrior 2/2, R (1) + * Goblin Cohort can't attack unless you've cast a creature spell this turn. + * + */ + + // test that Goblin Cohort can attack + @Test + public void testCanAttack() { + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Cohort"); + addCard(Zone.HAND, playerB, "Goblin Roughrider"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Goblin Roughrider"); + attack(2, playerB, "Goblin Cohort"); + + setStopAt(2, PhaseStep.DECLARE_BLOCKERS); + execute(); + + assertAttacking("Goblin Cohort", true); + + } + + // test that Goblin Cohort can't attack + @Test + public void testCantAttack() { + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Cohort"); + addCard(Zone.HAND, playerB, "Goblin Roughrider"); + + attack(2, playerB, "Goblin Cohort"); + + setStopAt(2, PhaseStep.DECLARE_BLOCKERS); + execute(); + + assertAttacking("Goblin Cohort", false); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/HallOfTheBanditLordTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/HallOfTheBanditLordTest.java new file mode 100644 index 00000000000..20d74cefb82 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/HallOfTheBanditLordTest.java @@ -0,0 +1,55 @@ +package org.mage.test.cards.watchers; + +import mage.abilities.keyword.HasteAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author BetaSteward + */ +public class HallOfTheBanditLordTest extends CardTestPlayerBase { + /* + * Hall of the Bandit Lord + * Legendary Land + * Hall of the Bandit Lord enters the battlefield tapped. + * {T}, Pay 3 life: Add {1} to your mana pool. If that mana is spent on a + * creature spell, it gains haste. + * + */ + + // test that a creature cast using Hall of the Bandit Lord mana gains haste + @Test + public void testGainsHaste() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Hall of the Bandit Lord"); + addCard(Zone.HAND, playerA, "Goblin Roughrider"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Goblin Roughrider"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + this.assertAbility(playerA, "Goblin Roughrider", HasteAbility.getInstance(), true); + + } + + // test that a creature cast not using Hall of the Bandit Lord mana does not gain haste + @Test + public void testNotGainsHaste() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Hall of the Bandit Lord"); + addCard(Zone.HAND, playerA, "Ember Hauler"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ember Hauler"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + this.assertAbility(playerA, "Ember Hauler", HasteAbility.getInstance(), false); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/KaradorGhostChieftainTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/KaradorGhostChieftainTest.java new file mode 100644 index 00000000000..0d02dcfddea --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/KaradorGhostChieftainTest.java @@ -0,0 +1,74 @@ +package org.mage.test.cards.watchers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author BetaSteward + */ +public class KaradorGhostChieftainTest extends CardTestPlayerBase { + /* + * Karador, Ghost Chieftain + * Legendary Creature — Centaur Spirit 3/4, 5WBG (8) + * Karador, Ghost Chieftain costs {1} less to cast for each creature card in your graveyard. + * During each of your turns, you may cast one creature card from your graveyard. + * + */ + + // test that can play spell from graveyard + @Test + public void testPlayFromGraveyard() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerA, "Karador, Ghost Chieftain"); + addCard(Zone.GRAVEYARD, playerA, "Raging Goblin"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Raging Goblin"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + this.assertPermanentCount(playerA, "Raging Goblin", 1); + this.assertGraveyardCount(playerA, "Raging Goblin", 0); + + } + + // test that can only play one spell from graveyard + @Test + public void testPlayOneFromGraveyard() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerA, "Karador, Ghost Chieftain"); + addCard(Zone.GRAVEYARD, playerA, "Raging Goblin", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Raging Goblin"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Raging Goblin"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + this.assertPermanentCount(playerA, "Raging Goblin", 1); + this.assertGraveyardCount(playerA, "Raging Goblin", 1); + + } + + // test that can only play one spell from graveyard per turn + @Test + public void testPlayOneFromGraveyardPerTurn() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerA, "Karador, Ghost Chieftain"); + addCard(Zone.GRAVEYARD, playerA, "Raging Goblin", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Raging Goblin"); + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Raging Goblin"); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + this.assertPermanentCount(playerA, "Raging Goblin", 2); + this.assertGraveyardCount(playerA, "Raging Goblin", 0); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/OvermasterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/OvermasterTest.java new file mode 100644 index 00000000000..8cb0a04e440 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/OvermasterTest.java @@ -0,0 +1,66 @@ +package org.mage.test.cards.watchers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author BetaSteward + */ +public class OvermasterTest extends CardTestPlayerBase { + /* + * Overmaster + * Sorcery, R (1) + * The next instant or sorcery spell you cast this turn can't be countered by spells or abilities. + * Draw a card. + * + */ + + // test that next spell can't be countered + @Test + public void testCantCounter() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.HAND, playerA, "Counterspell"); + addCard(Zone.HAND, playerA, "Overmaster"); + addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Overmaster"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Raging Goblin"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Counterspell", "Lightning Bolt"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Raging Goblin", 0); + assertGraveyardCount(playerA, "Raging Goblin", 1); + } + + // test that second spell can be countered + @Test + public void testCanCounter() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.HAND, playerA, "Lightning Bolt", 2); + addCard(Zone.HAND, playerA, "Counterspell"); + addCard(Zone.HAND, playerA, "Overmaster"); + addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Overmaster"); + castSpell(1, PhaseStep.DECLARE_ATTACKERS, playerA, "Lightning Bolt", "Raging Goblin"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Raging Goblin"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Counterspell", "Lightning Bolt"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Raging Goblin", 1); + assertGraveyardCount(playerA, "Raging Goblin", 1); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/SiftThroughSandsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/SiftThroughSandsTest.java new file mode 100644 index 00000000000..13b4b6c999b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/SiftThroughSandsTest.java @@ -0,0 +1,82 @@ +package org.mage.test.cards.watchers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author BetaSteward + */ +public class SiftThroughSandsTest extends CardTestPlayerBase { + /* + * Sift Through Sands + * Instant — Arcane, 1UU (3) + * Draw two cards, then discard a card. + * If you've cast a spell named Peer Through Depths and a spell named Reach + * Through Mists this turn, you may search your library for a card named + * The Unspeakable, put it onto the battlefield, then shuffle your library. + * + * Peer Through Depths + * Instant — Arcane, 1U (2) + * Look at the top five cards of your library. You may reveal an instant or + * sorcery card from among them and put it into your hand. Put the rest on + * the bottom of your library in any order. + * + * Reach Through Mists + * Instant — Arcane, U (1) + * Draw a card. + * + * The Unspeakable + * Legendary Creature — Spirit 6/7, 6UUU (9) + * Flying, trample + * Whenever The Unspeakable deals combat damage to a player, you may return + * target Arcane card from your graveyard to your hand. + * + */ + + // test that The Unspeakable is put onto the battlefield + @Test + public void testTheUnspeakable() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 6); + addCard(Zone.HAND, playerA, "Sift Through Sands"); + addCard(Zone.HAND, playerA, "Peer Through Depths"); + addCard(Zone.HAND, playerA, "Reach Through Mists"); + addCard(Zone.LIBRARY, playerA, "The Unspeakable"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reach Through Mists"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Peer Through Depths"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Sift Through Sands"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "The Unspeakable", 1); + assertHandCount(playerA, 2); + + } + + // test that The Unspeakable is not put onto the battlefield + @Test + public void testNotTheUnspeakable() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 6); + addCard(Zone.HAND, playerA, "Sift Through Sands"); + addCard(Zone.HAND, playerA, "Peer Through Depths"); + addCard(Zone.HAND, playerA, "Reach Through Mists"); + addCard(Zone.LIBRARY, playerA, "The Unspeakable"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reach Through Mists"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Sift Through Sands"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "The Unspeakable", 0); + assertHandCount(playerA, 3); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/SpiritOfTheLabyrinthTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/SpiritOfTheLabyrinthTest.java new file mode 100644 index 00000000000..2dae2257e5e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/SpiritOfTheLabyrinthTest.java @@ -0,0 +1,37 @@ +package org.mage.test.cards.watchers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author BetaSteward + */ +public class SpiritOfTheLabyrinthTest extends CardTestPlayerBase { + /* + * Spirit of the Labyrinth + * Enchantment Creature — Spirit 3/1, 1W (2) + * Each player can't draw more than one card each turn. + * + */ + + // test that only 1 card is drawn + @Test + public void testDrawCard() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + addCard(Zone.BATTLEFIELD, playerA, "Spirit of the Labyrinth"); + addCard(Zone.HAND, playerA, "Brilliant Plan"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Brilliant Plan"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + this.assertHandCount(playerA, 1); + + } + + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/TunnelIgnusTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/TunnelIgnusTest.java new file mode 100644 index 00000000000..d80e7b1e3fd --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/TunnelIgnusTest.java @@ -0,0 +1,38 @@ +package org.mage.test.cards.watchers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author BetaSteward + */ +public class TunnelIgnusTest extends CardTestPlayerBase { + /* + * Tunnel Ignus + * Creature — Elemental 2/1, 1R (2) + * Whenever a land enters the battlefield under an opponent's control, if + * that player had another land enter the battlefield under his or her + * control this turn, Tunnel Ignus deals 3 damage to that player. + * + */ + + // test extra lands damage controller + @Test + public void testExtraLandsDamage() { + addCard(Zone.BATTLEFIELD, playerB, "Tunnel Ignus"); + addCard(Zone.HAND, playerA, "Scalding Tarn"); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scalding Tarn"); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}, Pay 1 life, Sacrifice {this}: Search your library for a Island or Mountain and put it onto the battlefield. Then shuffle your library."); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 16); + assertGraveyardCount(playerA, "Scalding Tarn", 1); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/UnscytheKillerOfKingsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/UnscytheKillerOfKingsTest.java new file mode 100644 index 00000000000..8c5a7783a44 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/UnscytheKillerOfKingsTest.java @@ -0,0 +1,66 @@ +package org.mage.test.cards.watchers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author BetaSteward + */ +public class UnscytheKillerOfKingsTest extends CardTestPlayerBase { + /* + * Unscythe, Killer of Kings + * Legendary Artifact — Equipment, UBBR (4) + * Equipped creature gets +3/+3 and has first strike. + * Whenever a creature dealt damage by equipped creature this turn dies, you + * may exile that card. If you do, put a 2/2 black Zombie creature token onto + * the battlefield. + * Equip {2} + * + */ + + // test that when creature damaged by equipped creature dies a Zombie token is created + @Test + public void testDamagedCreatureDies() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Unscythe, Killer of Kings"); + addCard(Zone.BATTLEFIELD, playerA, "Prodigal Pyromancer"); + addCard(Zone.BATTLEFIELD, playerB, "Sejiri Merfolk"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip", "Prodigal Pyromancer"); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: {source} deals 1 damage to target creature or player.", "Sejiri Merfolk"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerB, "Sejiri Merfolk", 0); + assertPermanentCount(playerA, "Zombie", 1); + assertExileCount("Sejiri Merfolk", 1); + + } + + // test that when creature damaged by equipped creature dies a Zombie token is created + @Test + public void testDamagedCreatureDiesAfterEquipped() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, "Unscythe, Killer of Kings"); + addCard(Zone.BATTLEFIELD, playerA, "Prodigal Pyromancer"); + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerB, "Craw Wurm"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals 1 damage to target creature or player.", "Craw Wurm"); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Equip", "Prodigal Pyromancer"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Craw Wurm"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerB, "Craw Wurm", 0); + assertPermanentCount(playerA, "Zombie", 1); + assertExileCount("Craw Wurm", 1); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/ZuberasTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/ZuberasTest.java new file mode 100644 index 00000000000..6561393c922 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/ZuberasTest.java @@ -0,0 +1,70 @@ +package org.mage.test.cards.watchers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author BetaSteward + */ +public class ZuberasTest extends CardTestPlayerBase { + /* + * Ashen-Skin Zubera + * Creature — Zubera Spirit 1/2, 1B (2) + * When Ashen-Skin Zubera dies, target opponent discards a card for each + * Zubera that died this turn. + + * Dripping-Tongue Zubera + * Creature — Zubera Spirit 1/2, 1G (2) + * When Dripping-Tongue Zubera dies, put a 1/1 colorless Spirit creature + * token onto the battlefield for each Zubera that died this turn. + + * Ember-Fist Zubera + * Creature — Zubera Spirit 1/2, 1R (2) + * When Ember-Fist Zubera dies, it deals damage to target creature or player + * equal to the number of Zubera that died this turn. + + * Floating-Dream Zubera + * Creature — Zubera Spirit 1/2, 1U (2) + * When Floating-Dream Zubera dies, draw a card for each Zubera that died + * this turn. + + * Silent-Chant Zubera + * Creature — Zubera Spirit 1/2, 1W (2) + * When Silent-Chant Zubera dies, you gain 2 life for each Zubera that died this turn. + */ + + // test that creatures damaged by Aggravate attack + @Test + public void testZuberas() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerA, "Ashen-Skin Zubera"); + addCard(Zone.BATTLEFIELD, playerA, "Dripping-Tongue Zubera"); + addCard(Zone.BATTLEFIELD, playerA, "Ember-Fist Zubera"); + addCard(Zone.BATTLEFIELD, playerA, "Floating-Dream Zubera"); + addCard(Zone.BATTLEFIELD, playerA, "Silent-Chant Zubera"); + addCard(Zone.HAND, playerA, "Lightning Bolt", 5); + addCard(Zone.HAND, playerB, "Island", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Silent-Chant Zubera"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Floating-Dream Zubera"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Ember-Fist Zubera"); + addTarget(playerA, playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Ashen-Skin Zubera"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Dripping-Tongue Zubera"); + + setStopAt(1, PhaseStep.DECLARE_BLOCKERS); + execute(); + + assertPermanentCount(playerA, "Spirit", 1); + assertHandCount(playerB, 1); + assertHandCount(playerA, 4); + assertLife(playerB, 17); + assertLife(playerA, 30); + + } + + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index d69b3f862ce..f336540ccf7 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -531,7 +531,7 @@ public class TestPlayer extends ComputerPlayer { boolean result = true; for (int i = 1; i < groups.length; i++) { String group = groups[i]; - if (group.startsWith("spellOnStack") || group.startsWith("spellOnTopOfStack") || group.startsWith("!spellOnStack")) { + if (group.startsWith("spellOnStack") || group.startsWith("spellOnTopOfStack") || group.startsWith("!spellOnStack") || group.startsWith("target=null") ) { break; } if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType().equals(SpellAbilityType.SPLIT_FUSED)) { 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 0477525d587..f87e3eabfde 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 @@ -548,6 +548,25 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement Assert.assertEquals("(Battlefield) Tapped state is not equal (" + cardName + ")", tapped, found.isTapped()); } + /** + * 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 + */ + public void assertAttacking(String cardName, boolean attacking) throws AssertionError { + Permanent found = null; + for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents()) { + if (permanent.getLogName().equals(cardName)) { + found = permanent; + } + } + + Assert.assertNotNull("There is no such permanent on the battlefield, cardName=" + cardName, found); + + Assert.assertEquals("(Battlefield) Attacking state is not equal (" + cardName + ")", attacking, found.isAttacking()); + } + /** * Assert card count in player's hand. * diff --git a/Mage/pom.xml b/Mage/pom.xml index 169b4be24cd..4ec038a5e17 100644 --- a/Mage/pom.xml +++ b/Mage/pom.xml @@ -42,10 +42,6 @@ org.apache.maven.plugins maven-compiler-plugin - - 1.7 - 1.7 - maven-resources-plugin diff --git a/Mage/src/mage/abilities/AbilitiesImpl.java b/Mage/src/mage/abilities/AbilitiesImpl.java index 3d15dc79c62..daca919b29a 100644 --- a/Mage/src/mage/abilities/AbilitiesImpl.java +++ b/Mage/src/mage/abilities/AbilitiesImpl.java @@ -35,7 +35,6 @@ import java.util.List; import java.util.UUID; import mage.abilities.common.ZoneChangeTriggeredAbility; import mage.abilities.costs.AlternativeCost; -import mage.abilities.costs.AlternativeSourceCosts; import mage.abilities.costs.Cost; import mage.abilities.keyword.ProtectionAbility; import mage.abilities.mana.ManaAbility; @@ -300,6 +299,16 @@ public class AbilitiesImpl extends ArrayList implements Ab return false; } + @Override + public boolean containsClass(Class classObject) { + for (T ability: this) { + if (ability.getClass().equals(classObject)) { + return true; + } + } + return false; + } + @Override public T get(UUID abilityId) { for (T ability: this) { diff --git a/Mage/src/mage/abilities/Ability.java b/Mage/src/mage/abilities/Ability.java index 85e3526ea92..5d31f50d971 100644 --- a/Mage/src/mage/abilities/Ability.java +++ b/Mage/src/mage/abilities/Ability.java @@ -50,6 +50,7 @@ import mage.game.Game; import mage.players.Player; import mage.target.Target; import mage.target.Targets; +import mage.watchers.Watcher; /** * Practically everything in the game is started from an Ability. This @@ -360,6 +361,9 @@ public interface Ability extends Controllable, Serializable { boolean canChooseTarget(Game game); + List getWatchers(); + void addWatcher(Watcher watcher); + /** * Returns true if this abilities source is in the zone for the ability * diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index c3af738254f..86778a06933 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -75,6 +75,7 @@ import mage.game.stack.StackAbility; import mage.players.Player; import mage.target.Target; import mage.target.Targets; +import mage.watchers.Watcher; import org.apache.log4j.Logger; /** @@ -84,6 +85,7 @@ import org.apache.log4j.Logger; public abstract class AbilityImpl implements Ability { private static final transient Logger logger = Logger.getLogger(AbilityImpl.class); + private static final List emptyWatchers = new ArrayList<>(); protected UUID id; protected UUID originalId; @@ -107,6 +109,7 @@ public abstract class AbilityImpl implements Ability { protected boolean activated = false; protected boolean worksFaceDown = false; protected MageObject sourceObject; + protected List watchers = null; public AbilityImpl(AbilityType abilityType, Zone zone) { this.id = UUID.randomUUID(); @@ -136,6 +139,12 @@ public abstract class AbilityImpl implements Ability { for (AlternativeCost cost: ability.alternativeCosts) { this.alternativeCosts.add((AlternativeCost)cost.copy()); } + if (ability.watchers != null) { + this.watchers = new ArrayList<>(); + for (Watcher watcher: ability.watchers) { + watchers.add(watcher.copy()); + } + } this.modes = ability.modes.copy(); this.ruleAtTheTop = ability.ruleAtTheTop; this.ruleVisible = ability.ruleVisible; @@ -548,6 +557,11 @@ public abstract class AbilityImpl implements Ability { @Override public void setControllerId(UUID controllerId) { this.controllerId = controllerId; + if (watchers != null) { + for (Watcher watcher: watchers) { + watcher.setControllerId(controllerId); + } + } } @@ -565,6 +579,11 @@ public abstract class AbilityImpl implements Ability { this.sourceId = sourceId; } } + if (watchers != null) { + for (Watcher watcher: watchers) { + watcher.setSourceId(sourceId); + } + } } @Override @@ -624,6 +643,23 @@ public abstract class AbilityImpl implements Ability { return zone; } + @Override + public List getWatchers() { + if (watchers != null) + return watchers; + else + return emptyWatchers; + } + + @Override + public void addWatcher(Watcher watcher) { + if (watchers == null) + watchers = new ArrayList<>(); + watcher.setSourceId(this.sourceId); + watcher.setControllerId(this.controllerId); + watchers.add(watcher); + } + @Override public boolean isUsesStack() { return usesStack; diff --git a/Mage/src/mage/abilities/TriggeredAbilities.java b/Mage/src/mage/abilities/TriggeredAbilities.java index dba1a450f70..bf49b4f0dec 100644 --- a/Mage/src/mage/abilities/TriggeredAbilities.java +++ b/Mage/src/mage/abilities/TriggeredAbilities.java @@ -140,12 +140,17 @@ public class TriggeredAbilities extends ConcurrentHashMap uuidList = new LinkedList<>(); - uuidList.add(sourceId); - // if the object that gained the ability moves zone, also then the triggered ability must be removed - uuidList.add(attachedTo.getId()); - sources.put(getKey(ability, attachedTo), uuidList); + if (sourceId == null) { + add(ability, attachedTo); + } + else { + this.add(ability, attachedTo); + List uuidList = new LinkedList<>(); + uuidList.add(sourceId); + // if the object that gained the ability moves zone, also then the triggered ability must be removed + uuidList.add(attachedTo.getId()); + sources.put(getKey(ability, attachedTo), uuidList); + } } public void add(TriggeredAbility ability, MageObject attachedTo) { diff --git a/Mage/src/mage/abilities/common/SimpleActivatedAbility.java b/Mage/src/mage/abilities/common/SimpleActivatedAbility.java index 9b9b93d93d4..980428b840b 100644 --- a/Mage/src/mage/abilities/common/SimpleActivatedAbility.java +++ b/Mage/src/mage/abilities/common/SimpleActivatedAbility.java @@ -52,6 +52,11 @@ public class SimpleActivatedAbility extends ActivatedAbilityImpl { public SimpleActivatedAbility(Zone zone, Effect effect, Cost cost) { super(zone, effect, cost); } + + public SimpleActivatedAbility(Zone zone, Effect effect, Cost cost, boolean usesStack) { + super(zone, effect, cost); + this.usesStack = usesStack; + } public SimpleActivatedAbility(SimpleActivatedAbility ability) { super(ability); diff --git a/Mage/src/mage/abilities/condition/common/SourceHasCounterCondition.java b/Mage/src/mage/abilities/condition/common/SourceHasCounterCondition.java index 357a638e739..42a249b4621 100644 --- a/Mage/src/mage/abilities/condition/common/SourceHasCounterCondition.java +++ b/Mage/src/mage/abilities/condition/common/SourceHasCounterCondition.java @@ -74,7 +74,7 @@ public class SourceHasCounterCondition implements Condition { if (from != -1) { //range compare int count; if (card != null) { - count = card.getCounters().getCount(counterType); + count = card.getCounters(game).getCount(counterType); } else { count = permanent.getCounters().getCount(counterType); } @@ -84,7 +84,7 @@ public class SourceHasCounterCondition implements Condition { return count >= from && count <= to; } else { // single compare (lte) if (card != null) { - return card.getCounters().getCount(counterType) >= amount; + return card.getCounters(game).getCount(counterType) >= amount; } else { return permanent.getCounters().getCount(counterType) >= amount; } diff --git a/Mage/src/mage/abilities/condition/common/SuspendedCondition.java b/Mage/src/mage/abilities/condition/common/SuspendedCondition.java index 3b2538b5843..742f30ce30e 100644 --- a/Mage/src/mage/abilities/condition/common/SuspendedCondition.java +++ b/Mage/src/mage/abilities/condition/common/SuspendedCondition.java @@ -71,7 +71,7 @@ public class SuspendedCondition implements Condition { } if (found) { if (game.getState().getZone(card.getId()) == Zone.EXILED && - card.getCounters().getCount(CounterType.TIME) > 0) { + card.getCounters(game).getCount(CounterType.TIME) > 0) { return true; } } diff --git a/Mage/src/mage/abilities/costs/mana/ManaCostImpl.java b/Mage/src/mage/abilities/costs/mana/ManaCostImpl.java index 465a60b4fdc..55407b2415c 100644 --- a/Mage/src/mage/abilities/costs/mana/ManaCostImpl.java +++ b/Mage/src/mage/abilities/costs/mana/ManaCostImpl.java @@ -39,6 +39,7 @@ import mage.filter.Filter; import mage.game.Game; import mage.players.ManaPool; import mage.players.Player; +import mage.util.ManaUtil; public abstract class ManaCostImpl extends CostImpl implements ManaCost { @@ -218,12 +219,15 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost { Player player = game.getPlayer(controllerId); assignPayment(game, ability, player.getManaPool()); while (!isPaid()) { - if (player.playMana(this, game)) { + ManaCost unpaid = this.getUnpaid(); + String promptText = ManaUtil.addSpecialManaPayAbilities(ability, game, unpaid); + if (player.playMana(unpaid, promptText, game)) { assignPayment(game, ability, player.getManaPool()); } else { return false; } + game.getState().getSpecialActions().removeManaActions(); } return true; } diff --git a/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java b/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java index 7b34a9701c8..77f84f7eae3 100644 --- a/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java +++ b/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java @@ -37,7 +37,6 @@ import mage.MageObject; import mage.Mana; import mage.abilities.Ability; import mage.abilities.costs.VariableCost; -import mage.abilities.keyword.DelveAbility; import mage.abilities.mana.ManaOptions; import mage.constants.ColoredManaSymbol; import mage.filter.Filter; @@ -45,6 +44,7 @@ import mage.game.Game; import mage.players.ManaPool; import mage.players.Player; import mage.target.Targets; +import mage.util.ManaUtil; /** * @author BetaSteward_at_googlemail.com @@ -121,8 +121,9 @@ public class ManaCostsImpl extends ArrayList implements M Player player = game.getPlayer(controllerId); assignPayment(game, ability, player.getManaPool()); while (!isPaid()) { - addSpecialManaPayAbilities(ability, game); - if (player.playMana(this.getUnpaid(), game)) { + ManaCost unpaid = this.getUnpaid(); + String promptText = ManaUtil.addSpecialManaPayAbilities(ability, game, unpaid); + if (player.playMana(unpaid, promptText, game)) { assignPayment(game, ability, player.getManaPool()); } else { return false; @@ -132,25 +133,6 @@ public class ManaCostsImpl extends ArrayList implements M return true; } - /** - * This activates the special button if there exists special ways to pay the mana (Delve, Convoke) - * - * @param ability - * @param game - */ - private void addSpecialManaPayAbilities(Ability source, Game game) { - // check for special mana payment possibilities - MageObject mageObject = source.getSourceObject(game); - if (mageObject != null) { - for (Ability ability :mageObject.getAbilities()) { - if (ability instanceof AlternateManaPaymentAbility) { - ((AlternateManaPaymentAbility) ability).addSpecialAction(source, game, getUnpaid()); - } - } - } - } - - /** * bookmarks the current state and restores it if player doesn't pay the mana cost * diff --git a/Mage/src/mage/abilities/dynamicvalue/common/ControllerGotLifeCount.java b/Mage/src/mage/abilities/dynamicvalue/common/ControllerGotLifeCount.java index 3a06c8aa580..a1df5caba3c 100644 --- a/Mage/src/mage/abilities/dynamicvalue/common/ControllerGotLifeCount.java +++ b/Mage/src/mage/abilities/dynamicvalue/common/ControllerGotLifeCount.java @@ -32,7 +32,6 @@ import mage.abilities.Ability; import mage.abilities.MageSingleton; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; -import mage.cards.Card; import mage.game.Game; import mage.watchers.common.PlayerGainedLifeWatcher; @@ -53,8 +52,7 @@ public class ControllerGotLifeCount implements DynamicValue, MageSingleton { return fINSTANCE; } - public static ControllerGotLifeCount getInstance(Card card) { - card.addWatcher(new PlayerGainedLifeWatcher()); + public static ControllerGotLifeCount getInstance() { return fINSTANCE; } diff --git a/Mage/src/mage/abilities/effects/ContinuousEffects.java b/Mage/src/mage/abilities/effects/ContinuousEffects.java index 2bbc85cf77b..2f0de6e876f 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffects.java @@ -331,6 +331,7 @@ public class ContinuousEffects implements Serializable { if(auraReplacementEffect.checksEventType(event, game) && auraReplacementEffect.applies(event, null, game)){ replaceEffects.put(auraReplacementEffect, null); } + boolean checkLKI = event.getType().equals(EventType.ZONE_CHANGE) || event.getType().equals(EventType.DESTROYED_PERMANENT); //get all applicable transient Replacement effects for (ReplacementEffect effect: replacementEffects) { if (!effect.checksEventType(event, game)) { @@ -344,7 +345,7 @@ public class ContinuousEffects implements Serializable { HashSet abilities = replacementEffects.getAbility(effect.getId()); HashSet applicableAbilities = new HashSet<>(); for (Ability ability : abilities) { - if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false)) { + if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, checkLKI)) { if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) { if (!game.getScopeRelevant() || effect.hasSelfScope() || !event.getTargetId().equals(ability.getSourceId())) { if (checkAbilityStillExists(ability, effect, event, game)) { diff --git a/Mage/src/mage/abilities/effects/common/SacrificeEffect.java b/Mage/src/mage/abilities/effects/common/SacrificeEffect.java index 3c4fcd49f0d..3b635c7b7fe 100644 --- a/Mage/src/mage/abilities/effects/common/SacrificeEffect.java +++ b/Mage/src/mage/abilities/effects/common/SacrificeEffect.java @@ -39,9 +39,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; -import mage.target.common.TargetControlledPermanent; - -import java.util.UUID; +import mage.target.TargetPermanent; import mage.util.CardUtil; /** @@ -54,19 +52,19 @@ public class SacrificeEffect extends OneShotEffect{ private String preText; private DynamicValue count; - public SacrificeEffect ( FilterPermanent filter, DynamicValue count, String preText ) { + public SacrificeEffect (FilterPermanent filter, int count, String preText ) { + this(filter, new StaticValue(count), preText); + } + + public SacrificeEffect (FilterPermanent filter, DynamicValue count, String preText ) { super(Outcome.Sacrifice); this.filter = filter; this.count = count; this.preText = preText; setText(); } - - public SacrificeEffect ( FilterPermanent filter, int count, String preText ) { - this(filter, new StaticValue(count), preText); - } - - public SacrificeEffect ( final SacrificeEffect effect ) { + + public SacrificeEffect (final SacrificeEffect effect ) { super(effect); this.filter = effect.filter; this.count = effect.count; @@ -87,14 +85,14 @@ public class SacrificeEffect extends OneShotEffect{ int realCount = game.getBattlefield().countAll(filter, player.getId(), game); amount = Math.min(amount, realCount); - Target target = new TargetControlledPermanent(amount, amount, filter, true); + Target target = new TargetPermanent(amount, amount, filter, true); // A spell or ability could have removed the only legal target this player // had, if thats the case this ability should fizzle. if (amount > 0 && target.canChoose(source.getSourceId(), player.getId(), game)) { boolean abilityApplied = false; while (!target.isChosen() && target.canChoose(player.getId(), game) && player.isInGame()) { - player.choose(Outcome.Sacrifice, target, source.getSourceId(), game); + player.chooseTarget(Outcome.Sacrifice, target, source, game); } for ( int idx = 0; idx < target.getTargets().size(); idx++) { diff --git a/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java b/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java index a99816c682b..2320b261986 100644 --- a/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java +++ b/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java @@ -36,11 +36,13 @@ import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; +import mage.constants.TargetController; import mage.filter.FilterPermanent; +import mage.filter.predicate.permanent.ControllerPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetControlledPermanent; +import mage.target.TargetPermanent; import mage.util.CardUtil; /** @@ -81,15 +83,17 @@ public class SacrificeOpponentsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - List perms = new ArrayList(); + List perms = new ArrayList<>(); + filter.add(new ControllerPredicate(TargetController.YOU)); for (UUID playerId : game.getOpponents(source.getControllerId())) { Player player = game.getPlayer(playerId); if (player != null) { + int numTargets = Math.min(amount.calculate(game, source, this), game.getBattlefield().countAll(filter, player.getId(), game)); - TargetControlledPermanent target = new TargetControlledPermanent(numTargets, numTargets, filter, false); + TargetPermanent target = new TargetPermanent(numTargets, numTargets, filter, false); if (target.canChoose(player.getId(), game)) { while (!target.isChosen() && player.isInGame()) { - player.choose(Outcome.Sacrifice, target, source.getSourceId(), game); + player.chooseTarget(Outcome.Sacrifice, target, source, game); } perms.addAll(target.getTargets()); } diff --git a/Mage/src/mage/abilities/effects/common/counter/RemoveCounterSourceEffect.java b/Mage/src/mage/abilities/effects/common/counter/RemoveCounterSourceEffect.java index 8b27a016726..da5842f460e 100644 --- a/Mage/src/mage/abilities/effects/common/counter/RemoveCounterSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/counter/RemoveCounterSourceEffect.java @@ -65,11 +65,11 @@ public class RemoveCounterSourceEffect extends OneShotEffect { return true; } Card c = game.getCard(source.getSourceId()); - if (c != null && c.getCounters().getCount(counter.getName()) >= counter.getCount()) { + if (c != null && c.getCounters(game).getCount(counter.getName()) >= counter.getCount()) { c.removeCounters(counter.getName(), counter.getCount(), game); game.informPlayers(new StringBuilder("Removed ").append(counter.getCount()).append(" ").append(counter.getName()) .append(" counter from ").append(c.getName()) - .append(" (").append(c.getCounters().getCount(counter.getName())).append(" left)").toString()); + .append(" (").append(c.getCounters(game).getCount(counter.getName())).append(" left)").toString()); return true; } return false; diff --git a/Mage/src/mage/abilities/effects/common/counter/RemoveCounterTargetEffect.java b/Mage/src/mage/abilities/effects/common/counter/RemoveCounterTargetEffect.java index d6bcef4738b..4d8d3f79686 100644 --- a/Mage/src/mage/abilities/effects/common/counter/RemoveCounterTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/counter/RemoveCounterTargetEffect.java @@ -66,11 +66,11 @@ public class RemoveCounterTargetEffect extends OneShotEffect { return true; } Card c = game.getCard(targetPointer.getFirst(game, source)); - if (c != null && c.getCounters().getCount(counter.getName()) >= counter.getCount()) { + if (c != null && c.getCounters(game).getCount(counter.getName()) >= counter.getCount()) { c.removeCounters(counter.getName(), counter.getCount(), game); game.informPlayers(new StringBuilder("Removed ").append(counter.getCount()).append(" ").append(counter.getName()) .append(" counter from ").append(c.getName()) - .append(" (").append(c.getCounters().getCount(counter.getName())).append(" left)").toString()); + .append(" (").append(c.getCounters(game).getCount(counter.getName())).append(" left)").toString()); return true; } return false; diff --git a/Mage/src/mage/abilities/effects/common/replacement/DealtDamageToCreatureBySourceDies.java b/Mage/src/mage/abilities/effects/common/replacement/DealtDamageToCreatureBySourceDies.java index e9781d1bc2c..bc7a72bdc91 100644 --- a/Mage/src/mage/abilities/effects/common/replacement/DealtDamageToCreatureBySourceDies.java +++ b/Mage/src/mage/abilities/effects/common/replacement/DealtDamageToCreatureBySourceDies.java @@ -52,7 +52,6 @@ public class DealtDamageToCreatureBySourceDies extends ReplacementEffectImpl { public DealtDamageToCreatureBySourceDies(Card card, Duration duration) { super(Duration.WhileOnBattlefield, Outcome.Exile); - card.addWatcher(new DamagedByWatcher()); if (card.getCardType().contains(CardType.CREATURE)) { staticText = "If a creature dealt damage by {this} this turn would die, exile it instead"; } else { diff --git a/Mage/src/mage/abilities/keyword/EchoAbility.java b/Mage/src/mage/abilities/keyword/EchoAbility.java index 7aef7242fe4..94215e8ae32 100644 --- a/Mage/src/mage/abilities/keyword/EchoAbility.java +++ b/Mage/src/mage/abilities/keyword/EchoAbility.java @@ -29,6 +29,7 @@ package mage.abilities.keyword; import java.util.UUID; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.TriggeredAbilityImpl; @@ -40,7 +41,6 @@ import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; @@ -120,7 +120,7 @@ public class EchoAbility extends TriggeredAbilityImpl { if (manaEcho) { sb.append(" "); } else { - sb.append("-"); + sb.append("—"); } sb.append(echoCosts.getText()); sb.append(" (At the beginning of your upkeep, if this came under your control since the beginning of your last upkeep, sacrifice it unless you pay its echo cost.)"); @@ -143,16 +143,16 @@ class EchoEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(source.getSourceId()); - if (player != null && permanent != null) { - if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() /* + " or sacrifice " + permanent.getName() */ + "?", game)) { - cost.clearPaid(); - if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { - return true; - } - } - permanent.sacrifice(source.getSourceId(), game); + Player controller = game.getPlayer(source.getControllerId()); + MageObjectReference mor = new MageObjectReference(source.getSourceId(), game); + if (controller != null && mor.refersTo(source.getSourceObject(game))) { + if (controller.chooseUse(Outcome.Benefit, "Pay " + cost.getText() /* + " or sacrifice " + permanent.getName() */ + "?", game)) { + cost.clearPaid(); + if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { + return true; + } + } + mor.getPermanent(game).sacrifice(source.getSourceId(), game); return true; } return false; diff --git a/Mage/src/mage/abilities/keyword/MiracleAbility.java b/Mage/src/mage/abilities/keyword/MiracleAbility.java index 1b601d5b548..56691ee1c7c 100644 --- a/Mage/src/mage/abilities/keyword/MiracleAbility.java +++ b/Mage/src/mage/abilities/keyword/MiracleAbility.java @@ -98,7 +98,7 @@ public class MiracleAbility extends TriggeredAbilityImpl { @SuppressWarnings("unchecked") public MiracleAbility(Card card, ManaCosts miracleCosts) { super(Zone.HAND, new MiracleEffect((ManaCosts)miracleCosts), true); - card.addWatcher(new MiracleWatcher()); + addWatcher(new MiracleWatcher()); ruleText = "Miracle " + miracleCosts.getText() + staticRule; } diff --git a/Mage/src/mage/abilities/keyword/ProwlAbility.java b/Mage/src/mage/abilities/keyword/ProwlAbility.java index 35711dcbe99..fb752f990b5 100644 --- a/Mage/src/mage/abilities/keyword/ProwlAbility.java +++ b/Mage/src/mage/abilities/keyword/ProwlAbility.java @@ -72,7 +72,7 @@ public class ProwlAbility extends StaticAbility implements AlternativeSourceCost name = PROWL_KEYWORD; setReminderText(card); this.addProwlCost(manaString); - card.addWatcher(new ProwlWatcher()); + addWatcher(new ProwlWatcher()); } diff --git a/Mage/src/mage/abilities/keyword/SuspendAbility.java b/Mage/src/mage/abilities/keyword/SuspendAbility.java index bf6a2a9dbb0..7cff82d2a2b 100644 --- a/Mage/src/mage/abilities/keyword/SuspendAbility.java +++ b/Mage/src/mage/abilities/keyword/SuspendAbility.java @@ -290,7 +290,7 @@ class SuspendPlayCardAbility extends TriggeredAbilityImpl { if (event.getTargetId().equals(getSourceId())) { Card card = game.getCard(getSourceId()); if (card != null && game.getState().getZone(card.getId()) == Zone.EXILED - && card.getCounters().getCount(CounterType.TIME) == 0) { + && card.getCounters(game).getCount(CounterType.TIME) == 0) { return true; } } diff --git a/Mage/src/mage/abilities/keyword/UndyingAbility.java b/Mage/src/mage/abilities/keyword/UndyingAbility.java index e5aad5a8e76..be16dd65a6f 100644 --- a/Mage/src/mage/abilities/keyword/UndyingAbility.java +++ b/Mage/src/mage/abilities/keyword/UndyingAbility.java @@ -23,7 +23,7 @@ public class UndyingAbility extends DiesTriggeredAbility { public UndyingAbility() { super(new UndyingEffect()); - this.addEffect(new ReturnSourceFromGraveyardToBattlefieldEffect()); + this.addEffect(new ReturnSourceFromGraveyardToBattlefieldEffect(false, true)); } public UndyingAbility(final UndyingAbility ability) { @@ -40,7 +40,6 @@ public class UndyingAbility extends DiesTriggeredAbility { if (super.checkTrigger(event, game)) { Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); if (!permanent.getCounters().containsKey(CounterType.P1P1) || permanent.getCounters().getCount(CounterType.P1P1) == 0) { - Logger.getLogger(UndyingAbility.class).info("Undying trigger: " + getSourceId()); game.getState().setValue("undying" + getSourceId(),permanent.getId()); return true; } @@ -115,8 +114,6 @@ class UndyingReplacementEffect extends ReplacementEffectImpl { if (event.getTargetId().equals(source.getSourceId())) { // Check if undying condition is true UUID targetId = (UUID) game.getState().getValue("undying" + source.getSourceId()); - Logger.getLogger(UndyingReplacementEffect.class).info("Undying replacement applies: " + targetId + " eventSourceId " + event.getTargetId()); - if (targetId != null && targetId.equals(source.getSourceId())) { return true; } diff --git a/Mage/src/mage/cards/Card.java b/Mage/src/mage/cards/Card.java index 25f7edc7018..aa53894b2e2 100644 --- a/Mage/src/mage/cards/Card.java +++ b/Mage/src/mage/cards/Card.java @@ -49,11 +49,9 @@ public interface Card extends MageObject { Rarity getRarity(); void setOwnerId(UUID ownerId); void addAbility(Ability ability); - void addWatcher(Watcher watcher); void setSpellAbility(SpellAbility ability); SpellAbility getSpellAbility(); List getRules(); - List getWatchers(); String getExpansionSetCode(); String getTokenSetCode(); void setFaceDown(boolean value); @@ -119,7 +117,7 @@ public interface Card extends MageObject { * @return true if there exists various art images for this card */ boolean getUsesVariousArt(); - Counters getCounters(); + Counters getCounters(Game game); void addCounters(String name, int amount, Game game); void addCounters(String name, int amount, Game game, ArrayList appliedEffects); diff --git a/Mage/src/mage/cards/CardImpl.java b/Mage/src/mage/cards/CardImpl.java index 6774095dc5e..505ffe95035 100644 --- a/Mage/src/mage/cards/CardImpl.java +++ b/Mage/src/mage/cards/CardImpl.java @@ -76,7 +76,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card { protected UUID ownerId; protected int cardNumber; - protected List watchers = new ArrayList<>(); protected String expansionSetCode; protected String tokenSetCode; protected Rarity rarity; @@ -90,7 +89,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card { protected int zoneChangeCounter = 1; protected Map info; protected boolean usesVariousArt = false; - protected Counters counters; protected boolean splitCard; protected boolean morphCard; @@ -119,7 +117,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card { abilities.add(ability); } this.usesVariousArt = Character.isDigit(this.getClass().getName().charAt(this.getClass().getName().length()-1)); - this.counters = new Counters(); this.morphCard = false; } @@ -134,14 +131,12 @@ public abstract class CardImpl extends MageObjectImpl implements Card { protected CardImpl(UUID ownerId, String name) { this.ownerId = ownerId; this.name = name; - this.counters = new Counters(); } protected CardImpl(UUID id, UUID ownerId, String name) { super(id); this.ownerId = ownerId; this.name = name; - this.counters = new Counters(); } public CardImpl(final CardImpl card) { @@ -150,10 +145,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card { cardNumber = card.cardNumber; expansionSetCode = card.expansionSetCode; rarity = card.rarity; - this.watchers.clear(); - for (Watcher watcher: (List)card.getWatchers()) { - watchers.add(watcher.copy()); - } faceDown = card.faceDown; canTransform = card.canTransform; @@ -170,7 +161,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card { flipCardName = card.flipCardName; splitCard = card.splitCard; usesVariousArt = card.usesVariousArt; - counters = card.counters.copy(); morphCard = card.isMorphCard(); } @@ -241,14 +231,12 @@ public abstract class CardImpl extends MageObjectImpl implements Card { ability.setSourceId(this.getId()); abilities.add(ability); } - - @Override - public void addWatcher(Watcher watcher) { - watcher.setSourceId(this.getId()); - watcher.setControllerId(this.ownerId); - watchers.add(watcher); - } + protected void addAbility(Ability ability, Watcher watcher) { + addAbility(ability); + ability.addWatcher(watcher); + } + @Override public SpellAbility getSpellAbility() { if (spellAbility == null) { @@ -268,11 +256,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card { abilities.setControllerId(ownerId); } - @Override - public List getWatchers() { - return watchers; - } - @Override public String getExpansionSetCode() { return expansionSetCode; @@ -664,8 +647,8 @@ public abstract class CardImpl extends MageObjectImpl implements Card { } @Override - public Counters getCounters() { - return counters; + public Counters getCounters(Game game) { + return game.getState().getCardState(this.objectId).getCounters(); } @Override @@ -682,7 +665,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { GameEvent event = GameEvent.getEvent(GameEvent.EventType.ADD_COUNTER, objectId, ownerId, name, 1); event.setAppliedEffects(appliedEffects); if (!game.replaceEvent(event)) { - counters.addCounter(name, 1); + game.getState().getCardState(this.objectId).getCounters().addCounter(name, 1); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER_ADDED, objectId, ownerId, name, 1)); } } @@ -706,7 +689,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { GameEvent event = GameEvent.getEvent(GameEvent.EventType.ADD_COUNTER, objectId, ownerId, counter.getName(), 1); event.setAppliedEffects(appliedEffects); if (!game.replaceEvent(event)) { - counters.addCounter(eventCounter); + game.getState().getCardState(this.objectId).getCounters().addCounter(eventCounter); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER_ADDED, objectId, ownerId, counter.getName(), 1)); } } @@ -716,7 +699,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { @Override public void removeCounters(String name, int amount, Game game) { for (int i = 0; i < amount; i++) { - counters.removeCounter(name, 1); + game.getState().getCardState(this.objectId).getCounters().removeCounter(name, 1); GameEvent event = GameEvent.getEvent(GameEvent.EventType.COUNTER_REMOVED, objectId, ownerId); event.setData(name); game.fireEvent(event); diff --git a/Mage/src/mage/cards/SplitCard.java b/Mage/src/mage/cards/SplitCard.java index 204812c3fec..1a5fb845fcd 100644 --- a/Mage/src/mage/cards/SplitCard.java +++ b/Mage/src/mage/cards/SplitCard.java @@ -42,7 +42,6 @@ import static mage.constants.SpellAbilityType.SPLIT_LEFT; import static mage.constants.SpellAbilityType.SPLIT_RIGHT; import mage.constants.Zone; import mage.game.Game; -import mage.watchers.Watcher; /** * @@ -166,14 +165,6 @@ public abstract class SplitCard extends CardImpl { } - @Override - public List getWatchers() { - List allWatchers = new ArrayList<>(); - allWatchers.addAll(super.getWatchers()); - allWatchers.addAll(leftHalfCard.getWatchers()); - allWatchers.addAll(rightHalfCard.getWatchers()); - return allWatchers; - } } /* diff --git a/Mage/src/mage/cards/repository/CardRepository.java b/Mage/src/mage/cards/repository/CardRepository.java index 2d341c4cdc8..ea44f795a11 100644 --- a/Mage/src/mage/cards/repository/CardRepository.java +++ b/Mage/src/mage/cards/repository/CardRepository.java @@ -60,7 +60,7 @@ public enum CardRepository { // raise this if db structure was changed private static final long CARD_DB_VERSION = 36; // raise this if new cards were added to the server - private static final long CARD_CONTENT_VERSION = 4; + private static final long CARD_CONTENT_VERSION = 5; private final Random random = new Random(); private Dao cardDao; diff --git a/Mage/src/mage/filter/common/FilterControlledPlaneswalkerPermanent.java b/Mage/src/mage/filter/common/FilterControlledPlaneswalkerPermanent.java new file mode 100644 index 00000000000..9853e6e8664 --- /dev/null +++ b/Mage/src/mage/filter/common/FilterControlledPlaneswalkerPermanent.java @@ -0,0 +1,58 @@ +/* + * 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.filter.common; + +import mage.constants.CardType; +import mage.filter.predicate.mageobject.CardTypePredicate; + +/** + * + * @author LevelX2 + */ + +public class FilterControlledPlaneswalkerPermanent extends FilterControlledPermanent { + + public FilterControlledPlaneswalkerPermanent() { + this("planeswalker you control"); + } + + public FilterControlledPlaneswalkerPermanent(String name) { + super(name); + this.add(new CardTypePredicate(CardType.PLANESWALKER)); + } + + public FilterControlledPlaneswalkerPermanent(final FilterControlledPlaneswalkerPermanent filter) { + super(filter); + } + + @Override + public FilterControlledPlaneswalkerPermanent copy() { + return new FilterControlledPlaneswalkerPermanent(this); + } + +} diff --git a/Mage/src/mage/filter/common/FilterPlaneswalkerOrPlayer.java b/Mage/src/mage/filter/common/FilterPlaneswalkerOrPlayer.java index 652fc368097..886db298e67 100644 --- a/Mage/src/mage/filter/common/FilterPlaneswalkerOrPlayer.java +++ b/Mage/src/mage/filter/common/FilterPlaneswalkerOrPlayer.java @@ -52,14 +52,14 @@ public class FilterPlaneswalkerOrPlayer extends FilterImpl { public FilterPlaneswalkerOrPlayer(Set defenders) { super("planeswalker or player"); - ArrayList> permanentPredicates = new ArrayList>(); + ArrayList> permanentPredicates = new ArrayList<>(); for (UUID defenderId : defenders) { permanentPredicates.add(new ControllerIdPredicate(defenderId)); } planeswalkerFilter = new FilterPlaneswalkerPermanent(); planeswalkerFilter.add(Predicates.or(permanentPredicates)); - ArrayList> playerPredicates = new ArrayList>(); + ArrayList> playerPredicates = new ArrayList<>(); for (UUID defenderId : defenders) { playerPredicates.add(new PlayerIdPredicate(defenderId)); } diff --git a/Mage/src/mage/filter/predicate/permanent/CounterPredicate.java b/Mage/src/mage/filter/predicate/permanent/CounterPredicate.java index 4c8852246fa..320ad68eca5 100644 --- a/Mage/src/mage/filter/predicate/permanent/CounterPredicate.java +++ b/Mage/src/mage/filter/predicate/permanent/CounterPredicate.java @@ -46,7 +46,7 @@ public class CounterPredicate implements Predicate { @Override public boolean apply(Card input, Game game) { - return input.getCounters().containsKey(counter); + return input.getCounters(game).containsKey(counter); } @Override diff --git a/Mage/src/mage/game/CardState.java b/Mage/src/mage/game/CardState.java new file mode 100644 index 00000000000..3311c5e5193 --- /dev/null +++ b/Mage/src/mage/game/CardState.java @@ -0,0 +1,64 @@ +package mage.game; + +import java.util.HashMap; +import java.util.Map; +import mage.abilities.Abilities; +import mage.abilities.AbilitiesImpl; +import mage.abilities.Ability; +import mage.counters.Counters; +/** + * + * @author BetaSteward + */ +public class CardState { + + protected Map info; + protected Counters counters; + + private static final Map emptyInfo = new HashMap<>(); + + public CardState() { + counters = new Counters(); + } + + public CardState(final CardState state) { + if (state.info != null) { + info = new HashMap<>(); + info.putAll(state.info); + } + counters = state.counters.copy(); + } + + public CardState copy() { + return new CardState(this); + } + + public Counters getCounters() { + return counters; + } + + public void addInfo(String key, String value) { + if (info == null) { + info = new HashMap<>(); + } + if (value == null || value.isEmpty()) { + info.remove(key); + } else { + info.put(key, value); + } + } + + public Map getInfo() { + if (info == null) { + return emptyInfo; + } + return info; + } + + + public void clear() { + counters.clear(); + info = null; + } + +} diff --git a/Mage/src/mage/game/GameState.java b/Mage/src/mage/game/GameState.java index 7a7e4f8abc3..c91ce9894ee 100644 --- a/Mage/src/mage/game/GameState.java +++ b/Mage/src/mage/game/GameState.java @@ -105,6 +105,7 @@ public class GameState implements Serializable, Copyable { private Map values = new HashMap<>(); private Map zones = new HashMap<>(); private List simultaneousEvents = new ArrayList<>(); + private Map cardState = new HashMap<>(); public GameState() { players = new Players(); @@ -160,6 +161,9 @@ public class GameState implements Serializable, Copyable { } this.paused = state.paused; this.simultaneousEvents.addAll(state.simultaneousEvents); + for (Map.Entry entry: state.cardState.entrySet()) { + cardState.put(entry.getKey(), entry.getValue().copy()); + } } @Override @@ -420,7 +424,10 @@ public class GameState implements Serializable, Copyable { } public void addEffect(ContinuousEffect effect, UUID sourceId, Ability source) { - effects.addEffect(effect, sourceId, source); + if (sourceId == null) + effects.addEffect(effect, source); + else + effects.addEffect(effect, sourceId, source); } // public void addMessage(String message) { @@ -491,6 +498,7 @@ public class GameState implements Serializable, Copyable { origPlayer.restore(copyPlayer); } this.simultaneousEvents = state.simultaneousEvents; + this.cardState = state.cardState; } public void addSimultaneousEvent(GameEvent event, Game game) { @@ -527,11 +535,6 @@ public class GameState implements Serializable, Copyable { public void addCard(Card card) { setZone(card.getId(), Zone.OUTSIDE); - for (Watcher watcher: card.getWatchers()) { - watcher.setControllerId(card.getOwnerId()); - watcher.setSourceId(card.getId()); - watchers.add(watcher); - } for (Ability ability: card.getAbilities()) { addAbility(ability, card); } @@ -553,6 +556,10 @@ public class GameState implements Serializable, Copyable { * @param ability * @param attachedTo */ + public void addAbility(Ability ability, Card attachedTo) { + addAbility(ability, null, attachedTo); + } + public void addAbility(Ability ability, MageObject attachedTo) { if (ability instanceof StaticAbility) { for (Mode mode: ability.getModes().values()) { @@ -574,7 +581,7 @@ public class GameState implements Serializable, Copyable { * @param sourceId * @param attachedTo */ - public void addAbility(Ability ability, UUID sourceId, MageObject attachedTo) { + public void addAbility(Ability ability, UUID sourceId, Card attachedTo) { if (ability instanceof StaticAbility) { for (Mode mode: ability.getModes().values()) { for (Effect effect: mode.getEffects()) { @@ -588,6 +595,11 @@ public class GameState implements Serializable, Copyable { // TODO: add sources for triggers - the same way as in addEffect: sources this.triggers.add((TriggeredAbility)ability, sourceId, attachedTo); } + for (Watcher watcher: ability.getWatchers()) { + watcher.setControllerId(attachedTo.getOwnerId()); + watcher.setSourceId(attachedTo.getId()); + watchers.add(watcher); + } } public void addCommandObject(CommandObject commandObject) { @@ -732,6 +744,7 @@ public class GameState implements Serializable, Copyable { gameOver = false; specialActions.clear(); otherAbilities.clear(); + cardState.clear(); combat.clear(); turnMods.clear(); watchers.clear(); @@ -767,4 +780,16 @@ public class GameState implements Serializable, Copyable { public TriggeredAbilities getTriggers() { return triggers; } + + public CardState getCardState(UUID cardId) { + if (!cardState.containsKey(cardId)) { + cardState.putIfAbsent(cardId, new CardState()); + } + return cardState.get(cardId); + } + + public void addWatcher(Watcher watcher) { + this.watchers.add(watcher); + } + } diff --git a/Mage/src/mage/game/GameTinyLeadersImpl.java b/Mage/src/mage/game/GameTinyLeadersImpl.java new file mode 100644 index 00000000000..1094ef3cf76 --- /dev/null +++ b/Mage/src/mage/game/GameTinyLeadersImpl.java @@ -0,0 +1,116 @@ +/* + * 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.game; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.EmptyEffect; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continious.CommanderManaReplacementEffect; +import mage.abilities.effects.common.continious.CommanderReplacementEffect; +import mage.abilities.effects.common.cost.CommanderCostModification; +import mage.cards.Card; +import mage.constants.MultiplayerAttackOption; +import mage.constants.PhaseStep; +import mage.constants.RangeOfInfluence; +import mage.constants.Zone; +import mage.game.turn.TurnMod; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * + * @author JRHerlehy + */ +public abstract class GameTinyLeadersImpl extends GameImpl{ + + protected boolean alsoLibrary; // replace also commander going to library + protected boolean startingPlayerSkipsDraw = true; + + public GameTinyLeadersImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, int freeMulligans, int startLife) { + super(attackOption, range, freeMulligans, startLife); + } + + public GameTinyLeadersImpl(final GameTinyLeadersImpl game) { + super(game); + this.alsoLibrary = game.alsoLibrary; + this.startingPlayerSkipsDraw = game.startingPlayerSkipsDraw; + } + + @Override + protected void init(UUID choosingPlayerId, GameOptions gameOptions) { + Ability ability = new SimpleStaticAbility(Zone.COMMAND, new EmptyEffect("Commander effects")); + //Move tiny leader to command zone + for (UUID playerId: state.getPlayerList(startingPlayerId)) { + Player player = getPlayer(playerId); + if (player != null){ + if (player.getSideboard().size() > 0){ + Card commander = getCard((UUID)player.getSideboard().toArray()[0]); + if (commander != null) { + player.setCommanderId(commander.getId()); + commander.moveToZone(Zone.COMMAND, null, this, true); + ability.addEffect(new CommanderReplacementEffect(commander.getId(), alsoLibrary)); + ability.addEffect(new CommanderCostModification(commander.getId())); + ability.addEffect(new CommanderManaReplacementEffect(player.getId(), CardUtil.getColorIdentity(commander))); + getState().setValue(commander.getId() + "_castCount", 0); + } + } + } + + } + this.getState().addAbility(ability, null); + super.init(choosingPlayerId, gameOptions); + if (startingPlayerSkipsDraw) { + state.getTurnMods().add(new TurnMod(startingPlayerId, PhaseStep.DRAW)); + } + } + + @Override + public Set getOpponents(UUID playerId) { + Set opponents = new HashSet<>(); + for (UUID opponentId: this.getPlayer(playerId).getInRange()) { + if (!opponentId.equals(playerId)) { + opponents.add(opponentId); + } + } + return opponents; + } + + @Override + public boolean isOpponent(Player player, UUID playerToCheck) { + return !player.getId().equals(playerToCheck); + } + + public void setAlsoLibrary(boolean alsoLibrary) { + this.alsoLibrary = alsoLibrary; + } + +} diff --git a/Mage/src/mage/game/permanent/Permanent.java b/Mage/src/mage/game/permanent/Permanent.java index fe471e7d5cd..4b17b88e216 100644 --- a/Mage/src/mage/game/permanent/Permanent.java +++ b/Mage/src/mage/game/permanent/Permanent.java @@ -36,6 +36,7 @@ import mage.abilities.Ability; import mage.cards.Card; import mage.constants.Rarity; import mage.constants.Zone; +import mage.counters.Counters; import mage.game.Controllable; import mage.game.Game; @@ -79,6 +80,8 @@ public interface Permanent extends Card, Controllable { void setFlipCardName(String flipCardName); void setSecondCardFace(Card card); + Counters getCounters(); + List getAttachments(); UUID getAttachedTo(); void attachTo(UUID permanentId, Game game); diff --git a/Mage/src/mage/game/permanent/PermanentCard.java b/Mage/src/mage/game/permanent/PermanentCard.java index 82ec0802165..3db80516186 100644 --- a/Mage/src/mage/game/permanent/PermanentCard.java +++ b/Mage/src/mage/game/permanent/PermanentCard.java @@ -90,8 +90,6 @@ public class PermanentCard extends PermanentImpl { this.abilities.clear(); this.abilities.addAll(card.getAbilities().copy()); this.abilities.setControllerId(this.controllerId); - this.watchers.clear(); - this.watchers.addAll(card.getWatchers()); this.cardType.clear(); this.cardType.addAll(card.getCardType()); this.color = card.getColor().copy(); diff --git a/Mage/src/mage/game/permanent/PermanentImpl.java b/Mage/src/mage/game/permanent/PermanentImpl.java index faa2d2a1437..983d4411a07 100644 --- a/Mage/src/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/mage/game/permanent/PermanentImpl.java @@ -61,6 +61,7 @@ import mage.constants.Rarity; import mage.constants.Zone; import mage.counters.Counter; import mage.counters.CounterType; +import mage.counters.Counters; import mage.game.Game; import mage.game.events.DamageCreatureEvent; import mage.game.events.DamagePlaneswalkerEvent; @@ -104,6 +105,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { protected List dealtDamageByThisTurn; protected UUID attachedTo; protected UUID pairedCard; + protected Counters counters; protected List markedDamage; protected int timesLoyaltyUsed = 0; @@ -113,12 +115,14 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { super(ownerId, name); this.originalControllerId = controllerId; this.controllerId = controllerId; + this.counters = new Counters(); } public PermanentImpl(UUID id, UUID ownerId, UUID controllerId, String name) { super(id, ownerId, name); this.originalControllerId = controllerId; this.controllerId = controllerId; + this.counters = new Counters(); } public PermanentImpl(final PermanentImpl permanent) { @@ -148,6 +152,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { } } } + this.counters = permanent.counters.copy(); this.attachedTo = permanent.attachedTo; this.minBlockedBy = permanent.minBlockedBy; this.maxBlockedBy = permanent.maxBlockedBy; @@ -251,6 +256,16 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { getAbilities().clear(); } + @Override + public Counters getCounters() { + return counters; + } + + @Override + public void addCounters(String name, int amount, Game game) { + addCounters(name, amount, game, null); + } + @Override public void addCounters(String name, int amount, Game game, ArrayList appliedEffects) { GameEvent countersEvent = GameEvent.getEvent(GameEvent.EventType.ADD_COUNTERS, objectId, controllerId, name, amount); @@ -295,6 +310,12 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { game.fireEvent(event); } } + + @Override + public void removeCounters(Counter counter, Game game) { + removeCounters(counter.getName(), counter.getCount(), game); + } + @Override public int getTurnsOnBattlefield() { return turnsOnBattlefield; diff --git a/Mage/src/mage/game/stack/Spell.java b/Mage/src/mage/game/stack/Spell.java index bed5d9ac863..81376e0a3ed 100644 --- a/Mage/src/mage/game/stack/Spell.java +++ b/Mage/src/mage/game/stack/Spell.java @@ -677,9 +677,6 @@ public class Spell implements StackObject, Card { @Override public void addAbility(Ability ability) {} - @Override - public void addWatcher(Watcher watcher) {} - @Override public SpellAbility getSpellAbility() { return ability; @@ -701,11 +698,6 @@ public class Spell implements StackObject, Card { return card.getRules(); } - @Override - public List getWatchers() { - return card.getWatchers(); - } - @Override public String getExpansionSetCode() { return card.getExpansionSetCode(); @@ -935,8 +927,8 @@ public class Spell implements StackObject, Card { public void build() {} @Override - public Counters getCounters() { - return card.getCounters(); + public Counters getCounters(Game game) { + return card.getCounters(game); } @Override diff --git a/Mage/src/mage/game/stack/StackAbility.java b/Mage/src/mage/game/stack/StackAbility.java index 2e65c538cca..3eb4254f2ed 100644 --- a/Mage/src/mage/game/stack/StackAbility.java +++ b/Mage/src/mage/game/stack/StackAbility.java @@ -57,6 +57,7 @@ import java.util.UUID; import mage.cards.Card; import mage.constants.AbilityWord; import mage.players.Player; +import mage.watchers.Watcher; /** * @@ -475,6 +476,16 @@ public class StackAbility implements StackObject, Ability { public void setWorksFaceDown(boolean worksFaceDown) { this.ability.setWorksFaceDown(worksFaceDown); } + + @Override + public List getWatchers() { + return this.ability.getWatchers(); + } + + @Override + public void addWatcher(Watcher watcher) { + throw new UnsupportedOperationException("Not supported."); + } @Override public MageObject getSourceObject(Game game) { diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index b22e86a7fd8..a9d847a8030 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -299,7 +299,7 @@ public interface Player extends MageItem, Copyable { boolean chooseUse(Outcome outcome, String message, Game game); boolean choose(Outcome outcome, Choice choice, Game game); boolean choosePile(Outcome outcome, String message, List pile1, List pile2, Game game); - boolean playMana(ManaCost unpaid, Game game); + boolean playMana(ManaCost unpaid, String promptText, Game game); /** * Moves the cards from cards to the bottom of the players library. diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 78103f71a5a..f5ce14b6e0f 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -1628,6 +1628,9 @@ public abstract class PlayerImpl implements Player, Serializable { MageObject source = game.getPermanentOrLKIBattlefield(sourceId); if (source == null) { source = game.getObject(sourceId); + if (source instanceof Card && !CardUtil.isPermanentCard((Card)source)) { + source = game.getLastKnownInformation(sourceId, Zone.STACK); + } if (source instanceof Spell) { sourceControllerId = ((Spell) source).getControllerId(); } else { diff --git a/Mage/src/mage/target/common/TargetCardInLibrary.java b/Mage/src/mage/target/common/TargetCardInLibrary.java index 1f9793e214a..69221dc3d8e 100644 --- a/Mage/src/mage/target/common/TargetCardInLibrary.java +++ b/Mage/src/mage/target/common/TargetCardInLibrary.java @@ -92,7 +92,7 @@ public class TargetCardInLibrary extends TargetCard { Collections.sort(cards, new CardNameComparator()); while (!isChosen() && !doneChosing()) { chosen = targets.size() >= minNumberOfTargets; - if (!player.choose(outcome, new CardsImpl(Zone.LIBRARY, cards), this, game)) { + if (!player.chooseTarget(outcome, new CardsImpl(Zone.LIBRARY, cards), this, null, game)) { return chosen; } chosen = targets.size() >= minNumberOfTargets; diff --git a/Mage/src/mage/target/common/TargetControlledPermanent.java b/Mage/src/mage/target/common/TargetControlledPermanent.java index e1eaccfa2da..24f0ddd7253 100644 --- a/Mage/src/mage/target/common/TargetControlledPermanent.java +++ b/Mage/src/mage/target/common/TargetControlledPermanent.java @@ -50,7 +50,7 @@ public class TargetControlledPermanent extends TargetPermanent { this(1, 1, filter, false); } - public TargetControlledPermanent(int minNumTargets, int maxNumTargets, FilterPermanent filter, boolean notTarget) { + public TargetControlledPermanent(int minNumTargets, int maxNumTargets, FilterControlledPermanent filter, boolean notTarget) { super(minNumTargets, maxNumTargets, filter, notTarget); this.targetName = filter.getMessage(); } diff --git a/Mage/src/mage/util/ManaUtil.java b/Mage/src/mage/util/ManaUtil.java index 160a483d450..080efef553f 100644 --- a/Mage/src/mage/util/ManaUtil.java +++ b/Mage/src/mage/util/ManaUtil.java @@ -10,6 +10,10 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Set; import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.costs.mana.AlternateManaPaymentAbility; +import mage.game.Game; /** * @author noxx @@ -334,4 +338,28 @@ public class ManaUtil { return useableAbilities; } + + /** + * This activates the special button inthe feedback panel of the client + * if there exists special ways to pay the mana (e.g. Delve, Convoke) + * + * @param source ability the mana costs have to be paid for + * @param game + * @param unpaid mana that has still to be paid + * @return message to be shown in human players feedback area + */ + public static String addSpecialManaPayAbilities(Ability source, Game game, ManaCost unpaid) { + // check for special mana payment possibilities + MageObject mageObject = source.getSourceObject(game); + if (mageObject != null) { + for (Ability ability :mageObject.getAbilities()) { + if (ability instanceof AlternateManaPaymentAbility) { + ((AlternateManaPaymentAbility) ability).addSpecialAction(source, game, unpaid); + } + } + return unpaid.getText() + "
" + mageObject.getLogName() + "
"; + } else { + return unpaid.getText(); + } + } } diff --git a/Mage/src/mage/watchers/common/MiracleWatcher.java b/Mage/src/mage/watchers/common/MiracleWatcher.java index 7c184049941..c2fd6a518e9 100644 --- a/Mage/src/mage/watchers/common/MiracleWatcher.java +++ b/Mage/src/mage/watchers/common/MiracleWatcher.java @@ -99,7 +99,7 @@ public class MiracleWatcher extends Watcher { Cards cards = new CardsImpl(); cards.add(card); controller.lookAtCards("Miracle", cards, game); - if (controller.chooseUse(Outcome.Benefit, "Reveal card to be able to use Miracle?", game)) { + if (controller.chooseUse(Outcome.Benefit, "Reveal " + card.getName() + " to be able to use Miracle?", game)) { controller.revealCards("Miracle", cards, game); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.MIRACLE_CARD_REVEALED, card.getId(), card.getId(),controller.getId())); break; diff --git a/Utils/known-sets.txt b/Utils/known-sets.txt index 569564d279d..062538f80d2 100644 --- a/Utils/known-sets.txt +++ b/Utils/known-sets.txt @@ -16,6 +16,7 @@ Conflux|conflux| Dark Ascension|darkascension| Darksteel|darksteel| Dissension|dissension| +Dragons of Tarkir[dragonsoftarkir] Dragon's Maze|dragonsmaze| Duel Decks: Ajani vs. Nicol Bolas|ajanivsnicolbolas| Duel Decks: Divine vs. Demonic|divinevsdemonic| diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index d435cde86b4..d22fe52e478 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -25570,4 +25570,5 @@ Curse of the Bloody Tome|WPN Gateway|80|Special|{2}{U}|Enchantment — Aura Curs Curse of Thirst|WPN Gateway|81|Special|{4}{B}|Enchantment — Aura Curse|||Enchant player$At the beginning of enchanted player's upkeep, Curse of Thirst deals damage to that player equal to the number of Curses attached to him or her.| Nearheath Stalker|WPN Gateway|82|Special|{4}{R}|Creature — Vampire Rogue|4|1|Undying (When this creature dies, if it had no +1/+1 counters on it, return it to the battlefield under its owner's control with a +1/+1 counter on it.)| Bloodcrazed Neonate|WPN Gateway|83|Special|{1}{R}|Creature — Vampire|2|1|Bloodcrazed Neonate attacks each turn if able.$Whenever Bloodcrazed Neonate deals combat damage to a player, put a +1/+1 counter on it.| -Boneyard Wurm|WPN Gateway|84|Special|{1}{G}|Creature — Wurm|*|*|Boneyard Wurm's power and toughness are each equal to the number of creature cards in your graveyard.| \ No newline at end of file +Boneyard Wurm|WPN Gateway|84|Special|{1}{G}|Creature — Wurm|*|*|Boneyard Wurm's power and toughness are each equal to the number of creature cards in your graveyard.| +Dragonlord's Reaper|Dragons of Tarkir|96|Rare|{5}{B}{B}|Creature - Dragon|5|6|Flying$When Dragonlord's Reaper enters the battlefield, if you cast it from your hand and there are five or more other creatures on the battlefield, destroy all other creatures.| \ No newline at end of file diff --git a/Utils/mtg-sets-data.txt b/Utils/mtg-sets-data.txt index b89667a0606..f5d3b826e01 100644 --- a/Utils/mtg-sets-data.txt +++ b/Utils/mtg-sets-data.txt @@ -31,6 +31,7 @@ Dissension|DIS| Deckmasters|DKM| Dragon's Maze|DGM| The Dark|DRK| +Dragons of Tarkir[DTK] Duel Decks: Ajani vs. Nicol Bolas|DDH| Duel Decks: Elves vs. Goblins|EVG| Duel Decks: Divine vs. Demonic|DDC| diff --git a/pom.xml b/pom.xml index f024001d95b..bc9933a845f 100644 --- a/pom.xml +++ b/pom.xml @@ -50,17 +50,16 @@ - Mage Mage.Common - Mage.Server - Mage.Sets Mage.Client Mage.Plugins + Mage + Mage.Server + Mage.Sets Mage.Server.Plugins Mage.Server.Console Mage.Tests Mage.Updater - Mage.Stats