diff --git a/Mage.Client/serverlist.txt b/Mage.Client/serverlist.txt index 596b67fadce..074dc0be88e 100644 --- a/Mage.Client/serverlist.txt +++ b/Mage.Client/serverlist.txt @@ -1,5 +1,5 @@ XMage.de 1 (Europe/Germany) fast :xmage.de:17171 -woogerworks (North America/USA) :xmage.woogerworks.info:17171 +woogerworks (North America/USA) :xmage.woogerworks.com:17171 XMage Testserver (Europe/France) 1.4.8v0 :176.31.186.181:17171 XMage BR (South America/Brazil) :ec2-54-233-67-0.sa-east-1.compute.amazonaws.com:17171 XMage.tahiti :xmage.tahiti.one:443 diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index c36056dace8..c9d69ca85f0 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -43,6 +43,7 @@ import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; +import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; @@ -61,6 +62,7 @@ import javax.imageio.ImageIO; import javax.swing.AbstractButton; import javax.swing.Box; import javax.swing.ImageIcon; +import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; @@ -75,6 +77,7 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JToggleButton; import javax.swing.JToolBar.Separator; +import javax.swing.KeyStroke; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.UIManager; @@ -116,6 +119,7 @@ import mage.client.table.TablesPane; import mage.client.tournament.TournamentPane; import mage.client.util.EDTExceptionHandler; import mage.client.util.GUISizeHelper; +import mage.client.util.ImageCaches; import mage.client.util.SettingsManager; import mage.client.util.SystemUtil; import mage.client.util.audio.MusicPlayer; @@ -242,6 +246,18 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { try { UIManager.put("desktop", new Color(0, 0, 0, 0)); UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"); + // stop JSplitPane from eating F6 and F8 or any other function keys + { + Object value = UIManager.get("SplitPane.ancestorInputMap"); + + if(value instanceof InputMap) { + InputMap map = (InputMap)value; + for(int vk = KeyEvent.VK_F2; vk <= KeyEvent.VK_F12; ++vk) { + map.remove(KeyStroke.getKeyStroke(vk, 0)); + } + } + } + GUISizeHelper.calculateGUISizes(); // UIManager.put("Table.rowHeight", GUISizeHelper.tableRowHeight); } catch (Exception ex) { @@ -1455,6 +1471,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { } public void changeGUISize() { + ImageCaches.flush(); setGUISize(); Plugins.getInstance().changeGUISize(); CountryUtil.changeGUISize(); diff --git a/Mage.Client/src/main/java/mage/client/cards/BigCard.java b/Mage.Client/src/main/java/mage/client/cards/BigCard.java index 75fa5508789..cc3417c1b44 100644 --- a/Mage.Client/src/main/java/mage/client/cards/BigCard.java +++ b/Mage.Client/src/main/java/mage/client/cards/BigCard.java @@ -54,6 +54,7 @@ import mage.client.plugins.impl.Plugins; import mage.client.util.ImageHelper; import mage.constants.EnlargeMode; import org.jdesktop.swingx.JXPanel; +import mage.client.util.TransformedImageCache; /** * Class for displaying big image of the card @@ -103,7 +104,13 @@ public class BigCard extends JComponent { } - public void setCard(UUID cardId, EnlargeMode enlargeMode, Image image, List strings) { + public void setCard(UUID cardId, EnlargeMode enlargeMode, Image image, List strings, boolean rotate) { + if (rotate && getWidth() > getHeight()) { + image = TransformedImageCache.getRotatedResizedImage((BufferedImage)image, getHeight(), getWidth(), Math.toRadians(90.0)); + } else { + image = TransformedImageCache.getResizedImage((BufferedImage)image, getWidth(), getHeight()); + } + if (this.cardId == null || !enlargeMode.equals(this.enlargeMode) || !this.cardId.equals(cardId)) { if (this.panel != null) { remove(this.panel); diff --git a/Mage.Client/src/main/java/mage/client/cards/Card.java b/Mage.Client/src/main/java/mage/client/cards/Card.java index 8d6235a08a3..1a43fca1558 100644 --- a/Mage.Client/src/main/java/mage/client/cards/Card.java +++ b/Mage.Client/src/main/java/mage/client/cards/Card.java @@ -374,7 +374,7 @@ public class Card extends MagePermanent implements MouseMotionListener, MouseLis @Override public void mouseMoved(MouseEvent arg0) { this.bigCard.showTextComponent(); - this.bigCard.setCard(card.getId(), EnlargeMode.NORMAL, image, getRules()); + this.bigCard.setCard(card.getId(), EnlargeMode.NORMAL, image, getRules(), false); } @Override diff --git a/Mage.Client/src/main/java/mage/client/cards/CardArea.java b/Mage.Client/src/main/java/mage/client/cards/CardArea.java index 206328c8319..6fad717b2a2 100644 --- a/Mage.Client/src/main/java/mage/client/cards/CardArea.java +++ b/Mage.Client/src/main/java/mage/client/cards/CardArea.java @@ -236,7 +236,7 @@ public class CardArea extends JPanel implements MouseListener { public void mousePressed(MouseEvent e) { if (e.getClickCount() >= 1 && !e.isConsumed()) { Object obj = e.getSource(); - if (e.getClickCount() == 2) { + if ((e.getClickCount() & 1) == 0 && (e.getClickCount() > 0)) { // double clicks and repeated double clicks e.consume(); if (obj instanceof Card) { if (e.isAltDown()) { diff --git a/Mage.Client/src/main/java/mage/client/cards/CardGrid.java b/Mage.Client/src/main/java/mage/client/cards/CardGrid.java index a2f974f991b..ce6051f0937 100644 --- a/Mage.Client/src/main/java/mage/client/cards/CardGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/CardGrid.java @@ -308,7 +308,7 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, // End of variables declaration//GEN-END:variables @Override public void mouseClicked(MouseEvent e) { - if (e.getClickCount() == 2 && !e.isConsumed()) { + if ((e.getClickCount() & 1) == 0 && (e.getClickCount() > 0) && !e.isConsumed()) { // double clicks and repeated double clicks e.consume(); Object obj = e.getSource(); if (obj instanceof Card) { diff --git a/Mage.Client/src/main/java/mage/client/cards/CardsList.java b/Mage.Client/src/main/java/mage/client/cards/CardsList.java index 2258ffce199..9d999ab6e40 100644 --- a/Mage.Client/src/main/java/mage/client/cards/CardsList.java +++ b/Mage.Client/src/main/java/mage/client/cards/CardsList.java @@ -200,7 +200,7 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar mainTable.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { - if (e.getClickCount() == 2 && !e.isConsumed()) { + if ((e.getClickCount() & 1) == 0 && (e.getClickCount() > 0) && !e.isConsumed()) { // double clicks and repeated double clicks e.consume(); if (e.isAltDown()) { handleAltDoubleClick(); @@ -755,7 +755,7 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar public void mousePressed(MouseEvent e) { if (e.getClickCount() >= 1 && !e.isConsumed()) { Object obj = e.getSource(); - if (e.getClickCount() == 2) { + if ((e.getClickCount() & 1) == 0 && (e.getClickCount() > 0)) { // double clicks and repeated double clicks e.consume(); if (obj instanceof Card) { if (e.isAltDown()) { diff --git a/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java b/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java index a2baeb67081..fc24968fc76 100644 --- a/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java @@ -183,7 +183,7 @@ public class DraftGrid extends javax.swing.JPanel implements MouseListener { @Override public void mouseClicked(MouseEvent e) { - if (e.getClickCount() == 2) { + if ((e.getClickCount() & 1) == 0 && (e.getClickCount() > 0)) { // double clicks and repeated double clicks if (e.getButton() == MouseEvent.BUTTON1) { Object obj = e.getSource(); if (obj instanceof MageCard) { diff --git a/Mage.Client/src/main/java/mage/client/cards/Permanent.java b/Mage.Client/src/main/java/mage/client/cards/Permanent.java index a01e368641d..0db10e58871 100644 --- a/Mage.Client/src/main/java/mage/client/cards/Permanent.java +++ b/Mage.Client/src/main/java/mage/client/cards/Permanent.java @@ -56,6 +56,8 @@ import mage.client.util.ImageHelper; import mage.constants.CardType; import mage.view.CounterView; import mage.view.PermanentView; +import org.mage.plugins.card.images.ImageCache; +import mage.client.util.TransformedImageCache; /** * @@ -215,7 +217,7 @@ public class Permanent extends Card { Graphics2D g = (Graphics2D) tappedImage.getGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g.drawImage(this.createImage(ImageHelper.rotate(small, dimension)), 0, 0, this); + g.drawImage(TransformedImageCache.getRotatedResizedImage(small, dimension.frameWidth, dimension.frameHeight, Math.toRadians(90.0)), 0, 0, this); g.dispose(); } diff --git a/Mage.Client/src/main/java/mage/client/components/ColorPane.java b/Mage.Client/src/main/java/mage/client/components/ColorPane.java index 240cc4380f1..8fc39aa5be9 100644 --- a/Mage.Client/src/main/java/mage/client/components/ColorPane.java +++ b/Mage.Client/src/main/java/mage/client/components/ColorPane.java @@ -139,9 +139,7 @@ public class ColorPane extends JEditorPane { if (hyperlinkEnabled) { text = text.replaceAll("(]*>([^<]*)) (\\[[0-9a-fA-F]*\\])", "$1 $3"); } - setEditable(true); kit.insertHTML(doc, doc.getLength(), text, 0, 0, null); - setEditable(false); int len = getDocument().getLength(); setCaretPosition(len); diff --git a/Mage.Client/src/main/java/mage/client/components/MageRoundPane.java b/Mage.Client/src/main/java/mage/client/components/MageRoundPane.java index 1e3c9a51689..da4afed58e1 100644 --- a/Mage.Client/src/main/java/mage/client/components/MageRoundPane.java +++ b/Mage.Client/src/main/java/mage/client/components/MageRoundPane.java @@ -1,12 +1,18 @@ package mage.client.components; +import com.google.common.base.Function; +import com.google.common.collect.MapMaker; import java.awt.BasicStroke; import java.awt.Color; +import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; +import java.util.Map; +import java.util.Objects; import javax.swing.JPanel; +import mage.client.util.ImageCaches; import org.jdesktop.swingx.graphics.GraphicsUtilities; import org.jdesktop.swingx.graphics.ShadowRenderer; @@ -21,23 +27,145 @@ public class MageRoundPane extends JPanel { private int X_OFFSET = 30; private int Y_OFFSET = 30; - private BufferedImage shadow = null; private final Color defaultBackgroundColor = new Color(255, 255, 255, 200); private Color backgroundColor = defaultBackgroundColor; private final int alpha = 0; + private static Map SHADOW_IMAGE_CACHE; + private static Map IMAGE_CACHE; + + static { + SHADOW_IMAGE_CACHE = ImageCaches.register(new MapMaker().softValues().makeComputingMap(new Function() { + @Override + public BufferedImage apply(ShadowKey key) { + return createShadowImage(key); + } + })); + + IMAGE_CACHE = ImageCaches.register(new MapMaker().softValues().makeComputingMap(new Function() { + @Override + public BufferedImage apply(Key key) { + return createImage(key); + } + })); + } + + private final static class ShadowKey + { + final int width; + final int height; + + public ShadowKey(int width, int height) { + this.width = width; + this.height = height; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 97 * hash + this.width; + hash = 97 * hash + this.height; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ShadowKey other = (ShadowKey) obj; + if (this.width != other.width) { + return false; + } + if (this.height != other.height) { + return false; + } + return true; + } + } + + private final static class Key + { + final int width; + final int height; + final int xOffset; + final int yOffset; + final Color backgroundColor; + + public Key(int width, int height, int xOffset, int yOffset, Color backgroundColor) { + this.width = width; + this.height = height; + this.xOffset = xOffset; + this.yOffset = yOffset; + this.backgroundColor = backgroundColor; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 59 * hash + this.width; + hash = 59 * hash + this.height; + hash = 59 * hash + this.xOffset; + hash = 59 * hash + this.yOffset; + hash = 59 * hash + Objects.hashCode(this.backgroundColor); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Key other = (Key) obj; + if (this.width != other.width) { + return false; + } + if (this.height != other.height) { + return false; + } + if (this.xOffset != other.xOffset) { + return false; + } + if (this.yOffset != other.yOffset) { + return false; + } + if (!Objects.equals(this.backgroundColor, other.backgroundColor)) { + return false; + } + return true; + } + } @Override protected void paintComponent(Graphics g) { - int x = X_OFFSET; - int y = Y_OFFSET; - int w = getWidth() - 2 * X_OFFSET; - int h = getHeight() - 2 * Y_OFFSET; + g.drawImage(IMAGE_CACHE.get(new Key(getWidth(), getHeight(), X_OFFSET, Y_OFFSET, backgroundColor)), 0, 0, null); + } + + private static BufferedImage createImage(Key key) { + int x = key.xOffset; + int y = key.yOffset; + int w = key.width - 2 * key.xOffset; + int h = key.height - 2 * key.yOffset; int arc = 10; - Graphics2D g2 = (Graphics2D) g.create(); + BufferedImage image = GraphicsUtilities.createCompatibleTranslucentImage(key.width, key.height); + Graphics2D g2 = image.createGraphics(); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - if (shadow != null) { + BufferedImage shadow = SHADOW_IMAGE_CACHE.get(new ShadowKey(w, h)); + + { int xOffset = (shadow.getWidth() - w) / 2; int yOffset = (shadow.getHeight() - h) / 2; g2.drawImage(shadow, x - xOffset, y - yOffset, null); @@ -54,7 +182,7 @@ public class MageRoundPane extends JPanel { g2.fillRoundRect(x, y, w, h, arc, arc); }*/ - g2.setColor(backgroundColor); + g2.setColor(key.backgroundColor); g2.fillRoundRect(x, y, w, h, arc, arc); ////////////////////////////////////////////////////////////////// @@ -66,6 +194,7 @@ public class MageRoundPane extends JPanel { // //////////////////////////////////////////////////////////////// g2.dispose(); + return image; } public void setXOffset(int x_offset) { @@ -76,24 +205,21 @@ public class MageRoundPane extends JPanel { Y_OFFSET = y_offset; } - @Override - public void setBounds(int x, int y, int width, int height) { - super.setBounds(x, y, width, height); - - int w = getWidth() - 2 * X_OFFSET; - int h = getHeight() - 2 * Y_OFFSET; + private static BufferedImage createShadowImage(ShadowKey key) { + int w = key.width; + int h = key.height; int arc = 10; int shadowSize = 50; - shadow = GraphicsUtilities.createCompatibleTranslucentImage(w, h); - Graphics2D g2 = shadow.createGraphics(); + BufferedImage base = GraphicsUtilities.createCompatibleTranslucentImage(w, h); + Graphics2D g2 = base.createGraphics(); g2.setColor(Color.WHITE); g2.fillRoundRect(0, 0, w, h, arc, arc); g2.dispose(); ShadowRenderer renderer = new ShadowRenderer(shadowSize, 0.5f, Color.GRAY); - shadow = renderer.createShadow(shadow); + return renderer.createShadow(base); } public void showDialog(boolean bShow) { diff --git a/Mage.Client/src/main/java/mage/client/components/MageTextArea.java b/Mage.Client/src/main/java/mage/client/components/MageTextArea.java index 49b0184edaa..219e0f72d7c 100644 --- a/Mage.Client/src/main/java/mage/client/components/MageTextArea.java +++ b/Mage.Client/src/main/java/mage/client/components/MageTextArea.java @@ -13,6 +13,8 @@ import org.mage.card.arcane.UI; * @author nantuko */ public class MageTextArea extends JEditorPane { + private String currentText; + private int currentPanelWidth; public MageTextArea() { UI.setHTMLEditorKit(this); @@ -31,6 +33,12 @@ public class MageTextArea extends JEditorPane { return; } + if(text.equals(currentText) && panelWidth == currentPanelWidth) + return; + + currentText = text; + currentPanelWidth = panelWidth; + final StringBuilder buffer = new StringBuilder(512); // Dialog is a java logical font family, so it should work on all systems buffer.append("F2 - Confirm \"Ok\", \"Yes\" or \"Done\" button") .append("
F4 - Skip current turn but stop on declare attackers/blockers and something on the stack") .append("
F5 - Skip to next end step but stop on declare attackers/blockers and something on the stack") + .append("
F6 - Skip current turn but stop on declare attackers/blockers") .append("
F7 - Skip to next main phase but stop on declare attackers/blockers and something on the stack") .append("
F9 - Skip everything until your next turn") .append("
F11 - Skip everything until the end step just prior to your turn") diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.form b/Mage.Client/src/main/java/mage/client/table/TablesPanel.form index 6080002708f..30e1a8e2284 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.form +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.form @@ -418,6 +418,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index e03aaad7814..0e085700777 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -158,7 +158,7 @@ public class TablesPanel extends javax.swing.JPanel { filterButtons = new JToggleButton[]{btnStateWaiting, btnStateActive, btnStateFinished, btnTypeMatch, btnTypeTourneyConstructed, btnTypeTourneyLimited, btnFormatBlock, btnFormatStandard, btnFormatModern, btnFormatLegacy, btnFormatVintage, btnFormatCommander, btnFormatTinyLeader, btnFormatLimited, btnFormatOther, - btnSkillBeginner, btnSkillCasual, btnSkillSerious, btnRated, btnUnrated}; + btnSkillBeginner, btnSkillCasual, btnSkillSerious, btnRated, btnUnrated, btnOpen, btnPassword}; JComponent[] components = new JComponent[]{chatPanelMain, jSplitPane1, jScrollPaneTablesActive, jScrollPaneTablesFinished, jPanelTop, jPanelTables}; for (JComponent component : components) { @@ -625,9 +625,19 @@ public class TablesPanel extends javax.swing.JPanel { if (btnUnrated.isSelected()){ ratingFilterList.add(RowFilter.regexFilter("^Unrated", TableTableModel.COLUMN_RATING)); } + + // Password + List> passwordFilterList = new ArrayList<>(); + if (btnOpen.isSelected()) { + passwordFilterList.add(RowFilter.regexFilter("^$", TableTableModel.COLUMN_PASSWORD)); + } + if (btnPassword.isSelected()) { + passwordFilterList.add(RowFilter.regexFilter("^\\*\\*\\*$", TableTableModel.COLUMN_PASSWORD)); + } if (stateFilterList.isEmpty() || typeFilterList.isEmpty() || formatFilterList.isEmpty() - || skillFilterList.isEmpty() || ratingFilterList.isEmpty()) { // no selection + || skillFilterList.isEmpty() || ratingFilterList.isEmpty() + || passwordFilterList.isEmpty()) { // no selection activeTablesSorter.setRowFilter(RowFilter.regexFilter("Nothing", TableTableModel.COLUMN_SKILL)); } else { List> filterList = new ArrayList<>(); @@ -661,6 +671,12 @@ public class TablesPanel extends javax.swing.JPanel { } else if (ratingFilterList.size() == 1) { filterList.addAll(ratingFilterList); } + + if (passwordFilterList.size() > 1) { + filterList.add(RowFilter.orFilter(passwordFilterList)); + } else if (passwordFilterList.size() == 1) { + filterList.addAll(passwordFilterList); + } if (filterList.size() == 1) { activeTablesSorter.setRowFilter(filterList.get(0)); @@ -710,6 +726,9 @@ public class TablesPanel extends javax.swing.JPanel { jSeparator2 = new javax.swing.JToolBar.Separator(); btnFormatLimited = new javax.swing.JToggleButton(); btnFormatOther = new javax.swing.JToggleButton(); + jSeparator5 = new javax.swing.JToolBar.Separator(); + btnOpen = new javax.swing.JToggleButton(); + btnPassword = new javax.swing.JToggleButton(); btnQuickStart = new javax.swing.JButton(); jSplitPane1 = new javax.swing.JSplitPane(); jPanelTables = new javax.swing.JPanel(); @@ -1079,6 +1098,39 @@ public class TablesPanel extends javax.swing.JPanel { } }); filterBar2.add(btnFormatOther); + filterBar2.add(jSeparator5); + + btnOpen.setSelected(true); + btnOpen.setText("Open"); + btnOpen.setToolTipText("Show open games"); + btnOpen.setFocusPainted(false); + btnOpen.setFocusable(false); + btnOpen.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnOpen.setRequestFocusEnabled(false); + btnOpen.setVerifyInputWhenFocusTarget(false); + btnOpen.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnOpen.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnFilterActionPerformed(evt); + } + }); + filterBar2.add(btnOpen); + + btnPassword.setSelected(true); + btnPassword.setText("PW"); + btnPassword.setToolTipText("Show passworded games"); + btnPassword.setFocusPainted(false); + btnPassword.setFocusable(false); + btnPassword.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnPassword.setRequestFocusEnabled(false); + btnPassword.setVerifyInputWhenFocusTarget(false); + btnPassword.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnPassword.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnFilterActionPerformed(evt); + } + }); + filterBar2.add(btnPassword); btnQuickStart.setText("Quick Start"); btnQuickStart.setFocusable(false); @@ -1290,6 +1342,8 @@ public class TablesPanel extends javax.swing.JPanel { private javax.swing.JToggleButton btnFormatVintage; private javax.swing.JButton btnNewTable; private javax.swing.JButton btnNewTournament; + private javax.swing.JToggleButton btnOpen; + private javax.swing.JToggleButton btnPassword; private javax.swing.JButton btnQuickStart; private javax.swing.JToggleButton btnSkillBeginner; private javax.swing.JToggleButton btnSkillCasual; @@ -1337,12 +1391,14 @@ class TableTableModel extends AbstractTableModel { public static final int COLUMN_GAME_TYPE = 3; public static final int COLUMN_INFO = 4; public static final int COLUMN_STATUS = 5; - public static final int COLUMN_SKILL = 7; - public static final int COLUMN_RATING = 8; - public static final int COLUMN_QUIT_RATIO = 9; - public static final int ACTION_COLUMN = 10; // column the action is located (starting with 0) + public static final int COLUMN_PASSWORD = 6; + public static final int COLUMN_CREATED = 7; + public static final int COLUMN_SKILL = 8; + public static final int COLUMN_RATING = 9; + public static final int COLUMN_QUIT_RATIO = 10; + public static final int ACTION_COLUMN = 11; // column the action is located (starting with 0) - private final String[] columnNames = new String[]{"M/T", "Deck Type", "Owner / Players", "Game Type", "Info", "Status", "Created / Started", "Skill Level", "Rating", "Quit %", "Action"}; + private final String[] columnNames = new String[]{"M/T", "Deck Type", "Owner / Players", "Game Type", "Info", "Status", "Password", "Created / Started", "Skill Level", "Rating", "Quit %", "Action"}; private TableView[] tables = new TableView[0]; private static final DateFormat timeFormatter = new SimpleDateFormat("HH:mm:ss"); @@ -1385,14 +1441,16 @@ class TableTableModel extends AbstractTableModel { case 5: return tables[arg0].getTableStateText(); case 6: - return timeFormatter.format(tables[arg0].getCreateTime()); + return tables[arg0].isPassworded() ? "***" : ""; case 7: - return tables[arg0].getSkillLevel(); + return timeFormatter.format(tables[arg0].getCreateTime()); case 8: - return tables[arg0].isRated() ? "Rated" : "Unrated"; + return tables[arg0].getSkillLevel(); case 9: - return tables[arg0].getQuitRatio(); + return tables[arg0].isRated() ? "Rated" : "Unrated"; case 10: + return tables[arg0].getQuitRatio(); + case 11: switch (tables[arg0].getTableState()) { case WAITING: @@ -1419,14 +1477,14 @@ class TableTableModel extends AbstractTableModel { default: return ""; } - case 11: - return tables[arg0].isTournament(); case 12: + return tables[arg0].isTournament(); + case 13: if (!tables[arg0].getGames().isEmpty()) { return tables[arg0].getGames().get(0); } return null; - case 13: + case 14: return tables[arg0].getTableId(); } return ""; diff --git a/Mage.Client/src/main/java/mage/client/util/ButtonColumn.java b/Mage.Client/src/main/java/mage/client/util/ButtonColumn.java index 20a4d76c452..7003a4ee6d0 100644 --- a/Mage.Client/src/main/java/mage/client/util/ButtonColumn.java +++ b/Mage.Client/src/main/java/mage/client/util/ButtonColumn.java @@ -111,7 +111,7 @@ public class ButtonColumn extends AbstractCellEditor implements TableCellRendere @Override public void actionPerformed(ActionEvent e) { - if (table.getRowCount() > 0 && table.getRowCount() >= table.getEditingRow()) { + if (table.getRowCount() > 0 && table.getRowCount() >= table.getEditingRow() && table.getEditingRow() >= 0) { int row = table.convertRowIndexToModel(table.getEditingRow()); fireEditingStopped(); ActionEvent event = new ActionEvent(table, ActionEvent.ACTION_PERFORMED, "" + row); diff --git a/Mage.Client/src/main/java/mage/client/util/ImageCaches.java b/Mage.Client/src/main/java/mage/client/util/ImageCaches.java new file mode 100644 index 00000000000..2cb045c4d04 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/ImageCaches.java @@ -0,0 +1,34 @@ +/* + * 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.client.util; + +import java.util.Map; +import java.util.Vector; + +/** + * + * @author user + */ +public class ImageCaches { + private static Vector IMAGE_CACHES; + + static { + IMAGE_CACHES = new Vector(); + } + + public static Map register(Map map) + { + IMAGE_CACHES.add(map); + return map; + } + + public static void flush() + { + for (Map map : IMAGE_CACHES) { + map.clear(); + } + } +} diff --git a/Mage.Client/src/main/java/mage/client/util/ImageHelper.java b/Mage.Client/src/main/java/mage/client/util/ImageHelper.java index e4d55b0df47..1eddc28e0dd 100644 --- a/Mage.Client/src/main/java/mage/client/util/ImageHelper.java +++ b/Mage.Client/src/main/java/mage/client/util/ImageHelper.java @@ -52,6 +52,7 @@ import static mage.client.constants.Constants.FRAME_MAX_WIDTH; import static mage.client.constants.Constants.SYMBOL_MAX_SPACE; import mage.view.CardView; import org.mage.card.arcane.UI; +import org.mage.plugins.card.images.ImageCache; /** * @@ -70,21 +71,6 @@ public class ImageHelper { return null; } - /** - * - * @param ref - image name - * @param height - height after scaling - * @return a scaled image that preserves the original aspect ratio, with a - * specified height - */ - public static BufferedImage loadImage(String ref, int height) { - BufferedImage image = loadImage(ref); - if (image != null) { - return scaleImage(image, height); - } - return null; - } - public static BufferedImage loadImage(String ref) { if (!images.containsKey(ref)) { try { @@ -107,67 +93,7 @@ public class ImageHelper { } public static BufferedImage scaleImage(BufferedImage image, int width, int height) { - BufferedImage scaledImage = image; - int w = image.getWidth(); - int h = image.getHeight(); - do { - w /= 2; - h /= 2; - if (w < width || h < height) { - w = width; - h = height; - } - BufferedImage newImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); - Graphics2D graphics2D = newImage.createGraphics(); - graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - graphics2D.drawImage(scaledImage, 0, 0, w, h, null); - graphics2D.dispose(); - scaledImage = newImage; - } while (w != width || h != height); - return scaledImage; - } - - public static BufferedImage scaleImage(BufferedImage image, int height) { - double ratio = height / (double) image.getHeight(); - int width = (int) (image.getWidth() * ratio); - return scaleImage(image, width, height); - } - - public static MemoryImageSource rotate(Image image, CardDimensions dimensions) { - int buffer[] = new int[dimensions.frameWidth * dimensions.frameHeight]; - int rotate[] = new int[dimensions.frameHeight * dimensions.frameWidth]; - PixelGrabber grabber = new PixelGrabber(image, 0, 0, dimensions.frameWidth, dimensions.frameHeight, buffer, 0, dimensions.frameWidth); - try { - grabber.grabPixels(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - for (int y = 0; y < dimensions.frameHeight; y++) { - for (int x = 0; x < dimensions.frameWidth; x++) { - rotate[((dimensions.frameWidth - x - 1) * dimensions.frameHeight) + y] = buffer[(y * dimensions.frameWidth) + x]; - } - } - - return new MemoryImageSource(dimensions.frameHeight, dimensions.frameWidth, rotate, 0, dimensions.frameHeight); - - } - - public static BufferedImage rotate(BufferedImage image, double angle) { - double sin = Math.abs(Math.sin(angle)), cos = Math.abs(Math.cos(angle)); - int w = image.getWidth(), h = image.getHeight(); - int neww = (int) Math.floor(w * cos + h * sin), newh = (int) Math.floor(h * cos + w * sin); - - GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - GraphicsDevice gs = ge.getDefaultScreenDevice(); - GraphicsConfiguration gc = gs.getDefaultConfiguration(); - - BufferedImage result = gc.createCompatibleImage(neww, newh, Transparency.TRANSLUCENT); - Graphics2D g = result.createGraphics(); - g.translate((neww - w) / 2, (newh - h) / 2); - g.rotate(angle, w / 2, h / 2); - g.drawRenderedImage(image, null); - g.dispose(); - return result; + return TransformedImageCache.getResizedImage(image, width, height); } public static void drawCosts(List costs, Graphics2D g, int xOffset, int yOffset, ImageObserver o) { @@ -191,26 +117,7 @@ public class ImageHelper { * @return */ public static BufferedImage getResizedImage(BufferedImage original, int width, int height) { - ResampleOp resampleOp = new ResampleOp(width, height); - BufferedImage image = resampleOp.filter(original, null); - return image; - } - - /** - * Returns an image scaled to fit width panel - * - * @param original - * @param width - * @return - */ - public static BufferedImage getResizedImage(BufferedImage original, int width) { - if (width != original.getWidth()) { - double ratio = width / (double) original.getWidth(); - int height = (int) (original.getHeight() * ratio); - return getResizedImage(original, width, height); - } else { - return original; - } + return TransformedImageCache.getResizedImage(original, width, height); } /** @@ -223,17 +130,7 @@ public class ImageHelper { * @return scaled image */ public static BufferedImage scale(BufferedImage sbi, int imageType, int dWidth, int dHeight) { - BufferedImage dbi = null; - if (sbi != null) { - double fWidth = dWidth / sbi.getWidth(); - double fHeight = dHeight / sbi.getHeight(); - dbi = new BufferedImage(dWidth, dHeight, imageType); - Graphics2D g = dbi.createGraphics(); - AffineTransform at = AffineTransform.getScaleInstance(fWidth, fHeight); - g.drawRenderedImage(sbi, at); - g.dispose(); - } - return dbi; + return TransformedImageCache.getResizedImage(sbi, dWidth, dHeight); } /** @@ -244,9 +141,7 @@ public class ImageHelper { * @return */ public static BufferedImage getResizedImage(BufferedImage original, Rectangle sizeNeed) { - ResampleOp resampleOp = new ResampleOp(sizeNeed.width, sizeNeed.height); - BufferedImage image = resampleOp.filter(original, null); - return image; + return TransformedImageCache.getResizedImage(original, sizeNeed.width, sizeNeed.height); } /** diff --git a/Mage.Client/src/main/java/mage/client/util/TransformedImageCache.java b/Mage.Client/src/main/java/mage/client/util/TransformedImageCache.java new file mode 100644 index 00000000000..9a2cf205dcd --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/TransformedImageCache.java @@ -0,0 +1,153 @@ +/* + * 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.client.util; + +import com.google.common.base.Function; +import com.google.common.collect.MapMaker; +import com.mortennobel.imagescaling.ResampleOp; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Transparency; +import java.awt.image.BufferedImage; +import java.util.Map; +import mage.client.util.ImageHelper; + +/** + * + * @author user + */ +public class TransformedImageCache { + private final static class Key + { + final int width; + final int height; + final double angle; + + public Key(int width, int height, double angle) { + this.width = width; + this.height = height; + this.angle = angle; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 53 * hash + this.width; + hash = 53 * hash + this.height; + hash = 53 * hash + (int) (Double.doubleToLongBits(this.angle) ^ (Double.doubleToLongBits(this.angle) >>> 32)); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Key other = (Key) obj; + if (this.width != other.width) { + return false; + } + if (this.height != other.height) { + return false; + } + if (Double.doubleToLongBits(this.angle) != Double.doubleToLongBits(other.angle)) { + return false; + } + return true; + } + } + + static Map> IMAGE_CACHE; + + static + { + // TODO: can we use a single map? + IMAGE_CACHE = ImageCaches.register(new MapMaker().softValues().makeComputingMap(new Function>() { + @Override + public Map apply(final Key key) { + return new MapMaker().weakKeys().softValues().makeComputingMap(new Function() { + @Override + public BufferedImage apply(BufferedImage image) { + if(key.width != image.getWidth() || key.height != image.getHeight()) + image = resizeImage(image, key.width, key.height); + if(key.angle != 0.0) + image = rotateImage(image, key.angle); + return image; + } + }); + } + })); + } + + private static BufferedImage rotateImage(BufferedImage image, double angle) { + double sin = Math.abs(Math.sin(angle)), cos = Math.abs(Math.cos(angle)); + int w = image.getWidth(), h = image.getHeight(); + int neww = (int) Math.floor(w * cos + h * sin), newh = (int) Math.floor(h * cos + w * sin); + + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice gs = ge.getDefaultScreenDevice(); + GraphicsConfiguration gc = gs.getDefaultConfiguration(); + + BufferedImage result = gc.createCompatibleImage(neww, newh, Transparency.TRANSLUCENT); + Graphics2D g = result.createGraphics(); + g.translate((neww - w) / 2, (newh - h) / 2); + g.rotate(angle, w / 2, h / 2); + g.drawRenderedImage(image, null); + g.dispose(); + return result; + } + + private static BufferedImage resizeImage(BufferedImage original, int width, int height) { + ResampleOp resampleOp = new ResampleOp(width, height); + BufferedImage image = resampleOp.filter(original, null); + return image; + } + + public static BufferedImage getResizedImage(BufferedImage image, int width, int height) + { + return getRotatedResizedImage(image, width, height, 0.0); + } + + public static BufferedImage getRotatedImage(BufferedImage image, double angle) + { + return getRotatedResizedImage(image, -1, -1, angle); + } + + public static BufferedImage getRotatedResizedImage(BufferedImage image, int width, int height, double angle) + { + int imageWidth = image.getWidth(); + int imageHeight = image.getHeight(); + + if(angle == 0.0 && (width < 0 || imageWidth == width) && (height < 0 || imageHeight == height)) + return image; + + int resWidth; + int resHeight; + if(width < 0 && height < 0) { + resWidth = imageWidth; + resHeight = imageHeight; + } else if((height < 0) || (width >= 0 && imageHeight * width <= imageWidth * height)) { + resWidth = width; + resHeight = imageHeight * width / imageWidth; + } else { + resWidth = imageWidth * height / imageHeight; + resHeight = height; + } + + if(angle == 0.0 && imageWidth == resWidth && imageHeight == resHeight) + return image; + + return IMAGE_CACHE.get(new Key(resWidth, resHeight, angle)).get(image); + } +} diff --git a/Mage.Client/src/main/java/mage/client/util/audio/LinePool.java b/Mage.Client/src/main/java/mage/client/util/audio/LinePool.java index 8145f756bbf..6f52fdae2d5 100644 --- a/Mage.Client/src/main/java/mage/client/util/audio/LinePool.java +++ b/Mage.Client/src/main/java/mage/client/util/audio/LinePool.java @@ -5,6 +5,10 @@ import java.util.LinkedList; import java.util.Set; import java.util.Timer; import java.util.TimerTask; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; @@ -19,8 +23,6 @@ import javax.sound.sampled.SourceDataLine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import mage.utils.ThreadUtils; - public class LinePool { private final Logger log = LoggerFactory.getLogger(getClass()); @@ -41,12 +43,25 @@ public class LinePool { private Mixer mixer; private int alwaysActive; + private ThreadPoolExecutor threadPool; + private int threadCount; public LinePool() { this(new AudioFormat(22050, 16, 1, true, false), 4, 1); } public LinePool(AudioFormat audioFormat, int size, int alwaysActive) { + threadPool = new ThreadPoolExecutor(alwaysActive, size, 30L, TimeUnit.SECONDS, new LinkedBlockingQueue(), new ThreadFactory() { + @Override + public Thread newThread (Runnable runnable) { + threadCount++; + Thread thread = new Thread(runnable, "Audio" + threadCount); + thread.setDaemon(true); + return thread; + } + }); + threadPool.prestartAllCoreThreads(); + format = audioFormat; this.alwaysActive = alwaysActive; mixer = AudioSystem.getMixer(null); @@ -95,7 +110,7 @@ public class LinePool { busyLines.add(line); logLineStats(); } - ThreadUtils.threadPool.submit(new Runnable() { + threadPool.submit(new Runnable() { @Override public void run() { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/Animation.java b/Mage.Client/src/main/java/org/mage/card/arcane/Animation.java index dec3764f708..8df10872f4f 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/Animation.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/Animation.java @@ -8,6 +8,8 @@ import java.util.Timer; import java.util.TimerTask; public abstract class Animation { + private static boolean ENABLED = true; + private static final long TARGET_MILLIS_PER_FRAME = 30; private static Timer timer = new Timer("Animation", true); @@ -25,6 +27,18 @@ public abstract class Animation { } public Animation (final long duration, long delay) { + if(!ENABLED) { + UI.invokeLater(new Runnable() { + @Override + public void run () { + start(); + //update(1.0f); + end(); + } + }); + return; + } + timerTask = new TimerTask() { @Override public void run () { @@ -171,6 +185,12 @@ public abstract class Animation { @Override protected void end () { + if (!state) { + parent.toggleTransformed(); + } + state = true; + panel.transformAngle = 0; + parent.onEndAnimation(); parent.repaint(); } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java index 480249f9bf6..38e317b226e 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java @@ -1,5 +1,7 @@ package org.mage.card.arcane; +import com.google.common.base.Function; +import com.google.common.collect.MapMaker; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Dimension; @@ -23,6 +25,7 @@ import java.awt.image.BufferedImage; import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.StringTokenizer; import java.util.UUID; import javax.swing.BorderFactory; @@ -38,6 +41,7 @@ import mage.cards.action.TransferData; import mage.client.dialog.PreferencesDialog; import mage.client.plugins.adapters.MageActionCallback; import mage.client.plugins.impl.Plugins; +import mage.client.util.ImageCaches; import mage.client.util.ImageHelper; import mage.client.util.audio.AudioManager; import mage.components.ImagePanel; @@ -52,8 +56,7 @@ import mage.view.PermanentView; import mage.view.StackAbilityView; import net.java.truevfs.access.TFile; import org.apache.log4j.Logger; -import org.mage.card.arcane.ScaledImagePanel.MultipassType; -import org.mage.card.arcane.ScaledImagePanel.ScalingType; +import org.jdesktop.swingx.graphics.GraphicsUtilities; import static org.mage.plugins.card.constants.Constants.THUMBNAIL_SIZE_FULL; import org.mage.plugins.card.dl.sources.DirectLinksForDownload; import org.mage.plugins.card.images.ImageCache; @@ -160,6 +163,111 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti // if this is set, it's opened if the user right clicks on the card panel private JPopupMenu popupMenu; + private static Map IMAGE_CACHE; + + private final static class Key + { + final int width; + final int height; + final int cardWidth; + final int cardHeight; + final int cardXOffset; + final int cardYOffset; + final boolean hasImage; + final boolean isSelected; + final boolean isChoosable; + final boolean isPlayable; + final boolean canAttack; + + public Key(int width, int height, int cardWidth, int cardHeight, int cardXOffset, int cardYOffset, boolean hasImage, boolean isSelected, boolean isChoosable, boolean isPlayable, boolean canAttack) { + this.width = width; + this.height = height; + this.cardWidth = cardWidth; + this.cardHeight = cardHeight; + this.cardXOffset = cardXOffset; + this.cardYOffset = cardYOffset; + this.hasImage = hasImage; + this.isSelected = isSelected; + this.isChoosable = isChoosable; + this.isPlayable = isPlayable; + this.canAttack = canAttack; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 19 * hash + this.width; + hash = 19 * hash + this.height; + hash = 19 * hash + this.cardWidth; + hash = 19 * hash + this.cardHeight; + hash = 19 * hash + this.cardXOffset; + hash = 19 * hash + this.cardYOffset; + hash = 19 * hash + (this.hasImage ? 1 : 0); + hash = 19 * hash + (this.isSelected ? 1 : 0); + hash = 19 * hash + (this.isChoosable ? 1 : 0); + hash = 19 * hash + (this.isPlayable ? 1 : 0); + hash = 19 * hash + (this.canAttack ? 1 : 0); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Key other = (Key) obj; + if (this.width != other.width) { + return false; + } + if (this.height != other.height) { + return false; + } + if (this.cardWidth != other.cardWidth) { + return false; + } + if (this.cardHeight != other.cardHeight) { + return false; + } + if (this.cardXOffset != other.cardXOffset) { + return false; + } + if (this.cardYOffset != other.cardYOffset) { + return false; + } + if (this.hasImage != other.hasImage) { + return false; + } + if (this.isSelected != other.isSelected) { + return false; + } + if (this.isChoosable != other.isChoosable) { + return false; + } + if (this.isPlayable != other.isPlayable) { + return false; + } + if (this.canAttack != other.canAttack) { + return false; + } + return true; + } + } + + static { + IMAGE_CACHE = ImageCaches.register(new MapMaker().softValues().makeComputingMap(new Function() { + @Override + public BufferedImage apply(Key key) { + return createImage(key); + } + })); + } + public CardPanel(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback, final boolean foil, Dimension dimension) { this.gameCard = newGameCard; this.callback = callback; @@ -304,44 +412,23 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti imagePanel = new ScaledImagePanel(); imagePanel.setBorder(BorderFactory.createLineBorder(Color.white)); add(imagePanel); - imagePanel.setScaleLarger(true); - imagePanel.setScalingType(ScalingType.nearestNeighbor); - imagePanel.setScalingMultiPassType(MultipassType.none); String cardType = getType(newGameCard); tooltipText.setText(getText(cardType, newGameCard)); - Util.threadPool.submit(new Runnable() { - @Override - public void run() { - try { - tappedAngle = isTapped() ? CardPanel.TAPPED_ANGLE : 0; - flippedAngle = isFlipped() ? CardPanel.FLIPPED_ANGLE : 0; - if (!loadImage) { - return; - } - BufferedImage srcImage; - if (gameCard.isFaceDown()) { - srcImage = getFaceDownImage(); - } else { - srcImage = ImageCache.getImage(gameCard, getCardWidth(), getCardHeight()); - } - if (srcImage != null) { - hasImage = true; - setText(gameCard); - setImage(srcImage); - } - if (gameCard.isTransformed()) { - toggleTransformed(); - } - setText(gameCard); - } catch (Exception e) { - LOGGER.fatal("Problem during image animation", e); - } catch (Error err) { - LOGGER.error("Problem during image animation", err); - } - } - }); + tappedAngle = isTapped() ? CardPanel.TAPPED_ANGLE : 0; + flippedAngle = isFlipped() ? CardPanel.FLIPPED_ANGLE : 0; + + if (!loadImage) { + return; + } + + if (gameCard.isTransformed()) { + // this calls updateImage + toggleTransformed(); + } else { + updateImage(); + } } private void setTypeIcon(BufferedImage bufferedImage, String toolTipText) { @@ -387,9 +474,12 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti titleText.setText(!displayTitleAnyway && hasImage ? "" : card.getName()); } - private void setImage(Image srcImage) { + private void setImage(BufferedImage srcImage) { synchronized (imagePanel) { - imagePanel.setImage(srcImage); + if(srcImage != null) + imagePanel.setImage(srcImage); + else + imagePanel.clearImage(); repaint(); } doLayout(); @@ -413,10 +503,6 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti return zone; } - public void setScalingType(ScalingType scalingType) { - imagePanel.setScalingType(scalingType); - } - public void setDisplayEnabled(boolean displayEnabled) { this.displayEnabled = displayEnabled; } @@ -484,15 +570,28 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti @Override protected void paintComponent(Graphics g) { - Graphics2D g2d = (Graphics2D) g; - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + Graphics2D g2d = (Graphics2D)(g.create()); if (alpha != 1.0f) { AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha); g2d.setComposite(composite); } - if (!hasImage) { + g2d.drawImage(IMAGE_CACHE.get(new Key(getWidth(), getHeight(), cardWidth, cardHeight, cardXOffset, cardYOffset, hasImage, isSelected, isChoosable, isPlayable, canAttack)), 0, 0, null); + g2d.dispose(); + } + + private static BufferedImage createImage(Key key) { + int cardWidth = key.cardWidth; + int cardHeight = key.cardHeight; + int cardXOffset = key.cardXOffset; + int cardYOffset = key.cardYOffset; + + BufferedImage image = GraphicsUtilities.createCompatibleTranslucentImage(key.width, key.height); + Graphics2D g2d = image.createGraphics(); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + if (!key.hasImage) { g2d.setColor(new Color(30, 200, 200, 120)); } else { g2d.setColor(new Color(0, 0, 0, 0)); @@ -501,19 +600,19 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti int cornerSize = Math.max(4, Math.round(cardWidth * ROUNDED_CORNER_SIZE)); g2d.fillRoundRect(cardXOffset, cardYOffset, cardWidth, cardHeight, cornerSize, cornerSize); - if (isSelected) { + if (key.isSelected) { g2d.setColor(Color.green); g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize); - } else if (isChoosable) { + } else if (key.isChoosable) { g2d.setColor(new Color(250, 250, 0, 230)); g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize); - } else if (isPlayable) { + } else if (key.isPlayable) { g2d.setColor(new Color(153, 102, 204, 200)); //g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize); g2d.fillRoundRect(cardXOffset, cardYOffset, cardWidth, cardHeight, cornerSize, cornerSize); } - if (canAttack) { + if (key.canAttack) { g2d.setColor(new Color(0, 0, 255, 230)); g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize); } @@ -524,6 +623,9 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti g2d.setColor(new Color(200,10,10,200)); g2d.fillRoundRect(cardXOffset+1, cardYOffset+1, cardWidth-2, cardHeight-2, cornerSize, cornerSize); }*/ + g2d.dispose(); + + return image; } @Override @@ -619,12 +721,6 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti ptText.setLocation(cardXOffset + ptX - TEXT_GLOW_SIZE / 2 - offsetX, cardYOffset + ptY - TEXT_GLOW_SIZE / 2); } - - if (isAnimationPanel || cardWidth < 200) { - imagePanel.setScalingType(ScalingType.nearestNeighbor); - } else { - imagePanel.setScalingType(ScalingType.bilinear); - } } @Override @@ -634,6 +730,11 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti @Override public final void setCardBounds(int x, int y, int cardWidth, int cardHeight) { + if(cardWidth == this.cardWidth && cardHeight == this.cardHeight) { + setBounds(x - cardXOffset, y - cardYOffset, getWidth(), getHeight()); + return; + } + this.cardWidth = cardWidth; this.symbolWidth = cardWidth / 7; this.cardHeight = cardHeight; @@ -656,6 +757,8 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti int height = cardYOffset * 2 + cardHeight; setBounds(x - cardXOffset, y - cardYOffset, width, height); } + if(imagePanel != null && imagePanel.getSrcImage() != null) + updateImage(); } public int getXOffset(int cardWidth) { @@ -734,15 +837,21 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti return cardYOffset; } + private int updateImageStamp; + @Override public void updateImage() { + tappedAngle = isTapped() ? CardPanel.TAPPED_ANGLE : 0; + flippedAngle = isFlipped() ? CardPanel.FLIPPED_ANGLE : 0; + + final CardView gameCard = this.gameCard; + final int stamp = ++updateImageStamp; + Util.threadPool.submit(new Runnable() { @Override public void run() { try { - tappedAngle = isTapped() ? CardPanel.TAPPED_ANGLE : 0; - flippedAngle = isFlipped() ? CardPanel.FLIPPED_ANGLE : 0; - BufferedImage srcImage; + final BufferedImage srcImage; if (gameCard.isFaceDown()) { srcImage = getFaceDownImage(); } else if (cardWidth > THUMBNAIL_SIZE_FULL.width) { @@ -750,11 +859,16 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti } else { srcImage = ImageCache.getThumbnail(gameCard); } - if (srcImage != null) { - hasImage = true; - setText(gameCard); - setImage(srcImage); - } + UI.invokeLater(new Runnable() { + @Override + public void run () { + if(stamp == updateImageStamp) { + hasImage = srcImage != null; + setText(gameCard); + setImage(srcImage); + } + } + }); } catch (Exception e) { e.printStackTrace(); } catch (Error err) { @@ -1056,7 +1170,7 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti if (gameCard.hideInfo()) { return; } - if (getMousePosition(true) != null) { + if (this.contains(e.getPoint())) { return; } if (tooltipShowing) { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java b/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java index c5e2f40c0d0..5a32f6b8727 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java @@ -1,15 +1,30 @@ package org.mage.card.arcane; +import com.google.common.base.Function; +import com.google.common.collect.MapMaker; import javax.swing.*; import java.awt.*; import java.awt.font.FontRenderContext; import java.awt.font.LineBreakMeasurer; import java.awt.font.TextAttribute; import java.awt.font.TextLayout; +import java.awt.image.BufferedImage; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; import java.text.AttributedCharacterIterator; import java.text.AttributedString; import java.text.BreakIterator; import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import mage.client.util.ImageCaches; +import org.jdesktop.swingx.graphics.GraphicsUtilities; public class GlowText extends JLabel { private static final long serialVersionUID = 1827677946939348001L; @@ -19,6 +34,109 @@ public class GlowText extends JLabel { private Color glowColor; private boolean wrap; private int lineCount = 0; + private static Map IMAGE_CACHE; + + private final static class Key + { + final int width; + final int height; + final String text; + final Map fontAttributes; + final Color color; + final int glowSize; + final float glowIntensity; + final Color glowColor; + final boolean wrap; + + // used to pass the native font to the create function so we don't waste performance recreating it, but without holding onto the native object + final transient WeakReference originalFont; + + Font getFont() { + Font res = this.originalFont.get(); + if(res == null) + res = Font.getFont(this.fontAttributes); + return res; + } + + public Key(int width, int height, String text, Font font, Color color, int glowSize, float glowIntensity, Color glowColor, boolean wrap) { + this.width = width; + this.height = height; + this.text = text; + this.originalFont = new WeakReference<>(font); + this.fontAttributes = font.getAttributes(); + this.color = color; + this.glowSize = glowSize; + this.glowIntensity = glowIntensity; + this.glowColor = glowColor; + this.wrap = wrap; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 23 * hash + this.width; + hash = 23 * hash + this.height; + hash = 23 * hash + Objects.hashCode(this.text); + hash = 23 * hash + Objects.hashCode(this.fontAttributes); + hash = 23 * hash + Objects.hashCode(this.color); + hash = 23 * hash + this.glowSize; + hash = 23 * hash + Float.floatToIntBits(this.glowIntensity); + hash = 23 * hash + Objects.hashCode(this.glowColor); + hash = 23 * hash + (this.wrap ? 1 : 0); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Key other = (Key) obj; + if (this.width != other.width) { + return false; + } + if (this.height != other.height) { + return false; + } + if (this.glowSize != other.glowSize) { + return false; + } + if (Float.floatToIntBits(this.glowIntensity) != Float.floatToIntBits(other.glowIntensity)) { + return false; + } + if (this.wrap != other.wrap) { + return false; + } + if (!Objects.equals(this.text, other.text)) { + return false; + } + if (!Objects.equals(this.fontAttributes, other.fontAttributes)) { + return false; + } + if (!Objects.equals(this.color, other.color)) { + return false; + } + if (!Objects.equals(this.glowColor, other.glowColor)) { + return false; + } + return true; + } + } + + static { + IMAGE_CACHE = ImageCaches.register(new MapMaker().softValues().makeComputingMap(new Function() { + @Override + public BufferedImage apply(Key key) { + return createImage(key); + } + })); + } public void setGlow (Color glowColor, int size, float intensity) { this.glowColor = glowColor; @@ -38,32 +156,32 @@ public class GlowText extends JLabel { return size; } - @Override - public void setText (String text) { - super.setText(text); - } - @Override public void paint (Graphics g) { if (getText().length() == 0) { return; } - Graphics2D g2d = (Graphics2D)g; + g.drawImage(IMAGE_CACHE.get(new Key(getWidth(), getHeight(), getText(), getFont(), getForeground(), glowSize, glowIntensity, glowColor, wrap)), 0, 0, null); + } + + private static BufferedImage createImage (Key key) { + Dimension size = new Dimension(key.width, key.height); + BufferedImage image = GraphicsUtilities.createCompatibleTranslucentImage(size.width, size.height); + Graphics2D g2d = image.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - Dimension size = getSize(); int textX = 0, textY = 0; - int wrapWidth = Math.max(0, wrap ? size.width - glowSize : Integer.MAX_VALUE); + int wrapWidth = Math.max(0, key.wrap ? size.width - key.glowSize : Integer.MAX_VALUE); - AttributedString attributedString = new AttributedString(getText()); - attributedString.addAttribute(TextAttribute.FONT, getFont()); + AttributedString attributedString = new AttributedString(key.text); + attributedString.addAttribute(TextAttribute.FONT, key.getFont()); AttributedCharacterIterator charIterator = attributedString.getIterator(); FontRenderContext fontContext = g2d.getFontRenderContext(); LineBreakMeasurer measurer = new LineBreakMeasurer(charIterator, BreakIterator.getWordInstance(Locale.ENGLISH), fontContext); - lineCount = 0; + int lineCount = 0; while (measurer.getPosition() < charIterator.getEndIndex()) { //TextLayout textLayout = measurer.nextLayout(wrapWidth); lineCount++; @@ -83,23 +201,21 @@ public class GlowText extends JLabel { float ascent = textLayout.getAscent(); textY += ascent; // Move down to baseline. - g2d.setColor(glowColor); + g2d.setColor(key.glowColor); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f)); + int glowSize = key.glowSize; textLayout.draw(g2d, textX + glowSize / 2 + 1, textY + glowSize / 2 - 1); textLayout.draw(g2d, textX + glowSize / 2 + 1, textY + glowSize / 2 + 1); textLayout.draw(g2d, textX + glowSize / 2 - 1, textY + glowSize / 2 - 1); textLayout.draw(g2d, textX + glowSize / 2 - 1, textY + glowSize / 2 + 1); - g2d.setColor(getForeground()); + g2d.setColor(key.color); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f)); textLayout.draw(g2d, textX + glowSize / 2, textY + glowSize / 2); textY += textLayout.getDescent() + textLayout.getLeading(); // Move down to top of next line. } - } - - public int getLineCount() { - return this.lineCount; + return image; } public void setGlowColor(Color glowColor) { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ScaledImagePanel.java b/Mage.Client/src/main/java/org/mage/card/arcane/ScaledImagePanel.java index 8a2edd3edb8..42913c1a3e1 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ScaledImagePanel.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ScaledImagePanel.java @@ -1,194 +1,42 @@ package org.mage.card.arcane; import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Image; -import java.awt.RenderingHints; import java.awt.image.BufferedImage; - import javax.swing.JPanel; +import mage.client.util.TransformedImageCache; public class ScaledImagePanel extends JPanel { private static final long serialVersionUID = -1523279873208605664L; - private volatile Image srcImage; - - private ScalingType scalingType = ScalingType.bilinear; - private boolean scaleLarger; - private MultipassType multiPassType = MultipassType.bilinear; + private volatile BufferedImage srcImage; public ScaledImagePanel () { super(false); setOpaque(false); } - - public void setImage(Image srcImage) { - this.srcImage = srcImage; - } - + public void clearImage () { srcImage = null; repaint(); } - public void setScalingMultiPassType (MultipassType multiPassType) { - this.multiPassType = multiPassType; - } - - public void setScalingType (ScalingType scalingType) { - this.scalingType = scalingType; - } - - public void setScaleLarger (boolean scaleLarger) { - this.scaleLarger = scaleLarger; + public void setImage(BufferedImage srcImage) { + this.srcImage = srcImage; } public boolean hasImage () { return srcImage != null; } - private ScalingInfo getScalingInfo () { - int panelWidth = getWidth(); - int panelHeight = getHeight(); - int srcWidth = srcImage.getWidth(null); - int srcHeight = srcImage.getHeight(null); - int targetWidth = srcWidth; - int targetHeight = srcHeight; - if (scaleLarger || srcWidth > panelWidth || srcHeight > panelHeight) { - targetWidth = Math.round(panelHeight * (srcWidth / (float)srcHeight)); - if (targetWidth > panelWidth) { - targetHeight = Math.round(panelWidth * (srcHeight / (float)srcWidth)); - targetWidth = panelWidth; - } else { - targetHeight = panelHeight; - } - } - ScalingInfo info = new ScalingInfo(); - info.targetWidth = targetWidth; - info.targetHeight = targetHeight; - info.srcWidth = srcWidth; - info.srcHeight = srcHeight; - info.x = panelWidth / 2 - targetWidth / 2; - info.y = panelHeight / 2 - targetHeight / 2; - return info; - } - @Override public void paint (Graphics g) { if (srcImage == null) { return; } - Graphics2D g2 = (Graphics2D)g.create(); - ScalingInfo info = getScalingInfo(); - - switch (scalingType) { - case nearestNeighbor: - scaleWithDrawImage(g2, info, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); - break; - case bilinear: - scaleWithDrawImage(g2, info, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - break; - case bicubic: - scaleWithDrawImage(g2, info, RenderingHints.VALUE_INTERPOLATION_BICUBIC); - break; - case areaAveraging: - scaleWithGetScaledInstance(g2, info, Image.SCALE_AREA_AVERAGING); - break; - case replicate: - scaleWithGetScaledInstance(g2, info, Image.SCALE_REPLICATE); - break; - } + g.drawImage(TransformedImageCache.getResizedImage(srcImage, getWidth(), getHeight()), 0, 0, null); } - private void scaleWithGetScaledInstance (Graphics2D g2, ScalingInfo info, int hints) { - Image scaledImage = srcImage.getScaledInstance(info.targetWidth, info.targetHeight, hints); - g2.drawImage(scaledImage, info.x, info.y, null); - } - - private void scaleWithDrawImage (Graphics2D g2, ScalingInfo info, Object hint) { - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint); - - int tempDestWidth = info.srcWidth / 2, tempDestHeight = info.srcHeight / 2; - if (tempDestWidth < info.targetWidth) { - tempDestWidth = info.targetWidth; - } - if (tempDestHeight < info.targetHeight) { - tempDestHeight = info.targetHeight; - } - - // If not doing multipass or multipass only needs a single pass, just scale it once directly to the panel surface. - if (multiPassType == MultipassType.none || (tempDestWidth == info.targetWidth && tempDestHeight == info.targetHeight)) { - g2.drawImage(srcImage, info.x, info.y, info.targetWidth, info.targetHeight, null); - return; - } - - BufferedImage tempImage = new BufferedImage(tempDestWidth, tempDestHeight, BufferedImage.TYPE_INT_RGB); - Graphics2D g2temp = tempImage.createGraphics(); - switch (multiPassType) { - case nearestNeighbor: - g2temp.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); - break; - case bilinear: - g2temp.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - break; - case bicubic: - g2temp.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); - break; - } - // Render first pass from image to temp. - g2temp.drawImage(srcImage, 0, 0, tempDestWidth, tempDestHeight, null); - // Render passes between the first and last pass. - int tempSrcWidth = tempDestWidth; - int tempSrcHeight = tempDestHeight; - while (true) { - if (tempDestWidth > info.targetWidth) { - tempDestWidth = tempDestWidth / 2; - if (tempDestWidth < info.targetWidth) { - tempDestWidth = info.targetWidth; - } - } - - if (tempDestHeight > info.targetHeight) { - tempDestHeight = tempDestHeight / 2; - if (tempDestHeight < info.targetHeight) { - tempDestHeight = info.targetHeight; - } - } - - if (tempDestWidth == info.targetWidth && tempDestHeight == info.targetHeight) { - break; - } - - g2temp.drawImage(tempImage, 0, 0, tempDestWidth, tempDestHeight, 0, 0, tempSrcWidth, tempSrcHeight, null); - - tempSrcWidth = tempDestWidth; - tempSrcHeight = tempDestHeight; - } - g2temp.dispose(); - // Render last pass from temp to panel surface. - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint); - g2.drawImage(tempImage, info.x, info.y, info.x + info.targetWidth, info.y + info.targetHeight, 0, 0, tempSrcWidth, - tempSrcHeight, null); - } - - public Image getSrcImage() { + public BufferedImage getSrcImage() { return srcImage; } - - private static class ScalingInfo { - public int targetWidth; - public int targetHeight; - public int srcWidth; - public int srcHeight; - public int x; - public int y; - } - - public static enum MultipassType { - none, nearestNeighbor, bilinear, bicubic - } - - public static enum ScalingType { - nearestNeighbor, replicate, bilinear, bicubic, areaAveraging - } } diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java index 2b9aeb8fcd0..30b20434c73 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java @@ -284,6 +284,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab if (card.getCardNumber() > 0 && !card.getSetCode().isEmpty() && !ignoreUrls.contains(card.getSetCode())) { String cardName = card.getName(); + boolean isType2 = type2SetsFilter.contains(card.getSetCode()); CardDownloadData url = new CardDownloadData(cardName, card.getSetCode(), card.getCardNumber(), card.usesVariousArt(), 0, "", false, card.isDoubleFaced(), card.isNightCard()); if (url.getUsesVariousArt()) { url.setDownloadName(createDownloadName(card)); @@ -291,10 +292,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab url.setFlipCard(card.isFlipCard()); url.setSplitCard(card.isSplitCard()); - - if (type2SetsFilter.contains(card.getSetCode())) { - url.setType2(true); - } + url.setType2(isType2); allCardsUrls.add(url); if (card.isDoubleFaced()) { @@ -302,6 +300,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab throw new IllegalStateException("Second side card can't have empty name."); } url = new CardDownloadData(card.getSecondSideName(), card.getSetCode(), card.getCardNumber(), card.usesVariousArt(), 0, "", false, card.isDoubleFaced(), true); + url.setType2(isType2); allCardsUrls.add(url); } if (card.isFlipCard()) { @@ -311,6 +310,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab url = new CardDownloadData(card.getFlipCardName(), card.getSetCode(), card.getCardNumber(), card.usesVariousArt(), 0, "", false, card.isDoubleFaced(), card.isNightCard()); url.setFlipCard(true); url.setFlippedSide(true); + url.setType2(isType2); allCardsUrls.add(url); } } else if (card.getCardNumber() < 1) { diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java index 9292cb51e4d..b5fdc3aea08 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java @@ -1,5 +1,6 @@ package org.mage.plugins.card.images; +import mage.client.util.TransformedImageCache; import com.google.common.base.Function; import com.google.common.collect.ComputationException; import com.google.common.collect.MapMaker; @@ -274,7 +275,7 @@ public class ImageCache { } public static BufferedImage makeThumbnail(BufferedImage original, String path) { - BufferedImage image = getResizedImage(original, Constants.THUMBNAIL_SIZE_FULL); + BufferedImage image = TransformedImageCache.getResizedImage(original, Constants.THUMBNAIL_SIZE_FULL.width, Constants.THUMBNAIL_SIZE_FULL.height); TFile imageFile = getTFile(path); if (imageFile == null) { return null; @@ -312,36 +313,7 @@ public class ImageCache { return original; } - ResampleOp resampleOp = new ResampleOp(tgtWidth, tgtHeight); - BufferedImage image = resampleOp.filter(original, null); - return image; - } - - /** - * Returns an image scaled to the size appropriate for the card picture - * panel For future use. - */ - private static BufferedImage getFullSizeImage(BufferedImage original, double scale) { - if (scale == 1) { - return original; - } - ResampleOp resampleOp = new ResampleOp((int) (original.getWidth() * scale), (int) (original.getHeight() * scale)); - BufferedImage image = resampleOp.filter(original, null); - return image; - } - - /** - * Returns an image scaled to the size appropriate for the card picture - * panel - * - * @param original - * @param sizeNeed - * @return - */ - public static BufferedImage getResizedImage(BufferedImage original, Rectangle sizeNeed) { - ResampleOp resampleOp = new ResampleOp(sizeNeed.width, sizeNeed.height); - BufferedImage image = resampleOp.filter(original, null); - return image; + return TransformedImageCache.getResizedImage(original, tgtWidth, tgtHeight); } /** @@ -364,11 +336,11 @@ public class ImageCache { } double scale = Math.min((double) width / original.getWidth(), (double) height / original.getHeight()); - if (scale > 1) { - scale = 1; + if (scale >= 1) { + return original; } - return getFullSizeImage(original, scale); + return TransformedImageCache.getResizedImage(original, (int)(original.getWidth() * scale), (int)(original.getHeight() * scale)); } public static TFile getTFile(String path) { diff --git a/Mage.Common/src/mage/cards/TextPopup.java b/Mage.Common/src/mage/cards/TextPopup.java index e2f1517a59d..e02e9b124c6 100644 --- a/Mage.Common/src/mage/cards/TextPopup.java +++ b/Mage.Common/src/mage/cards/TextPopup.java @@ -39,6 +39,8 @@ package mage.cards; * @author BetaSteward_at_googlemail.com */ public class TextPopup extends javax.swing.JPanel { + private String text; + private boolean needsUpdate; /** Creates new form TextPopup */ public TextPopup() { @@ -46,7 +48,17 @@ public class TextPopup extends javax.swing.JPanel { } public void setText(String text) { - popupText.setText(text); + if(!text.equals(this.text)) { + this.text = text; + this.needsUpdate = true; + } + } + + public void updateText() { + if(this.needsUpdate) { + popupText.setText(this.text); + this.needsUpdate = false; + } } /** This method is called from within the constructor to diff --git a/Mage.Common/src/mage/view/TableView.java b/Mage.Common/src/mage/view/TableView.java index 2b7c711b95b..4d4f7373dde 100644 --- a/Mage.Common/src/mage/view/TableView.java +++ b/Mage.Common/src/mage/view/TableView.java @@ -65,6 +65,7 @@ public class TableView implements Serializable { private final String quitRatio; private final boolean limited; private final boolean rated; + private final boolean passworded; public TableView(Table table) { this.tableId = table.getId(); @@ -94,8 +95,7 @@ public class TableView implements Serializable { if (!table.isTournament()) { // MATCH if (table.getState().equals(TableState.WAITING) || table.getState().equals(TableState.READY_TO_START)) { - tableStateText = table.getState().toString() + " (" + table.getMatch().getPlayers().size() + "/"+ table.getSeats().length + ")" + - (table.getMatch().getOptions().getPassword().isEmpty() ? "":" PW"); + tableStateText = table.getState().toString() + " (" + table.getMatch().getPlayers().size() + "/"+ table.getSeats().length + ")"; } else { tableStateText = table.getState().toString(); } @@ -136,6 +136,7 @@ public class TableView implements Serializable { this.quitRatio = Integer.toString(table.getMatch().getOptions().getQuitRatio()); this.limited = table.getMatch().getOptions().isLimited(); this.rated = table.getMatch().getOptions().isRated(); + this.passworded = !table.getMatch().getOptions().getPassword().isEmpty(); } else { // TOURNAMENT if (table.getTournament().getOptions().getNumberRounds() > 0) { @@ -155,9 +156,6 @@ public class TableView implements Serializable { switch (table.getState()) { case WAITING: stateText.append(" (").append(table.getTournament().getPlayers().size()).append("/").append(table.getNumberOfSeats()).append(")"); - if (!table.getTournament().getOptions().getPassword().isEmpty()) { - stateText.append(" PW"); - } case READY_TO_START: case STARTING: infoText.append(" Time: ").append(table.getTournament().getOptions().getMatchOptions().getMatchTimeLimit().toString()); @@ -185,6 +183,7 @@ public class TableView implements Serializable { this.quitRatio = Integer.toString(table.getTournament().getOptions().getQuitRatio()); this.limited = table.getTournament().getOptions().getMatchOptions().isLimited(); this.rated = table.getTournament().getOptions().getMatchOptions().isRated(); + this.passworded = !table.getTournament().getOptions().getPassword().isEmpty(); } } @@ -250,4 +249,8 @@ public class TableView implements Serializable { public boolean isRated() { return rated; } + + public boolean isPassworded() { + return passworded; + } } 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 781dab6157c..a2a48fb1d61 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 @@ -577,7 +577,8 @@ public class HumanPlayer extends PlayerImpl { return false; } } - if (passedAllTurns) { + + if (passedAllTurns || passedTurnSkipStack) { if (passWithManaPoolCheck(game)) { return false; } @@ -600,7 +601,7 @@ public class HumanPlayer extends PlayerImpl { if (game.getStack().isEmpty()) { passedUntilStackResolved = false; boolean dontCheckPassStep = false; - if (passedTurn) { + if (passedTurn || passedTurnSkipStack) { if (passWithManaPoolCheck(game)) { return false; } @@ -689,7 +690,7 @@ public class HumanPlayer extends PlayerImpl { result = true; } else { Player actingPlayer = null; - if (game.getPriorityPlayerId().equals(playerId)) { + if (playerId.equals(game.getPriorityPlayerId())) { actingPlayer = this; } else if (getPlayersUnderYourControl().contains(game.getPriorityPlayerId())) { actingPlayer = game.getPlayer(game.getPriorityPlayerId()); @@ -714,7 +715,7 @@ public class HumanPlayer extends PlayerImpl { } private boolean checkPassStep(Game game) { - if (game.getActivePlayerId().equals(playerId)) { + if (playerId.equals(game.getActivePlayerId())) { return !this.getUserData().getUserSkipPrioritySteps().getYourTurn().isPhaseStepSet(game.getStep().getType()); } else { return !this.getUserData().getUserSkipPrioritySteps().getOpponentTurn().isPhaseStepSet(game.getStep().getType()); @@ -887,7 +888,7 @@ public class HumanPlayer extends PlayerImpl { filter.add(new ControllerIdPredicate(attackingPlayerId)); while (!abort) { if (passedAllTurns || passedUntilEndStepBeforeMyTurn - || (!getUserData().getUserSkipPrioritySteps().isStopOnDeclareAttackersDuringSkipAction() && (passedTurn || passedUntilEndOfTurn || passedUntilNextMain))) { + || (!getUserData().getUserSkipPrioritySteps().isStopOnDeclareAttackersDuringSkipAction() && (passedTurn || passedTurnSkipStack || passedUntilEndOfTurn || passedUntilNextMain))) { return; } Map options = new HashMap<>(); @@ -1241,16 +1242,16 @@ public class HumanPlayer extends PlayerImpl { return; } } - if (userData.isUseFirstManaAbility() && object instanceof Permanent && object.getCardType().contains(CardType.LAND)){ + if (userData.isUseFirstManaAbility() && object instanceof Permanent && object.getCardType().contains(CardType.LAND)) { ActivatedAbility ability = abilities.values().iterator().next(); if (ability instanceof ManaAbility) { activateAbility(ability, game); return; } - } - + } + game.fireGetChoiceEvent(playerId, name, object, new ArrayList<>(abilities.values())); - + waitForResponse(game); if (response.getUUID() != null && isInGame()) { if (abilities.containsKey(response.getUUID())) { diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/RichManCubeDraftEliminationTournament.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/RichManCubeDraftEliminationTournament.java new file mode 100644 index 00000000000..70c3bdb847e --- /dev/null +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/RichManCubeDraftEliminationTournament.java @@ -0,0 +1,89 @@ +/* + * 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.tournament; + +import mage.constants.TournamentPlayerState; +import mage.game.draft.DraftOptions; +import mage.game.draft.RichManCubeBoosterDraft; +import mage.game.events.TableEvent.EventType; +import mage.game.tournament.TournamentOptions; +import mage.game.tournament.TournamentPlayer; +import mage.game.tournament.TournamentSingleElimination; + +/** + * + * @author spjspj + */ +public class RichManCubeDraftEliminationTournament extends TournamentSingleElimination { + + protected enum TournamentStep { + START, DRAFT, CONSTRUCT, COMPETE, WINNERS + } + + protected TournamentStep currentStep; + + public RichManCubeDraftEliminationTournament(TournamentOptions options) { + super(options); + currentStep = TournamentStep.START; + } + + protected void draft() { + draft = new RichManCubeBoosterDraft((DraftOptions) options.getLimitedOptions(), getSets()); + for (TournamentPlayer player : players.values()) { + draft.addPlayer(player.getPlayer()); + player.setState(TournamentPlayerState.DRAFTING); + } + tableEventSource.fireTableEvent(EventType.START_DRAFT, null, draft); + } + + @Override + public void nextStep() { + if (isAbort()) { + currentStep = TournamentStep.COMPETE; + } + switch (currentStep) { + case START: + currentStep = TournamentStep.DRAFT; + draft(); + break; + case DRAFT: + currentStep = TournamentStep.CONSTRUCT; + construct(); + break; + case CONSTRUCT: + currentStep = TournamentStep.COMPETE; + runTournament(); + break; + case COMPETE: + currentStep = TournamentStep.WINNERS; + winners(); + end(); + break; + } + } +} diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/RichManCubeDraftEliminationTournamentType.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/RichManCubeDraftEliminationTournamentType.java new file mode 100644 index 00000000000..b8ce6938de5 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/RichManCubeDraftEliminationTournamentType.java @@ -0,0 +1,50 @@ +/* + * 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.tournament; + +import mage.game.tournament.TournamentType; + +/** + * + * @author spjspj + */ +public class RichManCubeDraftEliminationTournamentType extends TournamentType { + + public RichManCubeDraftEliminationTournamentType() { + this.name = "Booster Draft Elimination (Rich Man Cube)"; + this.maxPlayers = 16; + this.minPlayers = 2; + this.numBoosters = 1; + this.draft = true; + this.limited = true; + this.cubeBooster = true; + this.elimination = true; + } + +} diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/AdamStyborskisPauperCube.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/AdamStyborskisPauperCube.java index 654f7aada6b..85ead02b474 100644 --- a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/AdamStyborskisPauperCube.java +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/AdamStyborskisPauperCube.java @@ -34,7 +34,7 @@ import mage.game.draft.DraftCube; * @author fireshoes */ public class AdamStyborskisPauperCube extends DraftCube { - + public AdamStyborskisPauperCube() { super("Adam Styborkski's Cube (411 cards)"); // https://docs.google.com/spreadsheets/d/12iQhC4bHqFW7hEWxPBjyC8yBDehFZ0_4DkqzyA8EL3o/edit#gid=0 @@ -44,6 +44,7 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("AEther Adept", "")); cubeCards.add(new CardIdentity("AEthersnipe", "")); cubeCards.add(new CardIdentity("Agony Warp", "")); + cubeCards.add(new CardIdentity("Aim High", "")); cubeCards.add(new CardIdentity("Ambush Viper", "")); cubeCards.add(new CardIdentity("Apex Hawks", "")); cubeCards.add(new CardIdentity("Arachnus Web", "")); @@ -67,7 +68,6 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Beetleback Chief", "")); cubeCards.add(new CardIdentity("Beetleform Mage", "")); cubeCards.add(new CardIdentity("Benthic Infiltrator", "")); - cubeCards.add(new CardIdentity("Blade of the Sixth Pride", "")); cubeCards.add(new CardIdentity("Blastoderm", "")); cubeCards.add(new CardIdentity("Blazing Torch", "")); cubeCards.add(new CardIdentity("Blightning", "")); @@ -81,10 +81,9 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Boros Guildgate", "")); cubeCards.add(new CardIdentity("Branching Bolt", "")); cubeCards.add(new CardIdentity("Brute Force", "")); - cubeCards.add(new CardIdentity("Brute Strength", "")); cubeCards.add(new CardIdentity("Burst Lightning", "")); cubeCards.add(new CardIdentity("Butcher Ghoul", "")); - cubeCards.add(new CardIdentity("Cadaver Imp", "")); + cubeCards.add(new CardIdentity("Byway Courier", "")); cubeCards.add(new CardIdentity("Cage of Hands", "")); cubeCards.add(new CardIdentity("Calcite Snapper", "")); cubeCards.add(new CardIdentity("Capsize", "")); @@ -93,6 +92,7 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Cavern Harpy", "")); cubeCards.add(new CardIdentity("Centaur Healer", "")); cubeCards.add(new CardIdentity("Center Soul", "")); + cubeCards.add(new CardIdentity("Cephalid Sage", "")); cubeCards.add(new CardIdentity("Chain Lightning", "")); cubeCards.add(new CardIdentity("Chainer's Edict", "")); cubeCards.add(new CardIdentity("Citanul Woodreaders", "")); @@ -105,14 +105,13 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Coalition Honor Guard", "")); cubeCards.add(new CardIdentity("Cogwork Librarian", "")); cubeCards.add(new CardIdentity("Colossal Might", "")); - cubeCards.add(new CardIdentity("Comparative Analysis", "")); cubeCards.add(new CardIdentity("Compulsive Research", "")); cubeCards.add(new CardIdentity("Consume Strength", "")); cubeCards.add(new CardIdentity("Corpse Churn", "")); cubeCards.add(new CardIdentity("Corrupted Zendikon", "")); cubeCards.add(new CardIdentity("Counterspell", "")); cubeCards.add(new CardIdentity("Crippling Fatigue", "")); - cubeCards.add(new CardIdentity("Crocanura", "")); + cubeCards.add(new CardIdentity("Crow of Dark Tidings", "")); cubeCards.add(new CardIdentity("Crypt Rats", "")); cubeCards.add(new CardIdentity("Crystallization", "")); cubeCards.add(new CardIdentity("Cultivate", "")); @@ -120,8 +119,7 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Curse of Chains", "")); cubeCards.add(new CardIdentity("Custodi Squire", "")); cubeCards.add(new CardIdentity("Daring Skyjek", "")); - cubeCards.add(new CardIdentity("Daru Lancer", "")); - cubeCards.add(new CardIdentity("Dauthi Slayer", "")); + cubeCards.add(new CardIdentity("Dauntless Cathar", "")); cubeCards.add(new CardIdentity("Dead Reveler", "")); cubeCards.add(new CardIdentity("Dead Weight", "")); cubeCards.add(new CardIdentity("Death Denied", "")); @@ -145,18 +143,20 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Eldrazi Skyspawner", "")); cubeCards.add(new CardIdentity("Elephant Ambush", "")); cubeCards.add(new CardIdentity("Elephant Guide", "")); + cubeCards.add(new CardIdentity("Elite Vanguard", "")); + cubeCards.add(new CardIdentity("Emperor Crocodile", "")); cubeCards.add(new CardIdentity("Epic Confrontation", "")); cubeCards.add(new CardIdentity("Errant Ephemeron", "")); cubeCards.add(new CardIdentity("Esper Cormorants", "")); cubeCards.add(new CardIdentity("Essence Scatter", "")); cubeCards.add(new CardIdentity("Evincar's Justice", "")); - cubeCards.add(new CardIdentity("Evolution Charm", "")); cubeCards.add(new CardIdentity("Evolving Wilds", "")); cubeCards.add(new CardIdentity("Faceless Butcher", "")); cubeCards.add(new CardIdentity("Faith's Fetters", "")); cubeCards.add(new CardIdentity("Fall of the Hammer", "")); cubeCards.add(new CardIdentity("Feeling of Dread", "")); cubeCards.add(new CardIdentity("Fellwar Stone", "")); + cubeCards.add(new CardIdentity("Fertile Thicket", "")); cubeCards.add(new CardIdentity("Fervent Cathar", "")); cubeCards.add(new CardIdentity("Firebolt", "")); cubeCards.add(new CardIdentity("Firefiend Elemental", "")); @@ -165,7 +165,6 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Flayer Husk", "")); cubeCards.add(new CardIdentity("Flurry of Horns", "")); cubeCards.add(new CardIdentity("Fortify", "")); - cubeCards.add(new CardIdentity("Foul Spirit", "")); cubeCards.add(new CardIdentity("Frilled Oculus", "")); cubeCards.add(new CardIdentity("Frostburn Weird", "")); cubeCards.add(new CardIdentity("Fyndhorn Elves", "")); @@ -175,9 +174,9 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Ghastly Demise", "")); cubeCards.add(new CardIdentity("Ghirapur Gearcrafter", "")); cubeCards.add(new CardIdentity("Ghitu Slinger", "")); + cubeCards.add(new CardIdentity("Ghoulcaller's Accomplice", "")); cubeCards.add(new CardIdentity("Giant Growth", "")); cubeCards.add(new CardIdentity("Gideon's Lawkeeper", "")); - cubeCards.add(new CardIdentity("Gideon's Reproach", "")); cubeCards.add(new CardIdentity("Goblin Freerunner", "")); cubeCards.add(new CardIdentity("Goblin Heelcutter", "")); cubeCards.add(new CardIdentity("Gods Willing", "")); @@ -195,15 +194,16 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Gurmag Angler", "")); cubeCards.add(new CardIdentity("Halimar Depths", "")); cubeCards.add(new CardIdentity("Halimar Wavewatch", "")); - cubeCards.add(new CardIdentity("Hand of Silumgar", "")); cubeCards.add(new CardIdentity("Harrow", "")); cubeCards.add(new CardIdentity("Harsh Sustenance", "")); cubeCards.add(new CardIdentity("Hissing Iguanar", "")); cubeCards.add(new CardIdentity("Hooting Mandrills", "")); + cubeCards.add(new CardIdentity("Humble", "")); cubeCards.add(new CardIdentity("Hymn to Tourach", "")); cubeCards.add(new CardIdentity("Imperiosaur", "")); cubeCards.add(new CardIdentity("Incinerate", "")); cubeCards.add(new CardIdentity("Inner-Flame Acolyte", "")); + cubeCards.add(new CardIdentity("Insolent Neonate", "")); cubeCards.add(new CardIdentity("Into the Roil", "")); cubeCards.add(new CardIdentity("Isolation Zone", "")); cubeCards.add(new CardIdentity("Izzet Chronarch", "")); @@ -211,7 +211,6 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Jilt", "")); cubeCards.add(new CardIdentity("Journey to Nowhere", "")); cubeCards.add(new CardIdentity("Jungle Hollow", "")); - cubeCards.add(new CardIdentity("Jwar Isle Avenger", "")); cubeCards.add(new CardIdentity("Kabuto Moth", "")); cubeCards.add(new CardIdentity("Keldon Marauders", "")); cubeCards.add(new CardIdentity("Khalni Garden", "")); @@ -231,6 +230,7 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Liliana's Specter", "")); cubeCards.add(new CardIdentity("Llanowar Elves", "")); cubeCards.add(new CardIdentity("Lone Missionary", "")); + cubeCards.add(new CardIdentity("Looming Spires", "")); cubeCards.add(new CardIdentity("Lotus Path Djinn", "")); cubeCards.add(new CardIdentity("Loyal Cathar", "")); cubeCards.add(new CardIdentity("Loyal Pegasus", "")); @@ -239,9 +239,7 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Mana Leak", "")); cubeCards.add(new CardIdentity("Man-o'-War", "")); cubeCards.add(new CardIdentity("Mardu Hordechief", "")); - cubeCards.add(new CardIdentity("Mardu Scout", "")); cubeCards.add(new CardIdentity("Mardu Skullhunter", "")); - cubeCards.add(new CardIdentity("Martial Glory", "")); cubeCards.add(new CardIdentity("Maul Splicer", "")); cubeCards.add(new CardIdentity("Maze of Ith", "")); cubeCards.add(new CardIdentity("Mind Stone", "")); @@ -250,30 +248,34 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Miscalculation", "")); cubeCards.add(new CardIdentity("Mishra's Factory", "")); cubeCards.add(new CardIdentity("Mist Raven", "")); - cubeCards.add(new CardIdentity("Misthoof Kirin", "")); + cubeCards.add(new CardIdentity("Mistral Charger", "")); cubeCards.add(new CardIdentity("Mogg Fanatic", "")); cubeCards.add(new CardIdentity("Mogg Flunkies", "")); cubeCards.add(new CardIdentity("Mogg War Marshal", "")); cubeCards.add(new CardIdentity("Moldervine Cloak", "")); cubeCards.add(new CardIdentity("Momentary Blink", "")); cubeCards.add(new CardIdentity("Morgue Theft", "")); + cubeCards.add(new CardIdentity("Mortuary Mire", "")); cubeCards.add(new CardIdentity("Mulldrifter", "")); cubeCards.add(new CardIdentity("Nameless Inversion", "")); cubeCards.add(new CardIdentity("Narcolepsy", "")); - cubeCards.add(new CardIdentity("Natural Connection", "")); cubeCards.add(new CardIdentity("Necromancer's Assistant", "")); cubeCards.add(new CardIdentity("Nessian Asp", "")); cubeCards.add(new CardIdentity("Nest Invader", "")); cubeCards.add(new CardIdentity("Nezumi Cutthroat", "")); + cubeCards.add(new CardIdentity("Night's Whisper", "")); cubeCards.add(new CardIdentity("Nightscape Familiar", "")); cubeCards.add(new CardIdentity("Ninja of the Deep Hours", "")); cubeCards.add(new CardIdentity("Oblivion Ring", "")); cubeCards.add(new CardIdentity("Okiba-Gang Shinobi", "")); + cubeCards.add(new CardIdentity("Oona's Grace", "")); + cubeCards.add(new CardIdentity("Orcish Oriflamme", "")); cubeCards.add(new CardIdentity("Orzhov Guildgate", "")); cubeCards.add(new CardIdentity("Otherworldly Journey", "")); cubeCards.add(new CardIdentity("Pacifism", "")); cubeCards.add(new CardIdentity("Path of Anger's Flame", "")); cubeCards.add(new CardIdentity("Penumbra Spider", "")); + cubeCards.add(new CardIdentity("Peregrine Drake", "")); cubeCards.add(new CardIdentity("Perilous Myr", "")); cubeCards.add(new CardIdentity("Pestermite", "")); cubeCards.add(new CardIdentity("Pestilence", "")); @@ -303,7 +305,9 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Raise the Alarm", "")); cubeCards.add(new CardIdentity("Rakdos Guildgate", "")); cubeCards.add(new CardIdentity("Rakdos Shred-Freak", "")); + cubeCards.add(new CardIdentity("Rally the Peasants", "")); cubeCards.add(new CardIdentity("Rampant Growth", "")); + cubeCards.add(new CardIdentity("Rancid Rats", "")); cubeCards.add(new CardIdentity("Rancor", "")); cubeCards.add(new CardIdentity("Ranger's Guile", "")); cubeCards.add(new CardIdentity("Ray of Command", "")); @@ -318,11 +322,12 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Ronin Houndmaster", "")); cubeCards.add(new CardIdentity("Rugged Highlands", "")); cubeCards.add(new CardIdentity("Runed Servitor", "")); + cubeCards.add(new CardIdentity("Rush of Adrenaline", "")); cubeCards.add(new CardIdentity("Rushing River", "")); - cubeCards.add(new CardIdentity("Saddleback Lagac", "")); cubeCards.add(new CardIdentity("Safehold Elite", "")); cubeCards.add(new CardIdentity("Sakura-Tribe Elder", "")); cubeCards.add(new CardIdentity("Sandsteppe Outcast", "")); + cubeCards.add(new CardIdentity("Sandstone Bridge", "")); cubeCards.add(new CardIdentity("Sangrite Backlash", "")); cubeCards.add(new CardIdentity("Sarkhan's Rage", "")); cubeCards.add(new CardIdentity("Savage Punch", "")); @@ -340,7 +345,6 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Separatist Voidmage", "")); cubeCards.add(new CardIdentity("Seraph of Dawn", "")); cubeCards.add(new CardIdentity("Serrated Arrows", "")); - cubeCards.add(new CardIdentity("Shadow Glider", "")); cubeCards.add(new CardIdentity("Shaper Parasite", "")); cubeCards.add(new CardIdentity("Sheer Drop", "")); cubeCards.add(new CardIdentity("Shelter", "")); @@ -351,6 +355,7 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Skinthinner", "")); cubeCards.add(new CardIdentity("Skirk Marauder", "")); cubeCards.add(new CardIdentity("Skyknight Legionnaire", "")); + cubeCards.add(new CardIdentity("Skyline Cascade", "")); cubeCards.add(new CardIdentity("Slash Panther", "")); cubeCards.add(new CardIdentity("Slippery Bogle", "")); cubeCards.add(new CardIdentity("Snakeform", "")); @@ -367,14 +372,13 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Stormscape Apprentice", "")); cubeCards.add(new CardIdentity("Strider Harness", "")); cubeCards.add(new CardIdentity("Strip Mine", "")); - cubeCards.add(new CardIdentity("Sultai Emissary", "")); cubeCards.add(new CardIdentity("Sultai Scavenger", "")); cubeCards.add(new CardIdentity("Suppression Bonds", "")); cubeCards.add(new CardIdentity("Suq'Ata Lancer", "")); cubeCards.add(new CardIdentity("Sweep Away", "")); cubeCards.add(new CardIdentity("Swiftwater Cliffs", "")); + cubeCards.add(new CardIdentity("Sylvan Might", "")); cubeCards.add(new CardIdentity("Sylvok Lifestaff", "")); - cubeCards.add(new CardIdentity("Tail Slash", "")); cubeCards.add(new CardIdentity("Tajuru Pathwarden", "")); cubeCards.add(new CardIdentity("Teetering Peaks", "")); cubeCards.add(new CardIdentity("Temporal Isolation", "")); @@ -384,6 +388,7 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Test of Faith", "")); cubeCards.add(new CardIdentity("Thornweald Archer", "")); cubeCards.add(new CardIdentity("Thornwood Falls", "")); + cubeCards.add(new CardIdentity("Thraben Inspector", "")); cubeCards.add(new CardIdentity("Thundering Giant", "")); cubeCards.add(new CardIdentity("Thundering Tanadon", "")); cubeCards.add(new CardIdentity("Time to Feed", "")); @@ -394,13 +399,11 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Travel Preparations", "")); cubeCards.add(new CardIdentity("Treasure Cruise", "")); cubeCards.add(new CardIdentity("Triplicate Spirits", "")); - cubeCards.add(new CardIdentity("Trumpet Blast", "")); cubeCards.add(new CardIdentity("Tumble Magnet", "")); - cubeCards.add(new CardIdentity("Twin Bolt", "")); cubeCards.add(new CardIdentity("Typhoid Rats", "")); cubeCards.add(new CardIdentity("Ulamog's Crusher", "")); - cubeCards.add(new CardIdentity("Umara Entangler", "")); cubeCards.add(new CardIdentity("Undying Evil", "")); + cubeCards.add(new CardIdentity("Undying Rage", "")); cubeCards.add(new CardIdentity("Unearth", "")); cubeCards.add(new CardIdentity("Unmake", "")); cubeCards.add(new CardIdentity("Unnatural Aggression", "")); @@ -410,6 +413,7 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Viashino Firstblade", "")); cubeCards.add(new CardIdentity("Vines of Vastwood", "")); cubeCards.add(new CardIdentity("Voidwielder", "")); + cubeCards.add(new CardIdentity("Voldaren Duelist", "")); cubeCards.add(new CardIdentity("Vulshok Morningstar", "")); cubeCards.add(new CardIdentity("Vulshok Sorcerer", "")); cubeCards.add(new CardIdentity("Vulturous Aven", "")); @@ -417,8 +421,9 @@ public AdamStyborskisPauperCube() { cubeCards.add(new CardIdentity("Walker of the Grove", "")); cubeCards.add(new CardIdentity("Wall of Roots", "")); cubeCards.add(new CardIdentity("War Flare", "")); + cubeCards.add(new CardIdentity("Warden of Evos Isle", "")); + cubeCards.add(new CardIdentity("Warped Landscape", "")); cubeCards.add(new CardIdentity("Warren Pilferers", "")); - cubeCards.add(new CardIdentity("Waterfront Bouncer", "")); cubeCards.add(new CardIdentity("Wayfarer's Bauble", "")); cubeCards.add(new CardIdentity("Whirlpool Whelm", "")); cubeCards.add(new CardIdentity("Whispers of the Muse", "")); diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/JimDavisCube.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/JimDavisCube.java index 83dbff1af76..e68e291245a 100644 --- a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/JimDavisCube.java +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/JimDavisCube.java @@ -49,7 +49,6 @@ public class JimDavisCube extends DraftCube { cubeCards.add(new CardIdentity("Ajani Vengeant", "")); cubeCards.add(new CardIdentity("Ancient Den", "")); cubeCards.add(new CardIdentity("Ancient Tomb", "")); - cubeCards.add(new CardIdentity("Angel of Serenity", "")); cubeCards.add(new CardIdentity("Animate Dead", "")); cubeCards.add(new CardIdentity("Ankh of Mishra", "")); cubeCards.add(new CardIdentity("Arcbound Ravager", "")); @@ -85,6 +84,7 @@ public class JimDavisCube extends DraftCube { cubeCards.add(new CardIdentity("Bonesplitter", "")); cubeCards.add(new CardIdentity("Boros Charm", "")); cubeCards.add(new CardIdentity("Boros Reckoner", "")); + cubeCards.add(new CardIdentity("Bounding Krasis", "")); cubeCards.add(new CardIdentity("Braids, Cabal Minion", "")); cubeCards.add(new CardIdentity("Brain Freeze", "")); cubeCards.add(new CardIdentity("Brainstorm", "")); @@ -100,6 +100,7 @@ public class JimDavisCube extends DraftCube { cubeCards.add(new CardIdentity("Chained to the Rocks", "")); cubeCards.add(new CardIdentity("Chain Lightning", "")); cubeCards.add(new CardIdentity("Champion of the Parish", "")); + cubeCards.add(new CardIdentity("Chandra, Flamecaller", "")); cubeCards.add(new CardIdentity("Chandra's Phoenix", "")); cubeCards.add(new CardIdentity("Chromatic Sphere", "")); cubeCards.add(new CardIdentity("Chromatic Star", "")); @@ -115,7 +116,6 @@ public class JimDavisCube extends DraftCube { cubeCards.add(new CardIdentity("Copperline Gorge", "")); cubeCards.add(new CardIdentity("Council's Judgment", "")); cubeCards.add(new CardIdentity("Counterspell", "")); - cubeCards.add(new CardIdentity("Court Hussar", "")); cubeCards.add(new CardIdentity("Cranial Plating", "")); cubeCards.add(new CardIdentity("Creeping Tar Pit", "")); cubeCards.add(new CardIdentity("Crop Rotation", "")); @@ -132,9 +132,9 @@ public class JimDavisCube extends DraftCube { cubeCards.add(new CardIdentity("Daze", "")); cubeCards.add(new CardIdentity("Deal Broker", "")); cubeCards.add(new CardIdentity("Deathrite Shaman", "")); + cubeCards.add(new CardIdentity("Declaration in Stone", "")); cubeCards.add(new CardIdentity("Delver of Secrets", "")); cubeCards.add(new CardIdentity("Demonic Tutor", "")); - cubeCards.add(new CardIdentity("Deranged Hermit", "")); cubeCards.add(new CardIdentity("Detention Sphere", "")); cubeCards.add(new CardIdentity("Dig Through Time", "")); cubeCards.add(new CardIdentity("Dimir Signet", "")); @@ -166,6 +166,7 @@ public class JimDavisCube extends DraftCube { cubeCards.add(new CardIdentity("Fact or Fiction", "")); cubeCards.add(new CardIdentity("Faithless Looting", "")); cubeCards.add(new CardIdentity("Falkenrath Aristocrat", "")); + cubeCards.add(new CardIdentity("Falkenrath Gorger", "")); cubeCards.add(new CardIdentity("Far // Away", "")); cubeCards.add(new CardIdentity("Fastbond", "")); cubeCards.add(new CardIdentity("Fauna Shaman", "")); @@ -173,7 +174,6 @@ public class JimDavisCube extends DraftCube { cubeCards.add(new CardIdentity("Fire // Ice", "")); cubeCards.add(new CardIdentity("Fireblast", "")); cubeCards.add(new CardIdentity("Firebolt", "")); - cubeCards.add(new CardIdentity("Firedrinker Satyr", "")); cubeCards.add(new CardIdentity("Firestorm", "")); cubeCards.add(new CardIdentity("Flagstones of Trokair", "")); cubeCards.add(new CardIdentity("Flametongue Kavu", "")); @@ -192,17 +192,15 @@ public class JimDavisCube extends DraftCube { cubeCards.add(new CardIdentity("Garruk Wildspeaker", "")); cubeCards.add(new CardIdentity("Geist of Saint Traft", "")); cubeCards.add(new CardIdentity("Gemstone Mine", "")); - cubeCards.add(new CardIdentity("Genesis Hydra", "")); cubeCards.add(new CardIdentity("Ghitu Encampment", "")); cubeCards.add(new CardIdentity("Ghor-Clan Rampager", "")); cubeCards.add(new CardIdentity("Gideon, Ally of Zendikar", "")); cubeCards.add(new CardIdentity("Gifts Ungiven", "")); cubeCards.add(new CardIdentity("Gilded Lotus", "")); cubeCards.add(new CardIdentity("Gitaxian Probe", "")); - cubeCards.add(new CardIdentity("Glint Hawk Idol", "")); cubeCards.add(new CardIdentity("Gnarled Scarhide", "")); cubeCards.add(new CardIdentity("Go for the Throat", "")); - cubeCards.add(new CardIdentity("Goblin Electromancer", "")); + cubeCards.add(new CardIdentity("Goblin Dark-Dwellers", "")); cubeCards.add(new CardIdentity("Goblin Guide", "")); cubeCards.add(new CardIdentity("Goblin Rabblemaster", "")); cubeCards.add(new CardIdentity("Goblin Welder", "")); @@ -244,7 +242,7 @@ public class JimDavisCube extends DraftCube { cubeCards.add(new CardIdentity("Jace, Vryn's Prodigy", "")); cubeCards.add(new CardIdentity("Jeskai Ascendancy", "")); cubeCards.add(new CardIdentity("Joraga Treespeaker", "")); - cubeCards.add(new CardIdentity("Kami of Ancient Law", "")); + cubeCards.add(new CardIdentity("Jori En, Ruin Diver", "")); cubeCards.add(new CardIdentity("Karakas", "")); cubeCards.add(new CardIdentity("Karn Liberated", "")); cubeCards.add(new CardIdentity("Keranos, God of Storms", "")); @@ -339,7 +337,6 @@ public class JimDavisCube extends DraftCube { cubeCards.add(new CardIdentity("Preordain", "")); cubeCards.add(new CardIdentity("Primeval Titan", "")); cubeCards.add(new CardIdentity("Prismatic Lens", "")); - cubeCards.add(new CardIdentity("Prophetic Bolt", "")); cubeCards.add(new CardIdentity("Putrid Imp", "")); cubeCards.add(new CardIdentity("Qasali Pridemage", "")); cubeCards.add(new CardIdentity("Rakdos Cackler", "")); @@ -350,9 +347,9 @@ public class JimDavisCube extends DraftCube { cubeCards.add(new CardIdentity("Razorverge Thicket", "")); cubeCards.add(new CardIdentity("Reanimate", "")); cubeCards.add(new CardIdentity("Reckless Charge", "")); - cubeCards.add(new CardIdentity("Reckless Waif", "")); cubeCards.add(new CardIdentity("Reclamation Sage", "")); cubeCards.add(new CardIdentity("Recurring Nightmare", "")); + cubeCards.add(new CardIdentity("Reflector Mage", "")); cubeCards.add(new CardIdentity("Regrowth", "")); cubeCards.add(new CardIdentity("Relic Seeker", "")); cubeCards.add(new CardIdentity("Remand", "")); @@ -363,7 +360,6 @@ public class JimDavisCube extends DraftCube { cubeCards.add(new CardIdentity("Rude Awakening", "")); cubeCards.add(new CardIdentity("Sacred Foundry", "")); cubeCards.add(new CardIdentity("Sakura-Tribe Elder", "")); - cubeCards.add(new CardIdentity("Sarkhan, the Dragonspeaker", "")); cubeCards.add(new CardIdentity("Savannah", "")); cubeCards.add(new CardIdentity("Scalding Tarn", "")); cubeCards.add(new CardIdentity("Scavenging Ooze", "")); @@ -374,7 +370,6 @@ public class JimDavisCube extends DraftCube { cubeCards.add(new CardIdentity("Searing Blaze", "")); cubeCards.add(new CardIdentity("Seat of the Synod", "")); cubeCards.add(new CardIdentity("Seeker of the Way", "")); - cubeCards.add(new CardIdentity("Seething Song", "")); cubeCards.add(new CardIdentity("Sensei's Divining Top", "")); cubeCards.add(new CardIdentity("Shadowmage Infiltrator", "")); cubeCards.add(new CardIdentity("Shambling Vent", "")); @@ -386,7 +381,6 @@ public class JimDavisCube extends DraftCube { cubeCards.add(new CardIdentity("Signal Pest", "")); cubeCards.add(new CardIdentity("Simic Growth Chamber", "")); cubeCards.add(new CardIdentity("Simic Signet", "")); - cubeCards.add(new CardIdentity("Simic Sky Swallower", "")); cubeCards.add(new CardIdentity("Sinkhole", "")); cubeCards.add(new CardIdentity("Skullclamp", "")); cubeCards.add(new CardIdentity("Skyshroud Elite", "")); @@ -410,11 +404,10 @@ public class JimDavisCube extends DraftCube { cubeCards.add(new CardIdentity("Stoke the Flames", "")); cubeCards.add(new CardIdentity("Stomping Ground", "")); cubeCards.add(new CardIdentity("Stoneforge Mystic", "")); + cubeCards.add(new CardIdentity("Stormchaser Mage", "")); cubeCards.add(new CardIdentity("Strangleroot Geist", "")); - cubeCards.add(new CardIdentity("Stratus Dancer", "")); cubeCards.add(new CardIdentity("Strip Mine", "")); cubeCards.add(new CardIdentity("Stromkirk Noble", "")); - cubeCards.add(new CardIdentity("Student of Warfare", "")); cubeCards.add(new CardIdentity("Sudden Demise", "")); cubeCards.add(new CardIdentity("Sudden Shock", "")); cubeCards.add(new CardIdentity("Sulfur Falls", "")); @@ -439,12 +432,15 @@ public class JimDavisCube extends DraftCube { cubeCards.add(new CardIdentity("Terminate", "")); cubeCards.add(new CardIdentity("Tezzeret, Agent of Bolas", "")); cubeCards.add(new CardIdentity("Thalia, Guardian of Thraben", "")); + cubeCards.add(new CardIdentity("Thalia's Lieutenant", "")); cubeCards.add(new CardIdentity("The Abyss", "")); cubeCards.add(new CardIdentity("The Tabernacle at Pendrell Vale", "")); + cubeCards.add(new CardIdentity("Thing in the Ice", "")); cubeCards.add(new CardIdentity("Thirst for Knowledge", "")); cubeCards.add(new CardIdentity("Thopter Engineer", "")); cubeCards.add(new CardIdentity("Thopter Spy Network", "")); cubeCards.add(new CardIdentity("Thoughtseize", "")); + cubeCards.add(new CardIdentity("Thraben Inspector", "")); cubeCards.add(new CardIdentity("Thragtusk", "")); cubeCards.add(new CardIdentity("Thran Dynamo", "")); cubeCards.add(new CardIdentity("Tidehollow Sculler", "")); @@ -452,10 +448,13 @@ public class JimDavisCube extends DraftCube { cubeCards.add(new CardIdentity("Timetwister", "")); cubeCards.add(new CardIdentity("Tin Street Hooligan", "")); cubeCards.add(new CardIdentity("Tinker", "")); + cubeCards.add(new CardIdentity("Tireless Tracker", "")); cubeCards.add(new CardIdentity("Tolarian Academy", "")); cubeCards.add(new CardIdentity("Tooth and Nail", "")); + cubeCards.add(new CardIdentity("Topplegeist", "")); cubeCards.add(new CardIdentity("Tormented Hero", "")); cubeCards.add(new CardIdentity("Tranquil Thicket", "")); + cubeCards.add(new CardIdentity("Traverse the Ulvenwald", "")); cubeCards.add(new CardIdentity("Treachery", "")); cubeCards.add(new CardIdentity("Treasure Cruise", "")); cubeCards.add(new CardIdentity("Tree of Tales", "")); @@ -480,6 +479,7 @@ public class JimDavisCube extends DraftCube { cubeCards.add(new CardIdentity("Vengevine", "")); cubeCards.add(new CardIdentity("Venser, Shaper Savant", "")); cubeCards.add(new CardIdentity("Verdant Catacombs", "")); + cubeCards.add(new CardIdentity("Village Messenger", "")); cubeCards.add(new CardIdentity("Vindicate", "")); cubeCards.add(new CardIdentity("Volcanic Island", "")); cubeCards.add(new CardIdentity("Volrath's Stronghold", "")); diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCubeJune2016.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCubeJune2016.java new file mode 100644 index 00000000000..8b5c21bec5d --- /dev/null +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCubeJune2016.java @@ -0,0 +1,583 @@ +/* + * 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.tournament.cubes; + +import mage.game.draft.DraftCube; + +/** + * + * @author fireshoes + */ +public class VintageCubeJune2016 extends DraftCube { + + public VintageCubeJune2016() { + super("MTGO Vintage Cube June 2016"); + + cubeCards.add(new DraftCube.CardIdentity("Abbot of Keral Keep", "")); + cubeCards.add(new DraftCube.CardIdentity("Abrupt Decay", "")); + cubeCards.add(new DraftCube.CardIdentity("Academy Rector", "")); + cubeCards.add(new DraftCube.CardIdentity("Academy Ruins", "")); + cubeCards.add(new DraftCube.CardIdentity("Acidic Slime", "")); + cubeCards.add(new DraftCube.CardIdentity("Ajani Vengeant", "")); + cubeCards.add(new DraftCube.CardIdentity("Ancestral Recall", "")); + cubeCards.add(new DraftCube.CardIdentity("Ancestral Vision", "")); + cubeCards.add(new DraftCube.CardIdentity("Ancient Grudge", "")); + cubeCards.add(new DraftCube.CardIdentity("Ancient Tomb", "")); + cubeCards.add(new DraftCube.CardIdentity("Angel of Serenity", "")); + cubeCards.add(new DraftCube.CardIdentity("Anguished Unmaking", "")); + cubeCards.add(new DraftCube.CardIdentity("Animate Dead", "")); + cubeCards.add(new DraftCube.CardIdentity("Anticipate", "")); + cubeCards.add(new DraftCube.CardIdentity("Arbor Elf", "")); + cubeCards.add(new DraftCube.CardIdentity("Archangel of Thune", "")); + cubeCards.add(new DraftCube.CardIdentity("Arid Mesa", "")); + cubeCards.add(new DraftCube.CardIdentity("Armageddon", "")); + cubeCards.add(new DraftCube.CardIdentity("Ashiok, Nightmare Weaver", "")); + cubeCards.add(new DraftCube.CardIdentity("Avacyn's Pilgrim", "")); + cubeCards.add(new DraftCube.CardIdentity("Avalanche Riders", "")); + cubeCards.add(new DraftCube.CardIdentity("Avenger of Zendikar", "")); + cubeCards.add(new DraftCube.CardIdentity("Awakening Zone", "")); + cubeCards.add(new DraftCube.CardIdentity("Azorius Signet", "")); + cubeCards.add(new DraftCube.CardIdentity("Badlands", "")); + cubeCards.add(new DraftCube.CardIdentity("Balance", "")); + cubeCards.add(new DraftCube.CardIdentity("Baleful Strix", "")); + cubeCards.add(new DraftCube.CardIdentity("Baneslayer Angel", "")); + cubeCards.add(new DraftCube.CardIdentity("Banisher Priest", "")); + cubeCards.add(new DraftCube.CardIdentity("Banishing Light", "")); + cubeCards.add(new DraftCube.CardIdentity("Basalt Monolith", "")); + cubeCards.add(new DraftCube.CardIdentity("Batterskull", "")); + cubeCards.add(new DraftCube.CardIdentity("Bayou", "")); + cubeCards.add(new DraftCube.CardIdentity("Bazaar of Baghdad", "")); + cubeCards.add(new DraftCube.CardIdentity("Beast Within", "")); + cubeCards.add(new DraftCube.CardIdentity("Birds of Paradise", "")); + cubeCards.add(new DraftCube.CardIdentity("Birthing Pod", "")); + cubeCards.add(new DraftCube.CardIdentity("Bitterblossom", "")); + cubeCards.add(new DraftCube.CardIdentity("Black Lotus", "")); + cubeCards.add(new DraftCube.CardIdentity("Blade Splicer", "")); + cubeCards.add(new DraftCube.CardIdentity("Blightsteel Colossus", "")); + cubeCards.add(new DraftCube.CardIdentity("Blood Crypt", "")); + cubeCards.add(new DraftCube.CardIdentity("Bloodbraid Elf", "")); + cubeCards.add(new DraftCube.CardIdentity("Bloodstained Mire", "")); + cubeCards.add(new DraftCube.CardIdentity("Bone Shredder", "")); + cubeCards.add(new DraftCube.CardIdentity("Bonfire of the Damned", "")); + cubeCards.add(new DraftCube.CardIdentity("Boros Charm", "")); + cubeCards.add(new DraftCube.CardIdentity("Boros Signet", "")); + cubeCards.add(new DraftCube.CardIdentity("Braids, Cabal Minion", "")); + cubeCards.add(new DraftCube.CardIdentity("Brain Freeze", "")); + cubeCards.add(new DraftCube.CardIdentity("Brain Maggot", "")); + cubeCards.add(new DraftCube.CardIdentity("Brainstorm", "")); + cubeCards.add(new DraftCube.CardIdentity("Breeding Pool", "")); + cubeCards.add(new DraftCube.CardIdentity("Bribery", "")); + cubeCards.add(new DraftCube.CardIdentity("Brimaz, King of Oreskos", "")); + cubeCards.add(new DraftCube.CardIdentity("Brimstone Volley", "")); + cubeCards.add(new DraftCube.CardIdentity("Bring to Light", "")); + cubeCards.add(new DraftCube.CardIdentity("Buried Alive", "")); + cubeCards.add(new DraftCube.CardIdentity("Burning of Xinye", "")); + cubeCards.add(new DraftCube.CardIdentity("Burst Lightning", "")); + cubeCards.add(new DraftCube.CardIdentity("Cabal Ritual", "")); + cubeCards.add(new DraftCube.CardIdentity("Careful Study", "")); + cubeCards.add(new DraftCube.CardIdentity("Celestial Colonnade", "")); + cubeCards.add(new DraftCube.CardIdentity("Chain Lightning", "")); + cubeCards.add(new DraftCube.CardIdentity("Chandra, Flamecaller", "")); + cubeCards.add(new DraftCube.CardIdentity("Chandra, Pyromaster", "")); + cubeCards.add(new DraftCube.CardIdentity("Char", "")); + cubeCards.add(new DraftCube.CardIdentity("Channel", "")); + cubeCards.add(new DraftCube.CardIdentity("Chrome Mox", "")); + cubeCards.add(new DraftCube.CardIdentity("Coalition Relic", "")); + cubeCards.add(new DraftCube.CardIdentity("Coercive Portal", "")); + cubeCards.add(new DraftCube.CardIdentity("Compulsive Research", "")); + cubeCards.add(new DraftCube.CardIdentity("Conclave Naturalists", "")); + cubeCards.add(new DraftCube.CardIdentity("Consecrated Sphinx", "")); + cubeCards.add(new DraftCube.CardIdentity("Control Magic", "")); + cubeCards.add(new DraftCube.CardIdentity("Corpse Dance", "")); + cubeCards.add(new DraftCube.CardIdentity("Council's Judgment", "")); + cubeCards.add(new DraftCube.CardIdentity("Counterspell", "")); + cubeCards.add(new DraftCube.CardIdentity("Courser of Kruphix", "")); + cubeCards.add(new DraftCube.CardIdentity("Crater's Claws", "")); + cubeCards.add(new DraftCube.CardIdentity("Craterhoof Behemoth", "")); + cubeCards.add(new DraftCube.CardIdentity("Creeping Tar Pit", "")); + cubeCards.add(new DraftCube.CardIdentity("Crucible of Worlds", "")); + cubeCards.add(new DraftCube.CardIdentity("Cryptic Command", "")); + cubeCards.add(new DraftCube.CardIdentity("Dack Fayden", "")); + cubeCards.add(new DraftCube.CardIdentity("Damnation", "")); + cubeCards.add(new DraftCube.CardIdentity("Daretti, Scrap Savant", "")); + cubeCards.add(new DraftCube.CardIdentity("Dark Confidant", "")); + cubeCards.add(new DraftCube.CardIdentity("Dark Petition", "")); + cubeCards.add(new DraftCube.CardIdentity("Dark Ritual", "")); + cubeCards.add(new DraftCube.CardIdentity("Day of Judgment", "")); + cubeCards.add(new DraftCube.CardIdentity("Daze", "")); + cubeCards.add(new DraftCube.CardIdentity("Deathrite Shaman", "")); + cubeCards.add(new DraftCube.CardIdentity("Deceiver Exarch", "")); + cubeCards.add(new DraftCube.CardIdentity("Declaration in Stone", "")); + cubeCards.add(new DraftCube.CardIdentity("Delver of Secrets", "")); + cubeCards.add(new DraftCube.CardIdentity("Demonic Tutor", "")); + cubeCards.add(new DraftCube.CardIdentity("Den Protector", "")); + cubeCards.add(new DraftCube.CardIdentity("Deranged Hermit", "")); + cubeCards.add(new DraftCube.CardIdentity("Desecration Demon", "")); + cubeCards.add(new DraftCube.CardIdentity("Diabolic Edict", "")); + cubeCards.add(new DraftCube.CardIdentity("Dig Through Time", "")); + cubeCards.add(new DraftCube.CardIdentity("Dimir Signet", "")); + cubeCards.add(new DraftCube.CardIdentity("Disenchant", "")); + cubeCards.add(new DraftCube.CardIdentity("Disfigure", "")); + cubeCards.add(new DraftCube.CardIdentity("Dismember", "")); + cubeCards.add(new DraftCube.CardIdentity("Dragonlord Atarka", "")); + cubeCards.add(new DraftCube.CardIdentity("Dragonlord Dromoka", "")); + cubeCards.add(new DraftCube.CardIdentity("Dragonlord Ojutai", "")); + cubeCards.add(new DraftCube.CardIdentity("Dragonlord Silumgar", "")); + cubeCards.add(new DraftCube.CardIdentity("Dreadbore", "")); + cubeCards.add(new DraftCube.CardIdentity("Dromoka's Command", "")); + cubeCards.add(new DraftCube.CardIdentity("Dualcaster Mage", "")); + cubeCards.add(new DraftCube.CardIdentity("Duplicant", "")); + cubeCards.add(new DraftCube.CardIdentity("Duress", "")); + cubeCards.add(new DraftCube.CardIdentity("Edric, Spymaster of Trest", "")); + cubeCards.add(new DraftCube.CardIdentity("Eidolon of the Great Revel", "")); + cubeCards.add(new DraftCube.CardIdentity("Electrolyze", "")); + cubeCards.add(new DraftCube.CardIdentity("Elesh Norn, Grand Cenobite", "")); + cubeCards.add(new DraftCube.CardIdentity("Elspeth, Knight-Errant", "")); + cubeCards.add(new DraftCube.CardIdentity("Elspeth, Sun's Champion", "")); + cubeCards.add(new DraftCube.CardIdentity("Elves of Deep Shadow", "")); + cubeCards.add(new DraftCube.CardIdentity("Elvish Mystic", "")); + cubeCards.add(new DraftCube.CardIdentity("Emeria Angel", "")); + cubeCards.add(new DraftCube.CardIdentity("Empty the Warrens", "")); + cubeCards.add(new DraftCube.CardIdentity("Emrakul, the Aeons Torn", "")); + cubeCards.add(new DraftCube.CardIdentity("Enlightened Tutor", "")); + cubeCards.add(new DraftCube.CardIdentity("Entomb", "")); + cubeCards.add(new DraftCube.CardIdentity("Eternal Witness", "")); + cubeCards.add(new DraftCube.CardIdentity("Eureka", "")); + cubeCards.add(new DraftCube.CardIdentity("Everflowing Chalice", "")); + cubeCards.add(new DraftCube.CardIdentity("Exalted Angel", "")); + cubeCards.add(new DraftCube.CardIdentity("Exquisite Firecraft", "")); + cubeCards.add(new DraftCube.CardIdentity("Exhume", "")); + cubeCards.add(new DraftCube.CardIdentity("Fact or Fiction", "")); + cubeCards.add(new DraftCube.CardIdentity("Faith's Fetters", "")); + cubeCards.add(new DraftCube.CardIdentity("Faithless Looting", "")); + cubeCards.add(new DraftCube.CardIdentity("Falkenrath Gorger", "")); + cubeCards.add(new DraftCube.CardIdentity("Fastbond", "")); + cubeCards.add(new DraftCube.CardIdentity("Fauna Shaman", "")); + cubeCards.add(new DraftCube.CardIdentity("Fiend Hunter", "")); + cubeCards.add(new DraftCube.CardIdentity("Fiery Confluence", "")); + cubeCards.add(new DraftCube.CardIdentity("Figure of Destiny", "")); + cubeCards.add(new DraftCube.CardIdentity("Fire // Ice", "")); + cubeCards.add(new DraftCube.CardIdentity("Fireblast", "")); + cubeCards.add(new DraftCube.CardIdentity("Firebolt", "")); + cubeCards.add(new DraftCube.CardIdentity("Firedrinker Satyr", "")); + cubeCards.add(new DraftCube.CardIdentity("Flametongue Kavu", "")); + cubeCards.add(new DraftCube.CardIdentity("Flickerwisp", "")); + cubeCards.add(new DraftCube.CardIdentity("Flooded Strand", "")); + cubeCards.add(new DraftCube.CardIdentity("Force of Will", "")); + cubeCards.add(new DraftCube.CardIdentity("Force Spike", "")); + cubeCards.add(new DraftCube.CardIdentity("Frantic Search", "")); + cubeCards.add(new DraftCube.CardIdentity("Freyalise, Llanowar's Fury", "")); + cubeCards.add(new DraftCube.CardIdentity("Frost Titan", "")); + cubeCards.add(new DraftCube.CardIdentity("Fyndhorn Elves", "")); + cubeCards.add(new DraftCube.CardIdentity("Gaea's Cradle", "")); + cubeCards.add(new DraftCube.CardIdentity("Garruk Relentless", "")); + cubeCards.add(new DraftCube.CardIdentity("Garruk Wildspeaker", "")); + cubeCards.add(new DraftCube.CardIdentity("Garruk, Apex Predator", "")); + cubeCards.add(new DraftCube.CardIdentity("Garruk, Primal Hunter", "")); + cubeCards.add(new DraftCube.CardIdentity("Geist of Saint Traft", "")); + cubeCards.add(new DraftCube.CardIdentity("Genesis Wave", "")); + cubeCards.add(new DraftCube.CardIdentity("Gideon Jura", "")); + cubeCards.add(new DraftCube.CardIdentity("Gideon, Ally of Zendikar", "")); + cubeCards.add(new DraftCube.CardIdentity("Gifts Ungiven", "")); + cubeCards.add(new DraftCube.CardIdentity("Gilded Lotus", "")); + cubeCards.add(new DraftCube.CardIdentity("Gitaxian Probe", "")); + cubeCards.add(new DraftCube.CardIdentity("Glen Elendra Archmage", "")); + cubeCards.add(new DraftCube.CardIdentity("Go for the Throat", "")); + cubeCards.add(new DraftCube.CardIdentity("Goblin Dark-Dwellers", "")); + cubeCards.add(new DraftCube.CardIdentity("Goblin Guide", "")); + cubeCards.add(new DraftCube.CardIdentity("Goblin Welder", "")); + cubeCards.add(new DraftCube.CardIdentity("Godless Shrine", "")); + cubeCards.add(new DraftCube.CardIdentity("Golgari Signet", "")); + cubeCards.add(new DraftCube.CardIdentity("Grave Titan", "")); + cubeCards.add(new DraftCube.CardIdentity("Green Sun's Zenith", "")); + cubeCards.add(new DraftCube.CardIdentity("Grim Lavamancer", "")); + cubeCards.add(new DraftCube.CardIdentity("Grim Monolith", "")); + cubeCards.add(new DraftCube.CardIdentity("Grim Tutor", "")); + cubeCards.add(new DraftCube.CardIdentity("Griselbrand", "")); + cubeCards.add(new DraftCube.CardIdentity("Gruul Signet", "")); + cubeCards.add(new DraftCube.CardIdentity("Gurmag Angler", "")); + cubeCards.add(new DraftCube.CardIdentity("Gush", "")); + cubeCards.add(new DraftCube.CardIdentity("Guttersnipe", "")); + cubeCards.add(new DraftCube.CardIdentity("Hallowed Fountain", "")); + cubeCards.add(new DraftCube.CardIdentity("Hangarback Walker", "")); + cubeCards.add(new DraftCube.CardIdentity("Harmonize", "")); + cubeCards.add(new DraftCube.CardIdentity("Heartbeat of Spring", "")); + cubeCards.add(new DraftCube.CardIdentity("Hedron Archive", "")); + cubeCards.add(new DraftCube.CardIdentity("Heir of Falkenrath", "")); + cubeCards.add(new DraftCube.CardIdentity("Hellrider", "")); + cubeCards.add(new DraftCube.CardIdentity("Hero of Bladehold", "")); + cubeCards.add(new DraftCube.CardIdentity("Hero's Downfall", "")); + cubeCards.add(new DraftCube.CardIdentity("High Tide", "")); + cubeCards.add(new DraftCube.CardIdentity("Hissing Quagmire", "")); + cubeCards.add(new DraftCube.CardIdentity("Honor of the Pure", "")); + cubeCards.add(new DraftCube.CardIdentity("Huntmaster of the Fells", "")); + cubeCards.add(new DraftCube.CardIdentity("Hymn to Tourach", "")); + cubeCards.add(new DraftCube.CardIdentity("Hypnotic Specter", "")); + cubeCards.add(new DraftCube.CardIdentity("Imperial Recruiter", "")); + cubeCards.add(new DraftCube.CardIdentity("Imperial Seal", "")); + cubeCards.add(new DraftCube.CardIdentity("Impulse", "")); + cubeCards.add(new DraftCube.CardIdentity("Incinerate", "")); + cubeCards.add(new DraftCube.CardIdentity("Inferno Titan", "")); + cubeCards.add(new DraftCube.CardIdentity("Inkwell Leviathan", "")); + cubeCards.add(new DraftCube.CardIdentity("Inquisition of Kozilek", "")); + cubeCards.add(new DraftCube.CardIdentity("Iona, Shield of Emeria", "")); + cubeCards.add(new DraftCube.CardIdentity("Isamaru, Hound of Konda", "")); + cubeCards.add(new DraftCube.CardIdentity("Isochron Scepter", "")); + cubeCards.add(new DraftCube.CardIdentity("Izzet Charm", "")); + cubeCards.add(new DraftCube.CardIdentity("Izzet Signet", "")); + cubeCards.add(new DraftCube.CardIdentity("Jace Beleren", "")); + cubeCards.add(new DraftCube.CardIdentity("Jace, Architect of Thought", "")); + cubeCards.add(new DraftCube.CardIdentity("Jace, the Mind Sculptor", "")); + cubeCards.add(new DraftCube.CardIdentity("Jace, Vryn's Prodigy", "")); + cubeCards.add(new DraftCube.CardIdentity("Jackal Pup", "")); + cubeCards.add(new DraftCube.CardIdentity("Joraga Treespeaker", "")); + cubeCards.add(new DraftCube.CardIdentity("Kalitas, Traitor of Ghet", "")); + cubeCards.add(new DraftCube.CardIdentity("Karakas", "")); + cubeCards.add(new DraftCube.CardIdentity("Kargan Dragonlord", "")); + cubeCards.add(new DraftCube.CardIdentity("Karn Liberated", "")); + cubeCards.add(new DraftCube.CardIdentity("Kiki-Jiki, Mirror Breaker", "")); + cubeCards.add(new DraftCube.CardIdentity("Kitchen Finks", "")); + cubeCards.add(new DraftCube.CardIdentity("Knight of the White Orchid", "")); + cubeCards.add(new DraftCube.CardIdentity("Kolaghan's Command", "")); + cubeCards.add(new DraftCube.CardIdentity("Koth of the Hammer", "")); + cubeCards.add(new DraftCube.CardIdentity("Kozilek, Butcher of Truth", "")); + cubeCards.add(new DraftCube.CardIdentity("Kozilek, the Great Distortion", "")); + cubeCards.add(new DraftCube.CardIdentity("Kuldotha Forgemaster", "")); + cubeCards.add(new DraftCube.CardIdentity("Kytheon, Hero of Akros", "")); + cubeCards.add(new DraftCube.CardIdentity("Land Tax", "")); + cubeCards.add(new DraftCube.CardIdentity("Lavaclaw Reaches", "")); + cubeCards.add(new DraftCube.CardIdentity("Leonin Relic-Warder", "")); + cubeCards.add(new DraftCube.CardIdentity("Leyline of Sanctity", "")); + cubeCards.add(new DraftCube.CardIdentity("Library of Alexandria", "")); + cubeCards.add(new DraftCube.CardIdentity("Lifebane Zombie", "")); + cubeCards.add(new DraftCube.CardIdentity("Lightning Bolt", "")); + cubeCards.add(new DraftCube.CardIdentity("Lightning Greaves", "")); + cubeCards.add(new DraftCube.CardIdentity("Lightning Helix", "")); + cubeCards.add(new DraftCube.CardIdentity("Lightning Mauler", "")); + cubeCards.add(new DraftCube.CardIdentity("Lightning Strike", "")); + cubeCards.add(new DraftCube.CardIdentity("Liliana of the Veil", "")); + cubeCards.add(new DraftCube.CardIdentity("Lingering Souls", "")); + cubeCards.add(new DraftCube.CardIdentity("Linvala, Keeper of Silence", "")); + cubeCards.add(new DraftCube.CardIdentity("Linvala, the Preserver", "")); + cubeCards.add(new DraftCube.CardIdentity("Lion's Eye Diamond", "")); + cubeCards.add(new DraftCube.CardIdentity("Living Death", "")); + cubeCards.add(new DraftCube.CardIdentity("Llanowar Elves", "")); + cubeCards.add(new DraftCube.CardIdentity("Lodestone Golem", "")); + cubeCards.add(new DraftCube.CardIdentity("Looter il-Kor", "")); + cubeCards.add(new DraftCube.CardIdentity("Lotus Bloom", "")); + cubeCards.add(new DraftCube.CardIdentity("Lotus Cobra", "")); + cubeCards.add(new DraftCube.CardIdentity("Lumbering Falls", "")); + cubeCards.add(new DraftCube.CardIdentity("Maelstrom Pulse", "")); + cubeCards.add(new DraftCube.CardIdentity("Magma Jet", "")); + cubeCards.add(new DraftCube.CardIdentity("Magus of the Moon", "")); + cubeCards.add(new DraftCube.CardIdentity("Magus of the Wheel", "")); + cubeCards.add(new DraftCube.CardIdentity("Makeshift Mannequin", "")); + cubeCards.add(new DraftCube.CardIdentity("Managorger Hydra", "")); + cubeCards.add(new DraftCube.CardIdentity("Mana Confluence", "")); + cubeCards.add(new DraftCube.CardIdentity("Mana Crypt", "")); + cubeCards.add(new DraftCube.CardIdentity("Mana Drain", "")); + cubeCards.add(new DraftCube.CardIdentity("Mana Leak", "")); + cubeCards.add(new DraftCube.CardIdentity("Mana Tithe", "")); + cubeCards.add(new DraftCube.CardIdentity("Mana Vault", "")); + cubeCards.add(new DraftCube.CardIdentity("Manic Vandal", "")); + cubeCards.add(new DraftCube.CardIdentity("Mardu Woe-Reaper", "")); + cubeCards.add(new DraftCube.CardIdentity("Marsh Flats", "")); + cubeCards.add(new DraftCube.CardIdentity("Massacre Wurm", "")); + cubeCards.add(new DraftCube.CardIdentity("Master of the Wild Hunt", "")); + cubeCards.add(new DraftCube.CardIdentity("Maze of Ith", "")); + cubeCards.add(new DraftCube.CardIdentity("Meloku the Clouded Mirror", "")); + cubeCards.add(new DraftCube.CardIdentity("Mental Misstep", "")); + cubeCards.add(new DraftCube.CardIdentity("Memory Jar", "")); + cubeCards.add(new DraftCube.CardIdentity("Mesmeric Fiend", "")); + cubeCards.add(new DraftCube.CardIdentity("Metalworker", "")); + cubeCards.add(new DraftCube.CardIdentity("Mind Twist", "")); + cubeCards.add(new DraftCube.CardIdentity("Mindslaver", "")); + cubeCards.add(new DraftCube.CardIdentity("Mind's Desire", "")); + cubeCards.add(new DraftCube.CardIdentity("Mirari's Wake", "")); + cubeCards.add(new DraftCube.CardIdentity("Mirran Crusader", "")); + cubeCards.add(new DraftCube.CardIdentity("Mishra's Factory", "")); + cubeCards.add(new DraftCube.CardIdentity("Mishra's Workshop", "")); + cubeCards.add(new DraftCube.CardIdentity("Misty Rainforest", "")); + cubeCards.add(new DraftCube.CardIdentity("Mizzium Mortars", "")); + cubeCards.add(new DraftCube.CardIdentity("Moat", "")); + cubeCards.add(new DraftCube.CardIdentity("Monastery Mentor", "")); + cubeCards.add(new DraftCube.CardIdentity("Monastery Swiftspear", "")); + cubeCards.add(new DraftCube.CardIdentity("Mother of Runes", "")); + cubeCards.add(new DraftCube.CardIdentity("Mox Diamond", "")); + cubeCards.add(new DraftCube.CardIdentity("Mox Emerald", "")); + cubeCards.add(new DraftCube.CardIdentity("Mox Jet", "")); + cubeCards.add(new DraftCube.CardIdentity("Mox Pearl", "")); + cubeCards.add(new DraftCube.CardIdentity("Mox Ruby", "")); + cubeCards.add(new DraftCube.CardIdentity("Mox Sapphire", "")); + cubeCards.add(new DraftCube.CardIdentity("Mulldrifter", "")); + cubeCards.add(new DraftCube.CardIdentity("Murderous Cut", "")); + cubeCards.add(new DraftCube.CardIdentity("Mutavault", "")); + cubeCards.add(new DraftCube.CardIdentity("Myr Battlesphere", "")); + cubeCards.add(new DraftCube.CardIdentity("Mystical Tutor", "")); + cubeCards.add(new DraftCube.CardIdentity("Mystic Confluence", "")); + cubeCards.add(new DraftCube.CardIdentity("Mystic Snake", "")); + cubeCards.add(new DraftCube.CardIdentity("Nahiri, the Harbinger", "")); + cubeCards.add(new DraftCube.CardIdentity("Natural Order", "")); + cubeCards.add(new DraftCube.CardIdentity("Nature's Claim", "")); + cubeCards.add(new DraftCube.CardIdentity("Necromancy", "")); + cubeCards.add(new DraftCube.CardIdentity("Necropotence", "")); + cubeCards.add(new DraftCube.CardIdentity("Needle Spires", "")); + cubeCards.add(new DraftCube.CardIdentity("Nekrataal", "")); + cubeCards.add(new DraftCube.CardIdentity("Nevinyrral's Disk", "")); + cubeCards.add(new DraftCube.CardIdentity("Nezumi Graverobber", "")); + cubeCards.add(new DraftCube.CardIdentity("Nezumi Shortfang", "")); + cubeCards.add(new DraftCube.CardIdentity("Nicol Bolas, Planeswalker", "")); + cubeCards.add(new DraftCube.CardIdentity("Nissa, Worldwaker", "")); + cubeCards.add(new DraftCube.CardIdentity("Noble Hierarch", "")); + cubeCards.add(new DraftCube.CardIdentity("Nykthos, Shrine to Nyx", "")); + cubeCards.add(new DraftCube.CardIdentity("Oath of Druids", "")); + cubeCards.add(new DraftCube.CardIdentity("Oath of Nissa", "")); + cubeCards.add(new DraftCube.CardIdentity("Oblivion Ring", "")); + cubeCards.add(new DraftCube.CardIdentity("Olivia Voldaren", "")); + cubeCards.add(new DraftCube.CardIdentity("Oona's Prowler", "")); + cubeCards.add(new DraftCube.CardIdentity("Ophiomancer", "")); + cubeCards.add(new DraftCube.CardIdentity("Opposition", "")); + cubeCards.add(new DraftCube.CardIdentity("Oracle of Mul Daya", "")); + cubeCards.add(new DraftCube.CardIdentity("Orzhov Signet", "")); + cubeCards.add(new DraftCube.CardIdentity("Outpost Siege", "")); + cubeCards.add(new DraftCube.CardIdentity("Overgrown Tomb", "")); + cubeCards.add(new DraftCube.CardIdentity("Pack Rat", "")); + cubeCards.add(new DraftCube.CardIdentity("Painful Truths", "")); + cubeCards.add(new DraftCube.CardIdentity("Palinchron", "")); + cubeCards.add(new DraftCube.CardIdentity("Parallax Wave", "")); + cubeCards.add(new DraftCube.CardIdentity("Path to Exile", "")); + cubeCards.add(new DraftCube.CardIdentity("Pentad Prism", "")); + cubeCards.add(new DraftCube.CardIdentity("Pernicious Deed", "")); + cubeCards.add(new DraftCube.CardIdentity("Pestermite", "")); + cubeCards.add(new DraftCube.CardIdentity("Phantasmal Image", "")); + cubeCards.add(new DraftCube.CardIdentity("Phyrexian Metamorph", "")); + cubeCards.add(new DraftCube.CardIdentity("Phyrexian Revoker", "")); + cubeCards.add(new DraftCube.CardIdentity("Pithing Needle", "")); + cubeCards.add(new DraftCube.CardIdentity("Plateau", "")); + cubeCards.add(new DraftCube.CardIdentity("Polluted Delta", "")); + cubeCards.add(new DraftCube.CardIdentity("Polukranos, World Eater", "")); + cubeCards.add(new DraftCube.CardIdentity("Ponder", "")); + cubeCards.add(new DraftCube.CardIdentity("Porcelain Legionnaire", "")); + cubeCards.add(new DraftCube.CardIdentity("Preordain", "")); + cubeCards.add(new DraftCube.CardIdentity("Primal Command", "")); + cubeCards.add(new DraftCube.CardIdentity("Primeval Titan", "")); + cubeCards.add(new DraftCube.CardIdentity("Progenitus", "")); + cubeCards.add(new DraftCube.CardIdentity("Puppeteer Clique", "")); + cubeCards.add(new DraftCube.CardIdentity("Putrid Imp", "")); + cubeCards.add(new DraftCube.CardIdentity("Qasali Pridemage", "")); + cubeCards.add(new DraftCube.CardIdentity("Quicken", "")); + cubeCards.add(new DraftCube.CardIdentity("Raging Ravine", "")); + cubeCards.add(new DraftCube.CardIdentity("Rakdos Signet", "")); + cubeCards.add(new DraftCube.CardIdentity("Rakdos's Return", "")); + cubeCards.add(new DraftCube.CardIdentity("Ral Zarek", "")); + cubeCards.add(new DraftCube.CardIdentity("Ravages of War", "")); + cubeCards.add(new DraftCube.CardIdentity("Reanimate", "")); + cubeCards.add(new DraftCube.CardIdentity("Reckless Bushwhacker", "")); + cubeCards.add(new DraftCube.CardIdentity("Reclamation Sage", "")); + cubeCards.add(new DraftCube.CardIdentity("Recurring Nightmare", "")); + cubeCards.add(new DraftCube.CardIdentity("Reflector Mage", "")); + cubeCards.add(new DraftCube.CardIdentity("Regrowth", "")); + cubeCards.add(new DraftCube.CardIdentity("Remand", "")); + cubeCards.add(new DraftCube.CardIdentity("Repeal", "")); + cubeCards.add(new DraftCube.CardIdentity("Restoration Angel", "")); + cubeCards.add(new DraftCube.CardIdentity("Reveillark", "")); + cubeCards.add(new DraftCube.CardIdentity("Rift Bolt", "")); + cubeCards.add(new DraftCube.CardIdentity("Riftwing Cloudskate", "")); + cubeCards.add(new DraftCube.CardIdentity("Rishadan Port", "")); + cubeCards.add(new DraftCube.CardIdentity("Roast", "")); + cubeCards.add(new DraftCube.CardIdentity("Rofellos, Llanowar Emissary", "")); + cubeCards.add(new DraftCube.CardIdentity("Sacred Foundry", "")); + cubeCards.add(new DraftCube.CardIdentity("Sakura-Tribe Elder", "")); + cubeCards.add(new DraftCube.CardIdentity("Savannah", "")); + cubeCards.add(new DraftCube.CardIdentity("Scalding Tarn", "")); + cubeCards.add(new DraftCube.CardIdentity("Scavenging Ooze", "")); + cubeCards.add(new DraftCube.CardIdentity("Scroll Rack", "")); + cubeCards.add(new DraftCube.CardIdentity("Scrubland", "")); + cubeCards.add(new DraftCube.CardIdentity("Search for Tomorrow", "")); + cubeCards.add(new DraftCube.CardIdentity("Searing Blaze", "")); + cubeCards.add(new DraftCube.CardIdentity("Searing Spear", "")); + cubeCards.add(new DraftCube.CardIdentity("Seasons Past", "")); + cubeCards.add(new DraftCube.CardIdentity("Secure the Wastes", "")); + cubeCards.add(new DraftCube.CardIdentity("Seeker of the Way", "")); + cubeCards.add(new DraftCube.CardIdentity("Seething Song", "")); + cubeCards.add(new DraftCube.CardIdentity("Selesnya Signet", "")); + cubeCards.add(new DraftCube.CardIdentity("Sensei's Divining Top", "")); + cubeCards.add(new DraftCube.CardIdentity("Shadowmage Infiltrator", "")); + cubeCards.add(new DraftCube.CardIdentity("Shallow Grave", "")); + cubeCards.add(new DraftCube.CardIdentity("Shambling Vent", "")); + cubeCards.add(new DraftCube.CardIdentity("Shardless Agent", "")); + cubeCards.add(new DraftCube.CardIdentity("Shelldock Isle", "")); + cubeCards.add(new DraftCube.CardIdentity("Sheoldred, Whispering One", "")); + cubeCards.add(new DraftCube.CardIdentity("Show and Tell", "")); + cubeCards.add(new DraftCube.CardIdentity("Shriekmaw", "")); + cubeCards.add(new DraftCube.CardIdentity("Shrine of Burning Rage", "")); + cubeCards.add(new DraftCube.CardIdentity("Sidisi, Undead Vizier", "")); + cubeCards.add(new DraftCube.CardIdentity("Siege-Gang Commander", "")); + cubeCards.add(new DraftCube.CardIdentity("Silverblade Paladin", "")); + cubeCards.add(new DraftCube.CardIdentity("Simic Signet", "")); + cubeCards.add(new DraftCube.CardIdentity("Skinrender", "")); + cubeCards.add(new DraftCube.CardIdentity("Skullclamp", "")); + cubeCards.add(new DraftCube.CardIdentity("Smash to Smithereens", "")); + cubeCards.add(new DraftCube.CardIdentity("Smokestack", "")); + cubeCards.add(new DraftCube.CardIdentity("Snapcaster Mage", "")); + cubeCards.add(new DraftCube.CardIdentity("Sneak Attack", "")); + cubeCards.add(new DraftCube.CardIdentity("Sol Ring", "")); + cubeCards.add(new DraftCube.CardIdentity("Soldier of the Pantheon", "")); + cubeCards.add(new DraftCube.CardIdentity("Solemn Simulacrum", "")); + cubeCards.add(new DraftCube.CardIdentity("Song of the Dryads", "")); + cubeCards.add(new DraftCube.CardIdentity("Soulfire Grand Master", "")); + cubeCards.add(new DraftCube.CardIdentity("Sower of Temptation", "")); + cubeCards.add(new DraftCube.CardIdentity("Spear of Heliod", "")); + cubeCards.add(new DraftCube.CardIdentity("Spectral Procession", "")); + cubeCards.add(new DraftCube.CardIdentity("Spell Pierce", "")); + cubeCards.add(new DraftCube.CardIdentity("Spellskite", "")); + cubeCards.add(new DraftCube.CardIdentity("Sphinx of the Steel Wind", "")); + cubeCards.add(new DraftCube.CardIdentity("Sphinx's Revelation", "")); + cubeCards.add(new DraftCube.CardIdentity("Spirit of the Labyrinth", "")); + cubeCards.add(new DraftCube.CardIdentity("Splinter Twin", "")); + cubeCards.add(new DraftCube.CardIdentity("Staff of Domination", "")); + cubeCards.add(new DraftCube.CardIdentity("Steam Vents", "")); + cubeCards.add(new DraftCube.CardIdentity("Stirring Wildwood", "")); + cubeCards.add(new DraftCube.CardIdentity("Stomping Ground", "")); + cubeCards.add(new DraftCube.CardIdentity("Stoneforge Mystic", "")); + cubeCards.add(new DraftCube.CardIdentity("Stormbreath Dragon", "")); + cubeCards.add(new DraftCube.CardIdentity("Strip Mine", "")); + cubeCards.add(new DraftCube.CardIdentity("Student of Warfare", "")); + cubeCards.add(new DraftCube.CardIdentity("Sulfuric Vortex", "")); + cubeCards.add(new DraftCube.CardIdentity("Sun Titan", "")); + cubeCards.add(new DraftCube.CardIdentity("Sundering Titan", "")); + cubeCards.add(new DraftCube.CardIdentity("Supreme Verdict", "")); + cubeCards.add(new DraftCube.CardIdentity("Survival of the Fittest", "")); + cubeCards.add(new DraftCube.CardIdentity("Sword of Body and Mind", "")); + cubeCards.add(new DraftCube.CardIdentity("Sword of Feast and Famine", "")); + cubeCards.add(new DraftCube.CardIdentity("Sword of Fire and Ice", "")); + cubeCards.add(new DraftCube.CardIdentity("Sword of Light and Shadow", "")); + cubeCards.add(new DraftCube.CardIdentity("Sword of War and Peace", "")); + cubeCards.add(new DraftCube.CardIdentity("Swords to Plowshares", "")); + cubeCards.add(new DraftCube.CardIdentity("Sylvan Caryatid", "")); + cubeCards.add(new DraftCube.CardIdentity("Sylvan Library", "")); + cubeCards.add(new DraftCube.CardIdentity("Taiga", "")); + cubeCards.add(new DraftCube.CardIdentity("Tamiyo, the Moon Sage", "")); + cubeCards.add(new DraftCube.CardIdentity("Tangle Wire", "")); + cubeCards.add(new DraftCube.CardIdentity("Tarmogoyf", "")); + cubeCards.add(new DraftCube.CardIdentity("Tasigur, the Golden Fang", "")); + cubeCards.add(new DraftCube.CardIdentity("Temple Garden", "")); + cubeCards.add(new DraftCube.CardIdentity("Temple of Abandon", "")); + cubeCards.add(new DraftCube.CardIdentity("Temple of Deceit", "")); + cubeCards.add(new DraftCube.CardIdentity("Temple of Enlightenment", "")); + cubeCards.add(new DraftCube.CardIdentity("Temple of Epiphany", "")); + cubeCards.add(new DraftCube.CardIdentity("Temple of Malady", "")); + cubeCards.add(new DraftCube.CardIdentity("Temple of Malice", "")); + cubeCards.add(new DraftCube.CardIdentity("Temple of Mystery", "")); + cubeCards.add(new DraftCube.CardIdentity("Temple of Plenty", "")); + cubeCards.add(new DraftCube.CardIdentity("Temple of Silence", "")); + cubeCards.add(new DraftCube.CardIdentity("Temple of Triumph", "")); + cubeCards.add(new DraftCube.CardIdentity("Tendrils of Agony", "")); + cubeCards.add(new DraftCube.CardIdentity("Terastodon", "")); + cubeCards.add(new DraftCube.CardIdentity("Terminate", "")); + cubeCards.add(new DraftCube.CardIdentity("Tezzeret the Seeker", "")); + cubeCards.add(new DraftCube.CardIdentity("Tezzeret, Agent of Bolas", "")); + cubeCards.add(new DraftCube.CardIdentity("Thada Adel, Acquisitor", "")); + cubeCards.add(new DraftCube.CardIdentity("Thalia, Guardian of Thraben", "")); + cubeCards.add(new DraftCube.CardIdentity("The Abyss", "")); + cubeCards.add(new DraftCube.CardIdentity("Thing in the Ice", "")); + cubeCards.add(new DraftCube.CardIdentity("Thirst for Knowledge", "")); + cubeCards.add(new DraftCube.CardIdentity("Thoughtseize", "")); + cubeCards.add(new DraftCube.CardIdentity("Thragtusk", "")); + cubeCards.add(new DraftCube.CardIdentity("Thran Dynamo", "")); + cubeCards.add(new DraftCube.CardIdentity("Through the Breach", "")); + cubeCards.add(new DraftCube.CardIdentity("Thrun, the Last Troll", "")); + cubeCards.add(new DraftCube.CardIdentity("Thundermaw Hellkite", "")); + cubeCards.add(new DraftCube.CardIdentity("Tidehollow Sculler", "")); + cubeCards.add(new DraftCube.CardIdentity("Time Spiral", "")); + cubeCards.add(new DraftCube.CardIdentity("Time Walk", "")); + cubeCards.add(new DraftCube.CardIdentity("Timely Reinforcements", "")); + cubeCards.add(new DraftCube.CardIdentity("Timetwister", "")); + cubeCards.add(new DraftCube.CardIdentity("Tinker", "")); + cubeCards.add(new DraftCube.CardIdentity("Tolarian Academy", "")); + cubeCards.add(new DraftCube.CardIdentity("Tooth and Nail", "")); + cubeCards.add(new DraftCube.CardIdentity("Torch Fiend", "")); + cubeCards.add(new DraftCube.CardIdentity("Toxic Deluge", "")); + cubeCards.add(new DraftCube.CardIdentity("Traverse the Ulvenwald", "")); + cubeCards.add(new DraftCube.CardIdentity("Treachery", "")); + cubeCards.add(new DraftCube.CardIdentity("Treasure Cruise", "")); + cubeCards.add(new DraftCube.CardIdentity("Trinket Mage", "")); + cubeCards.add(new DraftCube.CardIdentity("Tropical Island", "")); + cubeCards.add(new DraftCube.CardIdentity("True-Name Nemesis", "")); + cubeCards.add(new DraftCube.CardIdentity("Trygon Predator", "")); + cubeCards.add(new DraftCube.CardIdentity("Tundra", "")); + cubeCards.add(new DraftCube.CardIdentity("Turnabout", "")); + cubeCards.add(new DraftCube.CardIdentity("Ugin, the Spirit Dragon", "")); + cubeCards.add(new DraftCube.CardIdentity("Ulamog, the Ceaseless Hunger", "")); + cubeCards.add(new DraftCube.CardIdentity("Ulamog, the Infinite Gyre", "")); + cubeCards.add(new DraftCube.CardIdentity("Ultimate Price", "")); + cubeCards.add(new DraftCube.CardIdentity("Umezawa's Jitte", "")); + cubeCards.add(new DraftCube.CardIdentity("Unburial Rites", "")); + cubeCards.add(new DraftCube.CardIdentity("Underground Sea", "")); + cubeCards.add(new DraftCube.CardIdentity("Unexpectedly Absent", "")); + cubeCards.add(new DraftCube.CardIdentity("Upheaval", "")); + cubeCards.add(new DraftCube.CardIdentity("Urborg, Tomb of Yawgmoth", "")); + cubeCards.add(new DraftCube.CardIdentity("Vampire Nighthawk", "")); + cubeCards.add(new DraftCube.CardIdentity("Vampiric Tutor", "")); + cubeCards.add(new DraftCube.CardIdentity("Vedalken Shackles", "")); + cubeCards.add(new DraftCube.CardIdentity("Vendilion Clique", "")); + cubeCards.add(new DraftCube.CardIdentity("Venser, Shaper Savant", "")); + cubeCards.add(new DraftCube.CardIdentity("Verdant Catacombs", "")); + cubeCards.add(new DraftCube.CardIdentity("Villainous Wealth", "")); + cubeCards.add(new DraftCube.CardIdentity("Vindicate", "")); + cubeCards.add(new DraftCube.CardIdentity("Voice of Resurgence", "")); + cubeCards.add(new DraftCube.CardIdentity("Volcanic Island", "")); + cubeCards.add(new DraftCube.CardIdentity("Vryn Wingmare", "")); + cubeCards.add(new DraftCube.CardIdentity("Wall of Blossoms", "")); + cubeCards.add(new DraftCube.CardIdentity("Wall of Omens", "")); + cubeCards.add(new DraftCube.CardIdentity("Wall of Roots", "")); + cubeCards.add(new DraftCube.CardIdentity("Wandering Fumarole", "")); + cubeCards.add(new DraftCube.CardIdentity("War Priest of Thune", "")); + cubeCards.add(new DraftCube.CardIdentity("Wasteland", "")); + cubeCards.add(new DraftCube.CardIdentity("Watery Grave", "")); + cubeCards.add(new DraftCube.CardIdentity("Westvale Abbey", "")); + cubeCards.add(new DraftCube.CardIdentity("Wheel of Fortune", "")); + cubeCards.add(new DraftCube.CardIdentity("Whisperwood Elemental", "")); + cubeCards.add(new DraftCube.CardIdentity("Wildfire", "")); + cubeCards.add(new DraftCube.CardIdentity("Windswept Heath", "")); + cubeCards.add(new DraftCube.CardIdentity("Winter Orb", "")); + cubeCards.add(new DraftCube.CardIdentity("Wolfir Silverheart", "")); + cubeCards.add(new DraftCube.CardIdentity("Wooded Foothills", "")); + cubeCards.add(new DraftCube.CardIdentity("Woodfall Primus", "")); + cubeCards.add(new DraftCube.CardIdentity("Worn Powerstone", "")); + cubeCards.add(new DraftCube.CardIdentity("Wrath of God", "")); + cubeCards.add(new DraftCube.CardIdentity("Wurmcoil Engine", "")); + cubeCards.add(new DraftCube.CardIdentity("Xenagos, the Reveler", "")); + cubeCards.add(new DraftCube.CardIdentity("Yavimaya Elder", "")); + cubeCards.add(new DraftCube.CardIdentity("Yawgmoth's Bargain", "")); + cubeCards.add(new DraftCube.CardIdentity("Yawgmoth's Will", "")); + cubeCards.add(new DraftCube.CardIdentity("Young Pyromancer", "")); + cubeCards.add(new DraftCube.CardIdentity("Zealous Conscripts", "")); + cubeCards.add(new DraftCube.CardIdentity("Zurgo Bellstriker", "")); + } +} diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index 924d92a58aa..b211ab08a1b 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -81,6 +81,7 @@ + diff --git a/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java b/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java index 85a868464b4..e7f218b88ec 100644 --- a/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java +++ b/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java @@ -251,20 +251,28 @@ class TableListSorter implements Comparator { } else { return two.getEndTime().compareTo(one.getEndTime()); } + } else if (one.getEndTime() != null) { + return -1; } + if (two.getStartTime() != null) { if (one.getStartTime() == null) { return 1; } else { return two.getStartTime().compareTo(one.getStartTime()); } + } else if (one.getStartTime() != null) { + return -1; } + if (two.getCreateTime() != null) { if (one.getCreateTime() == null) { return 1; } else { return two.getCreateTime().compareTo(one.getCreateTime()); } + } else if (one.getCreateTime() != null) { + return -1; } return 0; } diff --git a/Mage.Sets/src/mage/sets/alarareborn/ThoughtHemorrhage.java b/Mage.Sets/src/mage/sets/alarareborn/ThoughtHemorrhage.java index 7498fe2f972..8eb65ddd33e 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/ThoughtHemorrhage.java +++ b/Mage.Sets/src/mage/sets/alarareborn/ThoughtHemorrhage.java @@ -56,9 +56,6 @@ public class ThoughtHemorrhage extends CardImpl { super(ownerId, 47, "Thought Hemorrhage", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{2}{B}{R}"); this.expansionSetCode = "ARB"; - - - // Name a nonland card. Target player reveals his or her hand. Thought Hemorrhage deals 3 damage to that player for each card with that name revealed this way. Search that player's graveyard, hand, and library for all cards with that name and exile them. Then that player shuffles his or her library. this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new NameACardEffect(NameACardEffect.TypeOfName.NON_LAND_NAME)); @@ -93,7 +90,7 @@ class ThoughtHemorrhageEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); String cardName = (String) game.getState().getValue(source.getSourceId().toString() + NameACardEffect.INFO_KEY); - if (sourceObject != null && controller != null && cardName != null && !cardName.isEmpty()) { + if (sourceObject != null && controller != null && cardName != null && !cardName.isEmpty()) { Player targetPlayer = game.getPlayer(source.getFirstTarget()); if (targetPlayer != null) { targetPlayer.revealCards("hand of " + targetPlayer.getName(), targetPlayer.getHand(), game); @@ -123,7 +120,7 @@ class ThoughtHemorrhageEffect extends OneShotEffect { // search cards in hand TargetCardInHand targetCardsHand = new TargetCardInHand(0, Integer.MAX_VALUE, filterNamedCards); controller.chooseTarget(outcome, targetPlayer.getGraveyard(), targetCardsHand, source, game); - for(UUID cardId: targetCardsHand.getTargets()) { + for (UUID cardId : targetCardsHand.getTargets()) { Card card = game.getCard(cardId); if (card != null) { controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.HAND, true); @@ -134,14 +131,14 @@ class ThoughtHemorrhageEffect extends OneShotEffect { // If the player has no nonland cards in his or her hand, you can still search that player's library and have him or her shuffle it. TargetCardInLibrary targetCardsLibrary = new TargetCardInLibrary(0, Integer.MAX_VALUE, filterNamedCards); controller.searchLibrary(targetCardsLibrary, game, targetPlayer.getId()); - for(UUID cardId: targetCardsLibrary.getTargets()) { + for (UUID cardId : targetCardsLibrary.getTargets()) { Card card = game.getCard(cardId); if (card != null) { controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.LIBRARY, true); } } targetPlayer.shuffleLibrary(source, game); - return true; + return true; } } return false; diff --git a/Mage.Sets/src/mage/sets/antiquities/TawnossCoffin.java b/Mage.Sets/src/mage/sets/antiquities/TawnossCoffin.java index 0f490f893a2..b3634e3a1a1 100644 --- a/Mage.Sets/src/mage/sets/antiquities/TawnossCoffin.java +++ b/Mage.Sets/src/mage/sets/antiquities/TawnossCoffin.java @@ -218,7 +218,9 @@ class TawnossCoffinReturnEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { ExileZone exileZone = game.getExile().getExileZone(source.getSourceId()); - + if (exileZone == null) { + return true; + } FilterCard filter = new FilterCard(); filter.add(new CardTypePredicate(CardType.CREATURE)); //There should be only 1 there, but the for each loop seems the most practical to get to it diff --git a/Mage.Sets/src/mage/sets/apocalypse/TundraKavu.java b/Mage.Sets/src/mage/sets/apocalypse/TundraKavu.java index e1f72960bf5..ad473af8132 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/TundraKavu.java +++ b/Mage.Sets/src/mage/sets/apocalypse/TundraKavu.java @@ -74,7 +74,6 @@ public class TundraKavu extends CardImpl { } } - class TundraKavuEffect extends BecomesBasicLandTargetEffect { public TundraKavuEffect() { @@ -86,6 +85,7 @@ class TundraKavuEffect extends BecomesBasicLandTargetEffect { super(effect); } + @Override public TundraKavuEffect copy() { return new TundraKavuEffect(this); } @@ -94,14 +94,18 @@ class TundraKavuEffect extends BecomesBasicLandTargetEffect { public void init(Ability source, Game game) { landTypes.clear(); Player controller = game.getPlayer(source.getControllerId()); - if(controller != null) { + if (controller != null) { Set choiceSet = new LinkedHashSet<>(); choiceSet.add("Island"); choiceSet.add("Plains"); ChoiceImpl choice = new ChoiceImpl(true); choice.setChoices(choiceSet); choice.setMessage("Choose a basic land type"); - controller.choose(outcome, choice, game); + while (!controller.choose(outcome, choice, game)) { + if (!controller.canRespond()) { + return; + } + } landTypes.add(choice.getChoice()); } else { this.discard(); diff --git a/Mage.Sets/src/mage/sets/archenemy/SynodSanctum.java b/Mage.Sets/src/mage/sets/archenemy/SynodSanctum.java new file mode 100644 index 00000000000..3c03d92e527 --- /dev/null +++ b/Mage.Sets/src/mage/sets/archenemy/SynodSanctum.java @@ -0,0 +1,171 @@ +/* + * 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; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +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.TargetController; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetPermanent; +import mage.util.CardUtil; + +/** + * + * @author spjspj + */ +public class SynodSanctum extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("permanent you control"); + + static { + filter.add(new ControllerPredicate(TargetController.YOU)); + } + + public SynodSanctum(UUID ownerId) { + super(ownerId, 120, "Synod Sanctum", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT}, "{1}"); + this.expansionSetCode = "ARC"; + + // {2}, {tap}: Exile target permanent you control. + SynodSanctumEffect effect = new SynodSanctumEffect(); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{2}")); + ability.addCost(new TapSourceCost()); + Target target = new TargetPermanent(filter); + ability.addTarget(target); + this.addAbility(ability); + + // {2}, Sacrifice Synod Sanctum: Return all cards exiled with Synod Sanctum to the battlefield under your control. + SynodSanctumEffect2 effect2 = new SynodSanctumEffect2(); + Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect2, new ManaCostsImpl("{2}")); + ability2.addCost(new SacrificeSourceCost()); + this.addAbility(ability2); + } + + public SynodSanctum(final SynodSanctum card) { + super(card); + } + + @Override + public SynodSanctum copy() { + return new SynodSanctum(this); + } +} + +class SynodSanctumEffect extends OneShotEffect { + + SynodSanctumEffect() { + super(Outcome.Benefit); + staticText = "Exile target permanent you control"; + } + + SynodSanctumEffect(SynodSanctumEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null && sourceObject != null) { + if (getTargetPointer().getFirst(game, source) != null) { + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent != null) { + UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + if (exileZone != null && controller.moveCardToExileWithInfo(permanent, exileZone, sourceObject.getIdName(), source.getId(), game, Zone.BATTLEFIELD, true)) { + return true; + } + } + } + } + return false; + } + + @Override + public SynodSanctumEffect copy() { + return new SynodSanctumEffect(this); + } +} + +class SynodSanctumEffect2 extends OneShotEffect { + + SynodSanctumEffect2() { + super(Outcome.Benefit); + staticText = "Return all cards exiled with {this} to the battlefield under your control"; + } + + SynodSanctumEffect2(SynodSanctumEffect2 effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + if (exileId == null) { + return false; + } + ExileZone exile = game.getExile().getExileZone(exileId); + if (exile == null) { + return false; + } + + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + boolean allReturned = true; + if (controller != null && sourceObject != null) { + for (Card card : exile.getCards(game)) { + if (!card.putOntoBattlefield(game, Zone.EXILED, source.getSourceId(), controller.getId())) { + allReturned = false; + } + } + return allReturned; + } + return false; + } + + @Override + public SynodSanctumEffect2 copy() { + return new SynodSanctumEffect2(this); + } +} diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/AdverseConditions.java b/Mage.Sets/src/mage/sets/battleforzendikar/AdverseConditions.java index 501c68e7d87..bc2a92d7430 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/AdverseConditions.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/AdverseConditions.java @@ -58,7 +58,7 @@ public class AdverseConditions extends CardImpl { // Tap up to two target creatures. Those creatures don't untap during their controller's next untap step. this.getSpellAbility().addEffect(new TapTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2)); - this.getSpellAbility().addEffect(new DontUntapInControllersNextUntapStepTargetEffect()); + this.getSpellAbility().addEffect(new DontUntapInControllersNextUntapStepTargetEffect("Those creatures")); // Put a 1/1 colorless Eldrazi Scion creature token onto the battlefield. It has "Sacrifice this creature: Add {C} to your mana pool." Effect effect = new CreateTokenEffect(new EldraziScionToken()); effect.setText("put a 1/1 colorless Eldrazi Scion creature token onto the battlefield. It has \"Sacrifice this creature: Add {C} to your mana pool.\""); diff --git a/Mage.Sets/src/mage/sets/bornofthegods/SuddenStorm.java b/Mage.Sets/src/mage/sets/bornofthegods/SuddenStorm.java index 320a21cad05..dc874b4c8c4 100644 --- a/Mage.Sets/src/mage/sets/bornofthegods/SuddenStorm.java +++ b/Mage.Sets/src/mage/sets/bornofthegods/SuddenStorm.java @@ -28,9 +28,9 @@ package mage.sets.bornofthegods; import java.util.UUID; -import mage.abilities.effects.keyword.ScryEffect; import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.keyword.ScryEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; @@ -50,7 +50,7 @@ public class SuddenStorm extends CardImpl { // Tap up to two target creatures. Those creatures don't untap during their controllers' next untap steps. Scry 1. this.getSpellAbility().addEffect(new TapTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2)); - this.getSpellAbility().addEffect(new DontUntapInControllersNextUntapStepTargetEffect()); + this.getSpellAbility().addEffect(new DontUntapInControllersNextUntapStepTargetEffect("Those creatures")); this.getSpellAbility().addEffect(new ScryEffect(1)); } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/CranialExtraction.java b/Mage.Sets/src/mage/sets/championsofkamigawa/CranialExtraction.java index 1754aeebd63..c86dbc47292 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/CranialExtraction.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/CranialExtraction.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,7 +20,7 @@ * 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. @@ -52,11 +52,10 @@ public class CranialExtraction extends CardImpl { public CranialExtraction(UUID ownerId) { super(ownerId, 105, "Cranial Extraction", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{3}{B}"); this.expansionSetCode = "CHK"; - this.subtype.add("Arcane"); + this.subtype.add("Arcane"); - - /* Name a nonland card. Search target player's graveyard, hand, and library for - * all cards with that name and exile them. Then that player shuffles his or her library. */ + /* Name a nonland card. Search target player's graveyard, hand, and library for + * all cards with that name and exile them. Then that player shuffles his or her library. */ this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new CranialExtractionEffect()); } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/KondaLordOfEiganjo.java b/Mage.Sets/src/mage/sets/championsofkamigawa/KondaLordOfEiganjo.java index e4d3e86fc1f..9898dd090e0 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/KondaLordOfEiganjo.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/KondaLordOfEiganjo.java @@ -25,17 +25,16 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.championsofkamigawa; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Rarity; import mage.MageInt; import mage.abilities.keyword.BushidoAbility; import mage.abilities.keyword.IndestructibleAbility; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; /** * @author Loki @@ -51,9 +50,11 @@ public class KondaLordOfEiganjo extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); + this.addAbility(VigilanceAbility.getInstance()); - this.addAbility(new BushidoAbility(5)); this.addAbility(IndestructibleAbility.getInstance()); + + this.addAbility(new BushidoAbility(5)); } public KondaLordOfEiganjo(final KondaLordOfEiganjo card) { diff --git a/Mage.Sets/src/mage/sets/coldsnap/JestersScepter.java b/Mage.Sets/src/mage/sets/coldsnap/JestersScepter.java index b348ea41b44..89b7b2dddba 100644 --- a/Mage.Sets/src/mage/sets/coldsnap/JestersScepter.java +++ b/Mage.Sets/src/mage/sets/coldsnap/JestersScepter.java @@ -244,8 +244,8 @@ class JestersScepterCounterEffect extends OneShotEffect { String nameOfExiledCardPayment = (String) game.getState().getValue(source.getSourceId() + "_nameOfExiledCardPayment"); String nameOfExiledCardPayment2 = (String) game.getState().getValue(source.getSourceId() + "_nameOfExiledCardPayment2"); if (nameOfExiledCardPayment != null) { - if (nameOfExiledCardPayment.matches(spell.getName()) - || nameOfExiledCardPayment2.matches(spell.getName())) { + if (nameOfExiledCardPayment.equals(spell.getCard().getName()) + || (nameOfExiledCardPayment2 != null) && nameOfExiledCardPayment2.equals(spell.getCard().getName())) { return game.getStack().counter(targetPointer.getFirst(game, source), source.getSourceId(), game); } } diff --git a/Mage.Sets/src/mage/sets/commander2014/Everglades.java b/Mage.Sets/src/mage/sets/commander2014/Everglades.java index 57054b36d58..9df83091628 100644 --- a/Mage.Sets/src/mage/sets/commander2014/Everglades.java +++ b/Mage.Sets/src/mage/sets/commander2014/Everglades.java @@ -69,7 +69,7 @@ public class Everglades extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeSourceUnlessPaysEffect(new ReturnToHandChosenControlledPermanentCost(new TargetControlledPermanent(1, 1, filter, true))))); // {tap}: Add {C}{B} to your mana pool. - this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new Mana(0, 0, 0, 0, 0, 1, 0, 1), new TapSourceCost())); + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new Mana(0, 0, 0, 0, 1, 0, 0, 1), new TapSourceCost())); } diff --git a/Mage.Sets/src/mage/sets/commander2014/MasterworkOfIngenuity.java b/Mage.Sets/src/mage/sets/commander2014/MasterworkOfIngenuity.java index aa2917c646f..de6b5aab55f 100644 --- a/Mage.Sets/src/mage/sets/commander2014/MasterworkOfIngenuity.java +++ b/Mage.Sets/src/mage/sets/commander2014/MasterworkOfIngenuity.java @@ -43,7 +43,7 @@ import mage.filter.predicate.mageobject.SubtypePredicate; */ public class MasterworkOfIngenuity extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact"); + private static final FilterPermanent filter = new FilterPermanent("equipment"); static { filter.add(new CardTypePredicate(CardType.ARTIFACT)); diff --git a/Mage.Sets/src/mage/sets/conspiracy/IgnitionTeam.java b/Mage.Sets/src/mage/sets/conspiracy/IgnitionTeam.java new file mode 100644 index 00000000000..b85f110fe1f --- /dev/null +++ b/Mage.Sets/src/mage/sets/conspiracy/IgnitionTeam.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.conspiracy; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.permanent.token.Token; +import mage.target.common.TargetLandPermanent; + +/** + * + * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) + */ +public class IgnitionTeam extends CardImpl { + + public IgnitionTeam(UUID ownerId) { + super(ownerId, 34, "Ignition Team", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{5}{R}{R}"); + this.expansionSetCode = "CNS"; + this.subtype.add("Goblin"); + this.subtype.add("Warrior"); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Ignition Team enters the battlefield with X +1/+1 counters on it, where X is the number of tapped lands on the battlefield. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance(0), + new TappedLandsCount(), true), + "with X +1/+1 counters on it, where X is the number of tapped lands on the battlefield.")); + + // {2}{R}, Remove a +1/+1 counter from Ignition Team: Target land becomes a 4/4 red Elemental creature until end of turn. It's still a land. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureTargetEffect(new IgnitionTeamToken(), false, true, Duration.EndOfTurn), new ManaCostsImpl("{2}{R}")); + ability.addCost(new RemoveCountersSourceCost(CounterType.P1P1.createInstance(1))); + ability.addTarget(new TargetLandPermanent()); + this.addAbility(ability); + + } + + public IgnitionTeam(final IgnitionTeam card) { + super(card); + } + + @Override + public IgnitionTeam copy() { + return new IgnitionTeam(this); + } +} + +class TappedLandsCount implements DynamicValue { + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + if (sourceAbility != null) { + FilterLandPermanent filter = new FilterLandPermanent("tapped lands on the battlefield"); + filter.add(new TappedPredicate()); + return game.getBattlefield().count(filter, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game); + } + return 0; + } + + @Override + public DynamicValue copy() { + return new TappedLandsCount(); + } + + @Override + public String toString() { + return "1"; + } + + @Override + public String getMessage() { + return "tapped lands on the battlefield"; + } +} + +class IgnitionTeamToken extends Token { + + public IgnitionTeamToken() { + super("", "4/4 red Elemental creature"); + this.cardType.add(CardType.CREATURE); + this.getSubtype().add("Elemental"); + this.color.setRed(true); + + this.power = new MageInt(4); + this.toughness = new MageInt(4); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/darkascension/ChaliceOfDeath.java b/Mage.Sets/src/mage/sets/darkascension/ChaliceOfDeath.java index b1638323554..f4997e607d9 100644 --- a/Mage.Sets/src/mage/sets/darkascension/ChaliceOfDeath.java +++ b/Mage.Sets/src/mage/sets/darkascension/ChaliceOfDeath.java @@ -28,14 +28,13 @@ package mage.sets.darkascension; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; import mage.constants.Zone; import mage.target.TargetPlayer; @@ -51,7 +50,6 @@ public class ChaliceOfDeath extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.canTransform = true; // {tap}: Target player loses 5 life. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new LoseLifeTargetEffect(5), new TapSourceCost()); diff --git a/Mage.Sets/src/mage/sets/darkascension/CurseOfEchoes.java b/Mage.Sets/src/mage/sets/darkascension/CurseOfEchoes.java index dd08ebaf846..bab24b5133c 100644 --- a/Mage.Sets/src/mage/sets/darkascension/CurseOfEchoes.java +++ b/Mage.Sets/src/mage/sets/darkascension/CurseOfEchoes.java @@ -167,8 +167,9 @@ class CurseOfEchoesEffect extends OneShotEffect { @Override public String getText(Mode mode) { - StringBuilder sb = new StringBuilder(); - sb.append("Copy target ").append(mode.getTargets().get(0).getTargetName()).append(". You may choose new targets for the copy"); - return sb.toString(); + if (mode.getTargets().size() > 0) { + return "Copy target " + mode.getTargets().get(0).getTargetName() + ". You may choose new targets for the copy"; + } + return "No target"; } } diff --git a/Mage.Sets/src/mage/sets/dissension/BrainPry.java b/Mage.Sets/src/mage/sets/dissension/BrainPry.java new file mode 100644 index 00000000000..c6483472f60 --- /dev/null +++ b/Mage.Sets/src/mage/sets/dissension/BrainPry.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.dissension; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.NameACardEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPlayer; + +/** + * + * @author Styxo + */ +public class BrainPry extends CardImpl { + + public BrainPry(UUID ownerId) { + super(ownerId, 39, "Brain Pry", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{1}{B}"); + this.expansionSetCode = "DIS"; + + //Name a nonland card. Target player reveals his or her hand. That player discards a card with that name. If he or she can't, you draw a card. + this.getSpellAbility().addEffect((new NameACardEffect(NameACardEffect.TypeOfName.NON_LAND_NAME))); + this.getSpellAbility().addTarget(new TargetPlayer()); + this.getSpellAbility().addEffect(new BrainPryEffect()); + } + + public BrainPry(final BrainPry card) { + super(card); + } + + @Override + public BrainPry copy() { + return new BrainPry(this); + } +} + +class BrainPryEffect extends OneShotEffect { + + public BrainPryEffect() { + super(Outcome.Discard); + staticText = "Target player reveals his or her hand. That player discards a card with that name. If he or she can't, you draw a card"; + } + + public BrainPryEffect(final BrainPryEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + NameACardEffect.INFO_KEY); + if (targetPlayer != null && controller != null && sourceObject != null && cardName != null) { + Boolean hasDiscarded = false; + for (Card card : targetPlayer.getHand().getCards(game)) { + if (card.getName().equals(cardName)) { + targetPlayer.discard(card, source, game); + hasDiscarded = true; + break; + } + } + if (!hasDiscarded) { + controller.drawCards(1, game); + } + controller.lookAtCards(sourceObject.getName() + " Hand", targetPlayer.getHand(), game); + } + return true; + } + + @Override + public BrainPryEffect copy() { + return new BrainPryEffect(this); + } +} diff --git a/Mage.Sets/src/mage/sets/dissension/SlitheringShade.java b/Mage.Sets/src/mage/sets/dissension/SlitheringShade.java new file mode 100644 index 00000000000..ff22780547c --- /dev/null +++ b/Mage.Sets/src/mage/sets/dissension/SlitheringShade.java @@ -0,0 +1,84 @@ +/* + * 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.dissension; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.HellbentCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalAsThoughEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Styxo + */ +public class SlitheringShade extends CardImpl { + + public SlitheringShade(UUID ownerId) { + super(ownerId, 55, "Slithering Shade", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{B}"); + this.expansionSetCode = "DIS"; + this.subtype.add("Shade"); + + this.power = new MageInt(0); + this.toughness = new MageInt(1); + + //Defender + this.addAbility(DefenderAbility.getInstance()); + + //{B}: Slithering Shade gets +1/+1 until end of turn. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 1, Duration.EndOfTurn), new ManaCostsImpl("{B}"))); + + // Hellbent — Slithering Shade can attack as though it didn't have defender as long as you have no cards in hand. + Effect effect = new ConditionalAsThoughEffect( + new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.WhileOnBattlefield), + HellbentCondition.getInstance()); + effect.setText("Hellbent - {this} can attack as though it didn't have defender as long as you have no cards in hand"); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + + } + + public SlitheringShade(final SlitheringShade card) { + super(card); + } + + @Override + public SlitheringShade copy() { + return new SlitheringShade(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/AssembledAlphas.java b/Mage.Sets/src/mage/sets/eldritchmoon/AssembledAlphas.java new file mode 100644 index 00000000000..800b22767d7 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/AssembledAlphas.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.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BlocksOrBecomesBlockedByCreatureTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DamageTargetControllerEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author fireshoes + */ +public class AssembledAlphas extends CardImpl { + + public AssembledAlphas(UUID ownerId) { + super(ownerId, 117, "Assembled Alphas", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{5}{R}"); + this.expansionSetCode = "EMN"; + this.subtype.add("Wolf"); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Whenever Assembled Alphas blocks or becomes blocked by a creature, Assembled Alphas deals 3 damage to that creature and 3 damage to that creature's controller. + Ability ability = new BlocksOrBecomesBlockedByCreatureTriggeredAbility(new DamageTargetEffect(3, true, "that creature"), false); + Effect effect = new DamageTargetControllerEffect(2); + effect.setText("and 3 damage to that creature's controller"); + ability.addEffect(effect); + this.addAbility(ability); + } + + public AssembledAlphas(final AssembledAlphas card) { + super(card); + } + + @Override + public AssembledAlphas copy() { + return new AssembledAlphas(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/AuroraOfEmrakul.java b/Mage.Sets/src/mage/sets/eldritchmoon/AuroraOfEmrakul.java new file mode 100644 index 00000000000..aea5f12ea0c --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/AuroraOfEmrakul.java @@ -0,0 +1,75 @@ +/* + * 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.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author fireshoes + */ +public class AuroraOfEmrakul extends CardImpl { + + public AuroraOfEmrakul(UUID ownerId) { + super(ownerId, 193, "Aurora of Emrakul", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, ""); + this.expansionSetCode = "EMN"; + this.subtype.add("Eldrazi"); + this.subtype.add("Reflection"); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // this card is the second face of double-faced card + this.nightCard = true; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Whenever Aurora of Emrakul attacks, each opponent loses 3 life. + this.addAbility(new AttacksTriggeredAbility(new LoseLifeOpponentsEffect(3),false)); + } + + public AuroraOfEmrakul(final AuroraOfEmrakul card) { + super(card); + } + + @Override + public AuroraOfEmrakul copy() { + return new AuroraOfEmrakul(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/BrunaTheFadingLight.java b/Mage.Sets/src/mage/sets/eldritchmoon/BrunaTheFadingLight.java new file mode 100644 index 00000000000..9023411f2dd --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/BrunaTheFadingLight.java @@ -0,0 +1,91 @@ +/* + * 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.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.effects.common.CastSourceTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.target.common.TargetCardInYourGraveyard; + +/** + * + * @author fireshoes + */ +public class BrunaTheFadingLight extends CardImpl { + + private static final FilterCard filter = new FilterCard("Angel or Human creature card"); + + static { + filter.add(Predicates.and(new CardTypePredicate(CardType.CREATURE), + (Predicates.or(new SubtypePredicate("Human"), + (new SubtypePredicate("Angel")))))); + } + + public BrunaTheFadingLight(UUID ownerId) { + super(ownerId, 15, "Bruna, the Fading Light", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{5}{W}{W}"); + this.expansionSetCode = "EMN"; + this.supertype.add("Legendary"); + this.subtype.add("Angel"); + this.subtype.add("Horror"); + this.power = new MageInt(5); + this.toughness = new MageInt(7); + + // When you cast Bruna, the Fading Light, you may return target Angel or Human creature card from your graveyard to the battlefield. + Ability ability = new CastSourceTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), true); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // (Melds with Gisela, the Broken Blade.) + } + + public BrunaTheFadingLight(final BrunaTheFadingLight card) { + super(card); + } + + @Override + public BrunaTheFadingLight copy() { + return new BrunaTheFadingLight(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/CemeteryRecruitment.java b/Mage.Sets/src/mage/sets/eldritchmoon/CemeteryRecruitment.java new file mode 100644 index 00000000000..64ca11e0617 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/CemeteryRecruitment.java @@ -0,0 +1,100 @@ +/* + * 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.eldritchmoon; + +import java.util.UUID; +import mage.abilities.Ability; +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.common.FilterCreatureCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +/** + * + * @author fireshoes + */ +public class CemeteryRecruitment extends CardImpl { + + public CemeteryRecruitment(UUID ownerId) { + super(ownerId, 83, "Cemetery Recruitment", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{1}{B}"); + this.expansionSetCode = "EMN"; + + // Return target creature card from your graveyard to your hand. If it's a Zombie card, draw a card. + this.getSpellAbility().addEffect(new CemeteryRecruitmentEffect()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 1, new FilterCreatureCard("creature card from your graveyard"))); + } + + public CemeteryRecruitment(final CemeteryRecruitment card) { + super(card); + } + + @Override + public CemeteryRecruitment copy() { + return new CemeteryRecruitment(this); + } +} + +class CemeteryRecruitmentEffect extends OneShotEffect { + + public CemeteryRecruitmentEffect() { + super(Outcome.Benefit); + staticText = "Return target creature card from your graveyard to your hand. If it's a Zombie card, draw a card"; + } + + public CemeteryRecruitmentEffect(final CemeteryRecruitmentEffect effect) { + super(effect); + } + + @Override + public CemeteryRecruitmentEffect copy() { + return new CemeteryRecruitmentEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Card card = game.getCard(targetPointer.getFirst(game, source)); + if (card != null) { + if (controller.moveCards(card, Zone.HAND, source, game) + && card.getSubtype().contains("Zombie")) { + controller.drawCards(1, game); + } + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/ChillingGrasp.java b/Mage.Sets/src/mage/sets/eldritchmoon/ChillingGrasp.java new file mode 100644 index 00000000000..3c2131dd449 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/ChillingGrasp.java @@ -0,0 +1,67 @@ +/* + * 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.eldritchmoon; + +import java.util.UUID; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.MadnessAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author fireshoes + */ +public class ChillingGrasp extends CardImpl { + + public ChillingGrasp(UUID ownerId) { + super(ownerId, 50, "Chilling Grasp", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{U}"); + this.expansionSetCode = "EMN"; + + // Tap up to two target creatures. Those creatures don't untap during their controller's next uptap step. + this.getSpellAbility().addEffect(new TapTargetEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2)); + this.getSpellAbility().addEffect(new DontUntapInControllersNextUntapStepTargetEffect("Those creatures")); + + // Madness {3}{U} + this.addAbility(new MadnessAbility(this, new ManaCostsImpl("{3}{U}"))); + } + + public ChillingGrasp(final ChillingGrasp card) { + super(card); + } + + @Override + public ChillingGrasp copy() { + return new ChillingGrasp(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/CryptolithFragment.java b/Mage.Sets/src/mage/sets/eldritchmoon/CryptolithFragment.java new file mode 100644 index 00000000000..fd9734f806b --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/CryptolithFragment.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.eldritchmoon; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.condition.common.TenOrLessLifeCondition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.common.LoseLifeAllPlayersEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.TransformAbility; +import mage.abilities.mana.AnyColorManaAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; + +/** + * + * @author fireshoes + */ +public class CryptolithFragment extends CardImpl { + + public CryptolithFragment(UUID ownerId) { + super(ownerId, 193, "Cryptolith Fragment", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "EMN"; + + this.canTransform = true; + this.secondSideCard = new AuroraOfEmrakul(ownerId); + + // Cryptolith Fragment enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add one mana of any color to your mana pool. Each player loses 1 life. + Ability AnyColorManaAbility = new AnyColorManaAbility(); + AnyColorManaAbility.addEffect(new LoseLifeAllPlayersEffect(1)); + this.addAbility(AnyColorManaAbility); + + // At the beginning of your upkeep, if each player has 10 or less life, transform Cryptolith Fragment. + this.addAbility(new TransformAbility()); + this.addAbility(new ConditionalTriggeredAbility( + new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.YOU, false), + new TenOrLessLifeCondition(TenOrLessLifeCondition.CheckType.EACH_PLAYER), + "At the beginning of your upkeep, if each player has 10 or less life, transform Cryptolith Fragment.")); + } + + public CryptolithFragment(final CryptolithFragment card) { + super(card); + } + + @Override + public CryptolithFragment copy() { + return new CryptolithFragment(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/DawnGryff.java b/Mage.Sets/src/mage/sets/eldritchmoon/DawnGryff.java new file mode 100644 index 00000000000..2ba43d32035 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/DawnGryff.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.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author fireshoes + */ +public class DawnGryff extends CardImpl { + + public DawnGryff(UUID ownerId) { + super(ownerId, 19, "Dawn Gryff", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{W}"); + this.expansionSetCode = "EMN"; + this.subtype.add("Hippogriff"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + } + + public DawnGryff(final DawnGryff card) { + super(card); + } + + @Override + public DawnGryff copy() { + return new DawnGryff(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/DocentOfPerfection.java b/Mage.Sets/src/mage/sets/eldritchmoon/DocentOfPerfection.java new file mode 100644 index 00000000000..16c1105c243 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/DocentOfPerfection.java @@ -0,0 +1,148 @@ +/* + * 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.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.game.Game; +import mage.game.permanent.token.Token; +import mage.players.Player; + +/** + * + * @author fireshoes + */ +public class DocentOfPerfection extends CardImpl { + + private static final FilterSpell filterSpell = new FilterSpell("instant or sorcery spell"); + + static { + filterSpell.add(Predicates.or( + new CardTypePredicate(CardType.INSTANT), + new CardTypePredicate(CardType.SORCERY))); + } + + public DocentOfPerfection(UUID ownerId) { + super(ownerId, 56, "Docent of Perfection", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); + this.expansionSetCode = "EMN"; + this.subtype.add("Insect"); + this.subtype.add("Horror"); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + this.canTransform = true; + this.secondSideCard = new FinalIteration(ownerId); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever you cast an instant or sorcery spell, put a 1/1 blue Human Wizard creature token onto the battlefield. + // Then if you control three or more Wizards, transform Docent of Perfection. + this.addAbility(new TransformAbility()); + Effect effect = new DocentOfPerfectionEffect(); + Ability ability = new SpellCastControllerTriggeredAbility(new CreateTokenEffect(new HumanWizardToken()), filterSpell, false); + ability.addEffect(effect); + this.addAbility(ability); + } + + public DocentOfPerfection(final DocentOfPerfection card) { + super(card); + } + + @Override + public DocentOfPerfection copy() { + return new DocentOfPerfection(this); + } +} + +class DocentOfPerfectionEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterPermanent("Wizards"); + + static { + filter.add(new SubtypePredicate("Wizards")); + filter.add(new ControllerPredicate(TargetController.YOU)); + } + + public DocentOfPerfectionEffect() { + super(Outcome.Benefit); + staticText = "Then if you control three or more Wizards, transform {this}"; + } + + public DocentOfPerfectionEffect(final DocentOfPerfectionEffect effect) { + super(effect); + } + + @Override + public DocentOfPerfectionEffect copy() { + return new DocentOfPerfectionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + if (game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game) >= 3) { + return new TransformSourceEffect(true).apply(game, source); + } + } + return false; + } +} + +class HumanWizardToken extends Token { + + public HumanWizardToken() { + super("Human Wizard", "1/1 blue Human Wizard creature token"); + cardType.add(CardType.CREATURE); + subtype.add("Human"); + subtype.add("Wizard"); + color.setBlue(true); + power = new MageInt(1); + toughness = new MageInt(1); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/EmrakulThePromisedEnd.java b/Mage.Sets/src/mage/sets/eldritchmoon/EmrakulThePromisedEnd.java new file mode 100644 index 00000000000..6fdf82899d4 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/EmrakulThePromisedEnd.java @@ -0,0 +1,173 @@ +/* + * 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.eldritchmoon; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CastSourceTriggeredAbility; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.ProtectionAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.CostModificationType; +import mage.constants.Duration; +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.turn.TurnMod; +import mage.players.Player; +import mage.target.common.TargetOpponent; +import mage.util.CardUtil; + +/** + * + * @author emerald000 + */ +public class EmrakulThePromisedEnd extends CardImpl { + + private static final FilterCard filter = new FilterCard("instants"); + static { + filter.add(new CardTypePredicate(CardType.INSTANT)); + } + + public EmrakulThePromisedEnd(UUID ownerId) { + super(ownerId, 6, "Emrakul, the Promised End", Rarity.MYTHIC, new CardType[]{CardType.CREATURE}, "{13}"); + this.expansionSetCode = "EMN"; + this.supertype.add("Legendary"); + this.subtype.add("Eldrazi"); + this.power = new MageInt(13); + this.toughness = new MageInt(13); + + // Emrakul, the Promised End costs {1} less to cast for each card type among cards in your graveyard. + Ability ability = new SimpleStaticAbility(Zone.ALL, new EmrakulThePromisedEndCostReductionEffect()); + ability.setRuleAtTheTop(true); + this.addAbility(ability); + + // When you cast Emrakul, you gain control of target opponent during that player's next turn. After that turn, that player takes an extra turn. + ability = new CastSourceTriggeredAbility(new EmrakulThePromisedEndGainControlEffect()); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Protection from instants + this.addAbility(new ProtectionAbility(filter)); + } + + public EmrakulThePromisedEnd(final EmrakulThePromisedEnd card) { + super(card); + } + + @Override + public EmrakulThePromisedEnd copy() { + return new EmrakulThePromisedEnd(this); + } +} + +class EmrakulThePromisedEndCostReductionEffect extends CostModificationEffectImpl { + + EmrakulThePromisedEndCostReductionEffect() { + super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); + staticText = "{this} costs {1} less to cast for each card type among cards in your graveyard"; + } + + EmrakulThePromisedEndCostReductionEffect(EmrakulThePromisedEndCostReductionEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Set foundCardTypes = new HashSet<>(8); + for (Card card : controller.getGraveyard().getCards(game)) { + foundCardTypes.addAll(card.getCardType()); + } + CardUtil.reduceCost(abilityToModify, foundCardTypes.size()); + return true; + } + return false; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return abilityToModify instanceof SpellAbility + && abilityToModify.getSourceId().equals(source.getSourceId()) + && game.getCard(abilityToModify.getSourceId()) != null; + } + + @Override + public EmrakulThePromisedEndCostReductionEffect copy() { + return new EmrakulThePromisedEndCostReductionEffect(this); + } +} + +class EmrakulThePromisedEndGainControlEffect extends OneShotEffect { + + EmrakulThePromisedEndGainControlEffect() { + super(Outcome.GainControl); + this.staticText = "you gain control of target opponent during that player's next turn. After that turn, that player takes an extra turn"; + } + + EmrakulThePromisedEndGainControlEffect(final EmrakulThePromisedEndGainControlEffect effect) { + super(effect); + } + + @Override + public EmrakulThePromisedEndGainControlEffect copy() { + return new EmrakulThePromisedEndGainControlEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source)); + if (controller != null && targetPlayer != null) { + game.getState().getTurnMods().add(new TurnMod(targetPlayer.getId(), false)); + game.getState().getTurnMods().add(new TurnMod(targetPlayer.getId(), controller.getId())); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/EmrakulsInfluence.java b/Mage.Sets/src/mage/sets/eldritchmoon/EmrakulsInfluence.java new file mode 100644 index 00000000000..3cb97fa6d34 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/EmrakulsInfluence.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.eldritchmoon; + +import java.util.UUID; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.Filter; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.filter.predicate.mageobject.SubtypePredicate; + +/** + * + * @author fireshoes + */ +public class EmrakulsInfluence extends CardImpl { + + private static final FilterSpell filterSpell = new FilterSpell("Eldrazi creature spell with converted mana cost 7 or greater"); + + static { + filterSpell.add(new SubtypePredicate("Eldrazi")); + filterSpell.add(new ConvertedManaCostPredicate(Filter.ComparisonType.GreaterThan, 6)); + } + + public EmrakulsInfluence(UUID ownerId) { + super(ownerId, 157, "Emrakul's Influence", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}{G}"); + this.expansionSetCode = "EMN"; + + // Whenever you cast an Eldrazi creature spell with converted mana cost 7 or greater, draw two cards. + this.addAbility(new SpellCastControllerTriggeredAbility(new DrawCardSourceControllerEffect(2), filterSpell, false)); + } + + public EmrakulsInfluence(final EmrakulsInfluence card) { + super(card); + } + + @Override + public EmrakulsInfluence copy() { + return new EmrakulsInfluence(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/FinalIteration.java b/Mage.Sets/src/mage/sets/eldritchmoon/FinalIteration.java new file mode 100644 index 00000000000..b08102f9dbf --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/FinalIteration.java @@ -0,0 +1,103 @@ +/* + * 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.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterSpell; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.permanent.ControllerPredicate; + +/** + * + * @author fireshoes + */ +public class FinalIteration extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Wizards"); + private static final FilterSpell filterSpell = new FilterSpell("instant or sorcery spell"); + + static { + filter.add(new SubtypePredicate("Wizard")); + filter.add(new ControllerPredicate(TargetController.YOU)); + filterSpell.add(Predicates.or( + new CardTypePredicate(CardType.INSTANT), + new CardTypePredicate(CardType.SORCERY))); + } + + public FinalIteration(UUID ownerId) { + super(ownerId, 56, "Final Iteration", Rarity.RARE, new CardType[]{CardType.CREATURE}, ""); + this.expansionSetCode = "EMN"; + this.subtype.add("Eldrazi"); + this.subtype.add("Insect"); + this.power = new MageInt(6); + this.toughness = new MageInt(5); + + // this card is the second face of double-faced card + this.nightCard = true; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Wizards you control get +2/+1 and have flying. + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(2, 1, Duration.WhileOnBattlefield, filter, true)); + Effect effect = new GainAbilityAllEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield, filter, false); + effect.setText("and have flying"); + ability.addEffect(effect); + this.addAbility(ability); + + // Whenever you cast an instant or sorcery spell, put a 1/1 blue Human Wizard creature token onto the battlefield. + this.addAbility(new SpellCastControllerTriggeredAbility(new CreateTokenEffect(new HumanWizardToken()), filterSpell, false)); + } + + public FinalIteration(final FinalIteration card) { + super(card); + } + + @Override + public FinalIteration copy() { + return new FinalIteration(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/GalvanicBombardment.java b/Mage.Sets/src/mage/sets/eldritchmoon/GalvanicBombardment.java new file mode 100644 index 00000000000..d0d1039a383 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/GalvanicBombardment.java @@ -0,0 +1,113 @@ +/* + * 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.eldritchmoon; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +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.FilterCard; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author fireshoes + */ +public class GalvanicBombardment extends CardImpl { + + private static final FilterCard filter = new FilterCard("2 plus the number of cards named Galvanic Bombardment"); + + static { + filter.add(new NamePredicate("Galvanic Bombardment")); + } + + public GalvanicBombardment(UUID ownerId) { + super(ownerId, 129, "Galvanic Bombardment", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{R}"); + this.expansionSetCode = "EMN"; + + // Galvanic Bombardment deals X damage to target creature, where X is 2 plus the number of cards named Galvanic Bombardment in your graveyard. + Effect effect = new DamageTargetEffect(new GalvanicBombardmentCardsInControllerGraveyardCount(filter)); + effect.setText("{this} deals X damage to target creature, where X is 2 plus the number of cards named {source} in your graveyard"); + this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + public GalvanicBombardment(final GalvanicBombardment card) { + super(card); + } + + @Override + public GalvanicBombardment copy() { + return new GalvanicBombardment(this); + } +} + +class GalvanicBombardmentCardsInControllerGraveyardCount implements DynamicValue { + + private final FilterCard filter; + + public GalvanicBombardmentCardsInControllerGraveyardCount(FilterCard filter) { + this.filter = filter; + } + + private GalvanicBombardmentCardsInControllerGraveyardCount(GalvanicBombardmentCardsInControllerGraveyardCount dynamicValue) { + this.filter = dynamicValue.filter; + } + + @Override + public int calculate(Game game, Ability source, Effect effect) { + int amount = 0; + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + amount += controller.getGraveyard().count(filter, source.getSourceId(), source.getControllerId(), game); + } + return amount + 2; + } + + @Override + public GalvanicBombardmentCardsInControllerGraveyardCount copy() { + return new GalvanicBombardmentCardsInControllerGraveyardCount(this); + } + + @Override + public String toString() { + return "1"; + } + + @Override + public String getMessage() { + return filter.getMessage() + " in your graveyard"; + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/GnarlwoodDryad.java b/Mage.Sets/src/mage/sets/eldritchmoon/GnarlwoodDryad.java new file mode 100644 index 00000000000..80ea32dc998 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/GnarlwoodDryad.java @@ -0,0 +1,77 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.DeliriumCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author fireshoes + */ +public class GnarlwoodDryad extends CardImpl { + + public GnarlwoodDryad(UUID ownerId) { + super(ownerId, 159, "Gnarlwood Dryad", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{G}"); + this.expansionSetCode = "EMN"; + this.subtype.add("Dryad"); + this.subtype.add("Horror"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Delirium &mdash Gnarlwood Dryad gets +2/+2 as long as there are four or more card types among cards in your graveyard. + ConditionalContinuousEffect effect = new ConditionalContinuousEffect( + new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), DeliriumCondition.getInstance(), + "Delirium — {this} gets +2/+2 as long as there are four or more card types among cards in your graveyard."); + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); + this.addAbility(ability); + } + + public GnarlwoodDryad(final GnarlwoodDryad card) { + super(card); + } + + @Override + public GnarlwoodDryad copy() { + return new GnarlwoodDryad(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/GrislyAnglerfish.java b/Mage.Sets/src/mage/sets/eldritchmoon/GrislyAnglerfish.java new file mode 100644 index 00000000000..bf7da3417c7 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/GrislyAnglerfish.java @@ -0,0 +1,109 @@ +/* + * 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.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.RequirementEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author fireshoes + */ +public class GrislyAnglerfish extends CardImpl { + + public GrislyAnglerfish(UUID ownerId) { + super(ownerId, 63, "Grisly Anglerfish", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, ""); + this.expansionSetCode = "EMN"; + this.subtype.add("Eldrazi"); + this.subtype.add("Fish"); + this.power = new MageInt(4); + this.toughness = new MageInt(5); + + // this card is the second face of double-faced card + this.nightCard = true; + + // {6}: Creatures your opponents control attack this turn if able. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GrislyAnglerfishMustAttackEffect(), new ManaCostsImpl("{6}"))); + } + + public GrislyAnglerfish(final GrislyAnglerfish card) { + super(card); + } + + @Override + public GrislyAnglerfish copy() { + return new GrislyAnglerfish(this); + } +} + +class GrislyAnglerfishMustAttackEffect extends RequirementEffect { + + public GrislyAnglerfishMustAttackEffect() { + super(Duration.EndOfTurn); + staticText = "Creatures your opponents control attack this turn if able"; + } + + public GrislyAnglerfishMustAttackEffect(final GrislyAnglerfishMustAttackEffect effect) { + super(effect); + } + + @Override + public GrislyAnglerfishMustAttackEffect copy() { + return new GrislyAnglerfishMustAttackEffect(this); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + if (game.getOpponents(source.getControllerId()).contains(permanent.getControllerId())) { + return true; + } + return false; + } + + @Override + public boolean mustAttack(Game game) { + return true; + } + + @Override + public boolean mustBlock(Game game) { + return false; + } + +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/GrizzledAngler.java b/Mage.Sets/src/mage/sets/eldritchmoon/GrizzledAngler.java new file mode 100644 index 00000000000..1b59133eefa --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/GrizzledAngler.java @@ -0,0 +1,112 @@ +/* + * 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.eldritchmoon; + +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.effects.OneShotEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.ColorlessPredicate; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author fireshoes + */ +public class GrizzledAngler extends CardImpl { + + public GrizzledAngler(UUID ownerId) { + super(ownerId, 63, "Grizzled Angler", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{U}"); + this.expansionSetCode = "EMN"; + this.subtype.add("Human"); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + this.canTransform = true; + this.secondSideCard = new GrislyAnglerfish(ownerId); + + // {T}: Put the top two cards of your library into your graveyard. Then if there is a colorless creature card in your graveyard, transform Grizzled Angler. + this.addAbility(new TransformAbility()); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GrizzledAnglerEffect(), new TapSourceCost())); + } + + public GrizzledAngler(final GrizzledAngler card) { + super(card); + } + + @Override + public GrizzledAngler copy() { + return new GrizzledAngler(this); + } +} + +class GrizzledAnglerEffect extends OneShotEffect { + + private static final FilterCreatureCard filter = new FilterCreatureCard("a colorless creature card in your graveyard"); + + static { + filter.add(new ColorlessPredicate()); + } + + public GrizzledAnglerEffect() { + super(Outcome.Benefit); + staticText = "Put the top two cards of your library into your graveyard. Then if there is a colorless creature card in your graveyard, transform {this}"; + } + + public GrizzledAnglerEffect(final GrizzledAnglerEffect effect) { + super(effect); + } + + @Override + public GrizzledAnglerEffect copy() { + return new GrizzledAnglerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + controller.moveCards(controller.getLibrary().getTopCards(game, 2), Zone.GRAVEYARD, source, game); + if (controller.getGraveyard().count(filter, source.getSourceId(), source.getControllerId(), game) >= 1) { + return new TransformSourceEffect(true).apply(game, source); + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/HanweirGarrison.java b/Mage.Sets/src/mage/sets/eldritchmoon/HanweirGarrison.java new file mode 100644 index 00000000000..4bf7ecf3a7c --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/HanweirGarrison.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.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.game.permanent.token.Token; + +/** + * + * @author fireshoes + */ +public class HanweirGarrison extends CardImpl { + + public HanweirGarrison(UUID ownerId) { + super(ownerId, 130, "Hanweir Garrison", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{R}"); + this.expansionSetCode = "EMN"; + this.subtype.add("Human"); + this.subtype.add("Soldier"); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Whenever Hanweir Garrison attacks, put two 1/1 red Human creature tokens onto the battlefield tapped and attacking. + this.addAbility(new AttacksTriggeredAbility(new CreateTokenEffect(new RedHumanToken(), 2, true, true), false)); + + // (Melds with Hanweir Battlements.) + } + + public HanweirGarrison(final HanweirGarrison card) { + super(card); + } + + @Override + public HanweirGarrison copy() { + return new HanweirGarrison(this); + } +} + +class RedHumanToken extends Token { + + public RedHumanToken() { + super("Human", "1/1 red Human creature token"); + this.cardType.add(CardType.CREATURE); + this.subtype.add("Human"); + + this.color = ObjectColor.RED; + this.power = new MageInt(1); + this.toughness = new MageInt(1); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/ItThatRidesAsOne.java b/Mage.Sets/src/mage/sets/eldritchmoon/ItThatRidesAsOne.java new file mode 100644 index 00000000000..9037c71a917 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/ItThatRidesAsOne.java @@ -0,0 +1,72 @@ +/* + * 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.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author fireshoes + */ +public class ItThatRidesAsOne extends CardImpl { + + public ItThatRidesAsOne(UUID ownerId) { + super(ownerId, 33, "It That Rides as One", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, ""); + this.expansionSetCode = "EMN"; + this.subtype.add("Eldrazi"); + this.subtype.add("Horror"); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // this card is the second face of double-faced card + this.nightCard = true; + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + // Trample + this.addAbility(TrampleAbility.getInstance()); + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + } + + public ItThatRidesAsOne(final ItThatRidesAsOne card) { + super(card); + } + + @Override + public ItThatRidesAsOne copy() { + return new ItThatRidesAsOne(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/LoneRider.java b/Mage.Sets/src/mage/sets/eldritchmoon/LoneRider.java new file mode 100644 index 00000000000..63a123a751f --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/LoneRider.java @@ -0,0 +1,109 @@ +/* + * 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.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.IntCompareCondition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.game.Game; +import mage.watchers.common.PlayerGainedLifeWatcher; + +/** + * + * @author fireshoes + */ +public class LoneRider extends CardImpl { + + public LoneRider(UUID ownerId) { + super(ownerId, 33, "Lone Rider", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{W}"); + this.expansionSetCode = "EMN"; + this.subtype.add("Human"); + this.subtype.add("Knight"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + this.canTransform = true; + this.secondSideCard = new ItThatRidesAsOne(ownerId); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // At the beginning of the end step, if you gained 3 or more life this turn, transform Lone Rider. + this.addAbility(new TransformAbility()); + TriggeredAbility ability = new BeginningOfEndStepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); + this.addAbility(new ConditionalTriggeredAbility(ability, new YouGainedLifeCondition(Condition.ComparisonType.GreaterThan, 2), + "At the beginning of the end step, if you gained 3 or more life this turn, transform {this}")); + } + + public LoneRider(final LoneRider card) { + super(card); + } + + @Override + public LoneRider copy() { + return new LoneRider(this); + } +} + +class YouGainedLifeCondition extends IntCompareCondition { + + public YouGainedLifeCondition(Condition.ComparisonType type, int value) { + super(type, value); + } + + @Override + protected int getInputValue(Game game, Ability source) { + int gainedLife = 0; + PlayerGainedLifeWatcher watcher = (PlayerGainedLifeWatcher) game.getState().getWatchers().get("PlayerGainedLifeWatcher"); + if (watcher != null) { + gainedLife = watcher.getLiveGained(source.getControllerId()); + } + return gainedLife; + } + + @Override + public String toString() { + return "if you gained 3 or more life this turn "; + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/LongRoadHome.java b/Mage.Sets/src/mage/sets/eldritchmoon/LongRoadHome.java new file mode 100644 index 00000000000..7e95f34d55a --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/LongRoadHome.java @@ -0,0 +1,200 @@ +/* + * 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.eldritchmoon; + +import java.util.UUID; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author fireshoes + */ +public class LongRoadHome extends CardImpl { + + public LongRoadHome(UUID ownerId) { + super(ownerId, 34, "Long Road Home", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{W}"); + this.expansionSetCode = "EMN"; + + // Exile target creature. At the beginning of the next end step, return that card to the battlefield under its owner's control with a +1/+1 counter on it. + this.getSpellAbility().addEffect(new LongRoadHomeEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + public LongRoadHome(final LongRoadHome card) { + super(card); + } + + @Override + public LongRoadHome copy() { + return new LongRoadHome(this); + } +} + +class LongRoadHomeEffect extends OneShotEffect { + + private static final String effectText = "Exile target creature. At the beginning of the next end step, return that card to the battlefield under its owner's control with a +1/+1 counter on it"; + + LongRoadHomeEffect() { + super(Outcome.Benefit); + staticText = effectText; + } + + LongRoadHomeEffect(LongRoadHomeEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent != null) { + if (permanent.moveToExile(source.getSourceId(), "Otherworldly Journey", source.getSourceId(), game)) { + ExileZone exile = game.getExile().getExileZone(source.getSourceId()); + // only if permanent is in exile (tokens would be stop to exist) + if (exile != null && !exile.isEmpty()) { + Card card = game.getCard(permanent.getId()); + if (card != null) { + //create delayed triggered ability + DelayedTriggeredAbility delayedAbility + = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new LongRoadHomeReturnFromExileEffect(new MageObjectReference(card, game))); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game), game); + game.addDelayedTriggeredAbility(delayedAbility); + } + } + return true; + } + } + return false; + } + + @Override + public LongRoadHomeEffect copy() { + return new LongRoadHomeEffect(this); + } + +} + +class LongRoadHomeReturnFromExileEffect extends OneShotEffect { + + MageObjectReference objectToReturn; + + public LongRoadHomeReturnFromExileEffect(MageObjectReference objectToReturn) { + super(Outcome.PutCardInPlay); + this.objectToReturn = objectToReturn; + staticText = "return that card to the battlefield under its owner's control with a +1/+1 counter on it"; + } + + public LongRoadHomeReturnFromExileEffect(final LongRoadHomeReturnFromExileEffect effect) { + super(effect); + this.objectToReturn = effect.objectToReturn; + } + + @Override + public LongRoadHomeReturnFromExileEffect copy() { + return new LongRoadHomeReturnFromExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Card card = game.getCard(objectToReturn.getSourceId()); + if (card != null && objectToReturn.refersTo(card, game)) { + Player owner = game.getPlayer(card.getOwnerId()); + if (owner != null) { + game.addEffect(new LongRoadHomeEntersBattlefieldEffect(objectToReturn), source); + owner.moveCards(card, Zone.BATTLEFIELD, source, game, false, false, true, null); + } + } + return true; + } +} + +class LongRoadHomeEntersBattlefieldEffect extends ReplacementEffectImpl { + + MageObjectReference objectToReturn; + + public LongRoadHomeEntersBattlefieldEffect(MageObjectReference objectToReturn) { + super(Duration.Custom, Outcome.BoostCreature); + this.objectToReturn = objectToReturn; + staticText = "that card returns to the battlefield with a +1/+1 counter on it"; + } + + public LongRoadHomeEntersBattlefieldEffect(LongRoadHomeEntersBattlefieldEffect effect) { + super(effect); + this.objectToReturn = effect.objectToReturn; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return EventType.ENTERS_THE_BATTLEFIELD.equals(event.getType()); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getType() == EventType.ENTERS_THE_BATTLEFIELD) { + return event.getTargetId().equals(objectToReturn.getSourceId()); + } + return false; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); + if (permanent != null) { + permanent.addCounters(CounterType.P1P1.createInstance(), game); + discard(); // use only once + } + return false; + } + + @Override + public LongRoadHomeEntersBattlefieldEffect copy() { + return new LongRoadHomeEntersBattlefieldEffect(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/MidnightScavengers.java b/Mage.Sets/src/mage/sets/eldritchmoon/MidnightScavengers.java new file mode 100644 index 00000000000..b03beb3e676 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/MidnightScavengers.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.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.Filter; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; +import mage.target.Target; +import mage.target.common.TargetCardInYourGraveyard; + +/** + * + * @author fireshoes + */ +public class MidnightScavengers extends CardImpl { + + private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with converted mana cost 3 or less from your graveyard"); + + static { + filter.add(new ConvertedManaCostPredicate(Filter.ComparisonType.LessThan, 4)); + } + + public MidnightScavengers(UUID ownerId) { + super(ownerId, 96, "Midnight Scavengers", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{4}{B}"); + this.expansionSetCode = "EMN"; + this.subtype.add("Human"); + this.subtype.add("Rogue"); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Midnight Scavengers enters the battlefield, you may return target creature card with converted mana cost 3 or less from your graveyard to your hand. + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect(), true); + Target target = new TargetCardInYourGraveyard(filter); + ability.addTarget(target); + this.addAbility(ability); + + // (Melds with Graf Rats.) + } + + public MidnightScavengers(final MidnightScavengers card) { + super(card); + } + + @Override + public MidnightScavengers copy() { + return new MidnightScavengers(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/NiblisOfFrost.java b/Mage.Sets/src/mage/sets/eldritchmoon/NiblisOfFrost.java new file mode 100644 index 00000000000..35a0c249796 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/NiblisOfFrost.java @@ -0,0 +1,93 @@ +/* + * 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.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.ProwessAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.filter.FilterSpell; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author fireshoes + */ +public class NiblisOfFrost extends CardImpl { + + private static final FilterSpell filterSpell = new FilterSpell("instant or sorcery spell"); + private static final FilterCreaturePermanent filterCreature = new FilterCreaturePermanent("creature an opponent controls"); + + static { + filterCreature.add(new ControllerPredicate(TargetController.OPPONENT)); + filterSpell.add(Predicates.or( + new CardTypePredicate(CardType.INSTANT), + new CardTypePredicate(CardType.SORCERY))); + } + + public NiblisOfFrost(UUID ownerId) { + super(ownerId, 72, "Niblis of Frost", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{U}{U}"); + this.expansionSetCode = "EMN"; + this.subtype.add("Spirit"); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Prowess + this.addAbility(new ProwessAbility()); + + // Whenever you cast an instant or sorcery spell, tap target creature an opponent controls. That creature doesn't untap during its controller's next untap step. + Ability ability = new SpellCastControllerTriggeredAbility(new TapTargetEffect(), filterSpell, false); + ability.addTarget(new TargetCreaturePermanent(filterCreature)); + ability.addEffect(new DontUntapInControllersNextUntapStepTargetEffect("That creature")); + this.addAbility(ability); + } + + public NiblisOfFrost(final NiblisOfFrost card) { + super(card); + } + + @Override + public NiblisOfFrost copy() { + return new NiblisOfFrost(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/NoosegrafMob.java b/Mage.Sets/src/mage/sets/eldritchmoon/NoosegrafMob.java new file mode 100644 index 00000000000..d6ef211a933 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/NoosegrafMob.java @@ -0,0 +1,107 @@ +/* + * 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.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SpellCastAllTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.ZombieToken; +import mage.players.Player; + +/** + * + * @author fireshoes + */ +public class NoosegrafMob extends CardImpl { + + public NoosegrafMob(UUID ownerId) { + super(ownerId, 98, "Noosegraf Mob", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{4}{B}{B}"); + this.expansionSetCode = "EMN"; + this.subtype.add("Zombie"); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Noosegraf Mob enters the battlefield with five +1/+1 counters on it. + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(5)), + "{this} enters the battlefield with five +1/+1 counters on it")); + + // Whenever a player casts a spell, remove a +1/+1 counter from Noosegraf Mob. If you do, put a 2/2 black Zombie creature token onto the battlefield. + this.addAbility(new SpellCastAllTriggeredAbility(new NoosegrafMobEffect(), false)); + } + + public NoosegrafMob(final NoosegrafMob card) { + super(card); + } + + @Override + public NoosegrafMob copy() { + return new NoosegrafMob(this); + } +} + +class NoosegrafMobEffect extends OneShotEffect { + + public NoosegrafMobEffect() { + super(Outcome.Benefit); + staticText = "remove a +1/+1 counter from Noosegraf Mob. If you do, put a 2/2 black Zombie creature token onto the battlefield"; + } + + public NoosegrafMobEffect(final NoosegrafMobEffect effect) { + super(effect); + } + + @Override + public NoosegrafMobEffect copy() { + return new NoosegrafMobEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null && permanent != null && permanent.getCounters().getCount(CounterType.P1P1) > 0) { + permanent.removeCounters(CounterType.P1P1.createInstance(), game); + Effect effect = new CreateTokenEffect(new ZombieToken()); + return effect.apply(game, source); + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/SanctifierOfSouls.java b/Mage.Sets/src/mage/sets/eldritchmoon/SanctifierOfSouls.java new file mode 100644 index 00000000000..f80c39a5cdc --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/SanctifierOfSouls.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.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureCard; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.game.permanent.token.SpiritWhiteToken; +import mage.target.common.TargetCardInYourGraveyard; + +/** + * + * @author fireshoes + */ +public class SanctifierOfSouls extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("another creature"); + + static { + filter.add(new AnotherPredicate()); + } + + public SanctifierOfSouls(UUID ownerId) { + super(ownerId, 39, "Sanctifier of Souls", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{W}"); + this.expansionSetCode = "EMN"; + this.subtype.add("Human"); + this.subtype.add("Cleric"); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Whenever another creature enters the battlefield under your control, Sanctifier of Souls gets +1/+1 until end of turn. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new BoostSourceEffect(1, 1, Duration.EndOfTurn), filter)); + + // {2}{W}, Exile a creature card from your graveyard: Put a 1/1 white Spirit creature token with flying onto the battlefield. + SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new CreateTokenEffect(new SpiritWhiteToken()), + new ManaCostsImpl("{2}{W}")); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(new FilterCreatureCard("a creature card from your graveyard")))); + this.addAbility(ability); + } + + public SanctifierOfSouls(final SanctifierOfSouls card) { + super(card); + } + + @Override + public SanctifierOfSouls copy() { + return new SanctifierOfSouls(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/TakeInventory.java b/Mage.Sets/src/mage/sets/eldritchmoon/TakeInventory.java new file mode 100644 index 00000000000..d685496e7bc --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/TakeInventory.java @@ -0,0 +1,71 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.eldritchmoon; + +import java.util.UUID; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.NamePredicate; + +/** + * + * @author fireshoes + */ +public class TakeInventory extends CardImpl { + + private static final FilterCard filter = new FilterCard("card named Take Inventory"); + + static { + filter.add(new NamePredicate("Take Inventory")); + } + + public TakeInventory(UUID ownerId) { + super(ownerId, 76, "Take Inventory", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{1}{U}"); + this.expansionSetCode = "EMN"; + + // Draw a card, then draw cards equal to the number of cards named Take Inventory in your graveyard. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + Effect effect = new DrawCardSourceControllerEffect(new CardsInControllerGraveyardCount(filter)); + effect.setText(", then draw cards equal to the number of cards named {source} in your graveyard"); + this.getSpellAbility().addEffect(effect); + } + + public TakeInventory(final TakeInventory card) { + super(card); + } + + @Override + public TakeInventory copy() { + return new TakeInventory(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/ThaliaHereticCathar.java b/Mage.Sets/src/mage/sets/eldritchmoon/ThaliaHereticCathar.java new file mode 100644 index 00000000000..9e2aea5cfee --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/ThaliaHereticCathar.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.eldritchmoon; + +import java.util.UUID; + +/** + * + * @author fireshoes + */ +public class ThaliaHereticCathar extends mage.sets.mediainserts.ThaliaHereticCathar { + + public ThaliaHereticCathar(UUID ownerId) { + super(ownerId); + this.cardNumber = 46; + this.expansionSetCode = "EMN"; + } + + public ThaliaHereticCathar(final ThaliaHereticCathar card) { + super(card); + } + + @Override + public ThaliaHereticCathar copy() { + return new ThaliaHereticCathar(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/ThaliasLancers.java b/Mage.Sets/src/mage/sets/eldritchmoon/ThaliasLancers.java new file mode 100644 index 00000000000..0fec2c78c04 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/ThaliasLancers.java @@ -0,0 +1,77 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.SupertypePredicate; +import mage.target.common.TargetCardInLibrary; + +/** + * + * @author fireshoes + */ +public class ThaliasLancers extends CardImpl { + + private static final FilterCard filter = new FilterCard("legendary card"); + + static { + filter.add(new SupertypePredicate("Legendary")); + } + + public ThaliasLancers(UUID ownerId) { + super(ownerId, 47, "Thalia's Lancers", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); + this.expansionSetCode = "EMN"; + this.subtype.add("Human"); + this.subtype.add("Knight"); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // When Thalia's Lancers enters the battlefield, you may search your library for a legendary card, reveal it, put it into your hand, then shuffle your library. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 1, filter), true, true), true)); + } + + public ThaliasLancers(final ThaliasLancers card) { + super(card); + } + + @Override + public ThaliasLancers copy() { + return new ThaliasLancers(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/UlrichOfTheKrallenhorde.java b/Mage.Sets/src/mage/sets/eldritchmoon/UlrichOfTheKrallenhorde.java new file mode 100644 index 00000000000..07ae27e76d7 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/UlrichOfTheKrallenhorde.java @@ -0,0 +1,128 @@ +/* + * 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.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.TriggeredAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author fireshoes + */ +public class UlrichOfTheKrallenhorde extends CardImpl { + + public UlrichOfTheKrallenhorde(UUID ownerId) { + super(ownerId, 191, "Ulrich of the Krallenhorde", Rarity.MYTHIC, new CardType[]{CardType.CREATURE}, "{3}{R}{G}"); + this.expansionSetCode = "EMN"; + this.supertype.add("Legendary"); + this.subtype.add("Human"); + this.subtype.add("Werewolf"); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + + this.canTransform = true; + this.secondSideCard = new UlrichUncontestedAlpha(ownerId); + + // Whenever this creature enters the battlefield or transforms into Ulrich of the Krallenhorde, target creature gets +4/+4 until end of turn. + this.addAbility(new UlrichOfTheKrallenhordeAbility()); + + // At the beginning of each upkeep, if no spells were cast last turn, transform Ulrich of the Krallenhorde. + this.addAbility(new TransformAbility()); + TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.ANY, false); + this.addAbility(new ConditionalTriggeredAbility(ability, NoSpellsWereCastLastTurnCondition.getInstance(), TransformAbility.NO_SPELLS_TRANSFORM_RULE)); + } + + public UlrichOfTheKrallenhorde(final UlrichOfTheKrallenhorde card) { + super(card); + } + + @Override + public UlrichOfTheKrallenhorde copy() { + return new UlrichOfTheKrallenhorde(this); + } +} + +class UlrichOfTheKrallenhordeAbility extends TriggeredAbilityImpl { + + public UlrichOfTheKrallenhordeAbility() { + super(Zone.BATTLEFIELD, new BoostTargetEffect(4, 4, Duration.EndOfTurn), false); + this.addTarget(new TargetCreaturePermanent()); + } + + public UlrichOfTheKrallenhordeAbility(final UlrichOfTheKrallenhordeAbility ability) { + super(ability); + } + + @Override + public UlrichOfTheKrallenhordeAbility copy() { + return new UlrichOfTheKrallenhordeAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TRANSFORMED || event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.TRANSFORMED && event.getTargetId().equals(this.getSourceId())) { + Permanent permanent = game.getPermanent(sourceId); + if (permanent != null && !permanent.isTransformed()) { + return true; + } + } + if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD && event.getTargetId().equals(this.getSourceId())) { + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever this creature enters the battlefield or transforms into Ulrich of the Krallenhorde, target creature gets +4/+4 until end of turn."; + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/UlrichUncontestedAlpha.java b/Mage.Sets/src/mage/sets/eldritchmoon/UlrichUncontestedAlpha.java new file mode 100644 index 00000000000..4d544662fb5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/UlrichUncontestedAlpha.java @@ -0,0 +1,135 @@ +/* + * 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.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.TriggeredAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.common.FightTargetSourceEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +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; +import mage.target.Target; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author fireshoes + */ +public class UlrichUncontestedAlpha extends CardImpl { + + public UlrichUncontestedAlpha(UUID ownerId) { + super(ownerId, 191, "Ulrich, Uncontested Alpha", Rarity.MYTHIC, new CardType[]{CardType.CREATURE}, ""); + this.expansionSetCode = "EMN"; + this.supertype.add("Legendary"); + this.subtype.add("Werewolf"); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // this card is the second face of double-faced card + this.nightCard = true; + this.canTransform = true; + + // Whenever this creature transforms into Ulrich, Uncontested Alpha, you may have it fight target non-Werewolf creature you don't control. + this.addAbility(new UlrichUncontestedAlphaAbility()); + + // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Ulrich, Uncontested Alpha. + TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(false), TargetController.ANY, false); + this.addAbility(new ConditionalTriggeredAbility(ability, TwoOrMoreSpellsWereCastLastTurnCondition.getInstance(), TransformAbility.TWO_OR_MORE_SPELLS_TRANSFORM_RULE)); + } + + public UlrichUncontestedAlpha(final UlrichUncontestedAlpha card) { + super(card); + } + + @Override + public UlrichUncontestedAlpha copy() { + return new UlrichUncontestedAlpha(this); + } +} + +class UlrichUncontestedAlphaAbility extends TriggeredAbilityImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("non-Werewolf creature you don't control"); + + static { + filter.add(Predicates.not(new SubtypePredicate("Werewolf"))); + filter.add(new ControllerPredicate(TargetController.NOT_YOU)); + } + + public UlrichUncontestedAlphaAbility() { + super(Zone.BATTLEFIELD, new FightTargetSourceEffect(), true); + Target target = new TargetCreaturePermanent(filter); + this.addTarget(target); + } + + public UlrichUncontestedAlphaAbility(final UlrichUncontestedAlphaAbility ability) { + super(ability); + } + + @Override + public UlrichUncontestedAlphaAbility copy() { + return new UlrichUncontestedAlphaAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TRANSFORMED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getTargetId().equals(sourceId)) { + Permanent permanent = game.getPermanent(sourceId); + if (permanent != null && permanent.isTransformed()) { + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever this creature transforms into Ulrich, Uncontested Alpha, you may have it fight target non-Werewolf creature you don't control."; + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/UlvenwaldObserver.java b/Mage.Sets/src/mage/sets/eldritchmoon/UlvenwaldObserver.java new file mode 100644 index 00000000000..3561b27a358 --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/UlvenwaldObserver.java @@ -0,0 +1,75 @@ +/* + * 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.eldritchmoon; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.filter.Filter; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ToughnessPredicate; +import mage.filter.predicate.permanent.ControllerPredicate; + +/** + * + * @author fireshoes + */ +public class UlvenwaldObserver extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature you control with toughness 4 or greater"); + + static { + filter.add(new ToughnessPredicate(Filter.ComparisonType.GreaterThan, 3)); + filter.add(new ControllerPredicate(TargetController.YOU)); + } + + public UlvenwaldObserver(UUID ownerId) { + super(ownerId, 176, "Ulvenwald Observer", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); + this.expansionSetCode = "EMN"; + this.subtype.add("Treefolk"); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Whenever a creature you control with toughness 4 or greater dies, draw a card. + this.addAbility(new DiesCreatureTriggeredAbility(new DrawCardSourceControllerEffect(1), false, filter)); + } + + public UlvenwaldObserver(final UlvenwaldObserver card) { + super(card); + } + + @Override + public UlvenwaldObserver copy() { + return new UlvenwaldObserver(this); + } +} diff --git a/Mage.Sets/src/mage/sets/eldritchmoon/Unsubstantiate.java b/Mage.Sets/src/mage/sets/eldritchmoon/Unsubstantiate.java new file mode 100644 index 00000000000..cbac64a430f --- /dev/null +++ b/Mage.Sets/src/mage/sets/eldritchmoon/Unsubstantiate.java @@ -0,0 +1,68 @@ +/* + * 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.eldritchmoon; + +import java.util.UUID; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterSpellOrPermanent; +import mage.target.common.TargetSpellOrPermanent; + +/** + * + * @author fireshoes + */ +public class Unsubstantiate extends CardImpl { + + private static final FilterSpellOrPermanent filter = new FilterSpellOrPermanent("spell or creature"); + + static { + filter.setPermanentFilter(new FilterCreaturePermanent()); + } + + public Unsubstantiate(UUID ownerId) { + super(ownerId, 79, "Unsubstantiate", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{U}"); + this.expansionSetCode = "EMN"; + + // Return target spell or creature to its owner's hand. + this.getSpellAbility().addTarget(new TargetSpellOrPermanent(1, 1, filter, false)); + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); + } + + public Unsubstantiate(final Unsubstantiate card) { + super(card); + } + + @Override + public Unsubstantiate copy() { + return new Unsubstantiate(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fallenempires/IcatianJavelineers1.java b/Mage.Sets/src/mage/sets/fallenempires/IcatianJavelineers1.java index a3a36e41680..bbb1b0d4d03 100644 --- a/Mage.Sets/src/mage/sets/fallenempires/IcatianJavelineers1.java +++ b/Mage.Sets/src/mage/sets/fallenempires/IcatianJavelineers1.java @@ -58,7 +58,7 @@ public class IcatianJavelineers1 extends CardImpl { this.toughness = new MageInt(1); // Icatian Javelineers enters the battlefield with a javelin counter on it. - this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.JAVELIN.createInstance()))); + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.JAVELIN.createInstance()), "with a javelin counter on it")); // {tap}, Remove a javelin counter from Icatian Javelineers: Icatian Javelineers deals 1 damage to target creature or player. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new TapSourceCost()); diff --git a/Mage.Sets/src/mage/sets/fatereforged/UginTheSpiritDragon.java b/Mage.Sets/src/mage/sets/fatereforged/UginTheSpiritDragon.java index a7cbbf6bc0b..c07286bae27 100644 --- a/Mage.Sets/src/mage/sets/fatereforged/UginTheSpiritDragon.java +++ b/Mage.Sets/src/mage/sets/fatereforged/UginTheSpiritDragon.java @@ -27,6 +27,8 @@ */ package mage.sets.fatereforged; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; @@ -35,6 +37,7 @@ import mage.abilities.costs.Cost; import mage.abilities.costs.common.PayVariableLoyaltyCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardsImpl; import mage.constants.CardType; @@ -48,7 +51,6 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorlessPredicate; import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; import mage.target.common.TargetCreatureOrPlayer; @@ -123,9 +125,9 @@ class UginTheSpiritDragonEffect2 extends OneShotEffect { FilterPermanent filter = new FilterPermanent("permanent with converted mana cost X or less that's one or more colors"); filter.add(new ConvertedManaCostPredicate(ComparisonType.LessThan, cmc + 1)); filter.add(Predicates.not(new ColorlessPredicate())); - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - controller.moveCardToExileWithInfo(permanent, null, "", source.getSourceId(), game, Zone.BATTLEFIELD, true); - } + Set permanentsToExile = new HashSet<>(); + permanentsToExile.addAll(game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)); + controller.moveCards(permanentsToExile, Zone.EXILED, source, game); return true; } } diff --git a/Mage.Sets/src/mage/sets/fatereforged/WillOfTheNaga.java b/Mage.Sets/src/mage/sets/fatereforged/WillOfTheNaga.java index 4c7e739d019..fd2742e572b 100644 --- a/Mage.Sets/src/mage/sets/fatereforged/WillOfTheNaga.java +++ b/Mage.Sets/src/mage/sets/fatereforged/WillOfTheNaga.java @@ -51,7 +51,7 @@ public class WillOfTheNaga extends CardImpl { // Tap up to two target creatures. Those creatures don't untap during their controller's next untap step. this.getSpellAbility().addEffect(new TapTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2)); - this.getSpellAbility().addEffect(new DontUntapInControllersNextUntapStepTargetEffect()); + this.getSpellAbility().addEffect(new DontUntapInControllersNextUntapStepTargetEffect("Those creatures")); } public WillOfTheNaga(final WillOfTheNaga card) { diff --git a/Mage.Sets/src/mage/sets/gatecrash/AngelicSkirmisher.java b/Mage.Sets/src/mage/sets/gatecrash/AngelicSkirmisher.java index a79dba31179..b9d81802e9a 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/AngelicSkirmisher.java +++ b/Mage.Sets/src/mage/sets/gatecrash/AngelicSkirmisher.java @@ -85,6 +85,7 @@ public class AngelicSkirmisher extends CardImpl { } class AngelicSkirmisherEffect extends OneShotEffect { + AngelicSkirmisherEffect() { super(Outcome.AddAbility); staticText = "choose first strike, vigilance or lifelink. Creatures you control gain that ability until end of turn"; @@ -96,7 +97,6 @@ class AngelicSkirmisherEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (controller != null && sourcePermanent != null) { @@ -107,29 +107,32 @@ class AngelicSkirmisherEffect extends OneShotEffect { abilityChoices.add("Vigilance"); abilityChoices.add("Lifelink"); abilityChoice.setChoices(abilityChoices); - Ability ability = null; - switch (abilityChoice.getChoice()) { - case "First strike": - ability = FirstStrikeAbility.getInstance(); - break; - case "Vigilance": - ability = VigilanceAbility.getInstance(); - break; - case "Lifelink": - ability = LifelinkAbility.getInstance(); - break; - default: - break; + while (!controller.choose(outcome, abilityChoice, game)) { + if (!controller.canRespond()) { + return false; + } } - if (ability != null) { - GainAbilityControlledEffect effect = new GainAbilityControlledEffect(ability, Duration.EndOfTurn, new FilterControlledCreaturePermanent()); - game.addEffect(effect, source); - game.informPlayers(new StringBuilder(sourcePermanent.getName()) - .append(": ") - .append(controller.getLogName()) - .append(" has chosen ") - .append(abilityChoice.getChoice().toLowerCase()).toString()); - return true; + Ability ability = null; + if (abilityChoice.getChoice() != null) { + switch (abilityChoice.getChoice()) { + case "First strike": + ability = FirstStrikeAbility.getInstance(); + break; + case "Vigilance": + ability = VigilanceAbility.getInstance(); + break; + case "Lifelink": + ability = LifelinkAbility.getInstance(); + break; + default: + break; + } + if (ability != null) { + GainAbilityControlledEffect effect = new GainAbilityControlledEffect(ability, Duration.EndOfTurn, new FilterControlledCreaturePermanent()); + game.addEffect(effect, source); + game.informPlayers(sourcePermanent.getName() + ": " + controller.getLogName() + " has chosen " + abilityChoice.getChoice().toLowerCase()); + return true; + } } } return false; @@ -139,4 +142,4 @@ class AngelicSkirmisherEffect extends OneShotEffect { public AngelicSkirmisherEffect copy() { return new AngelicSkirmisherEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/gatecrash/PrimeSpeakerZegana.java b/Mage.Sets/src/mage/sets/gatecrash/PrimeSpeakerZegana.java index fb54a138209..3377d273a71 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/PrimeSpeakerZegana.java +++ b/Mage.Sets/src/mage/sets/gatecrash/PrimeSpeakerZegana.java @@ -28,6 +28,7 @@ */ package mage.sets.gatecrash; +import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; @@ -45,8 +46,6 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; -import java.util.UUID; - /** * * @author Plopman @@ -65,7 +64,10 @@ public class PrimeSpeakerZegana extends CardImpl { this.toughness = new MageInt(1); //Prime Speaker Zegana enters the battlefield with X +1/+1 counters on it, where X is the greatest power among other creatures you control. - this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(0), new greatestPowerCount(), true), "where X is the greatest power among other creatures you control")); + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance(0), + new greatestPowerCount(), true), + "where X is the greatest power among other creatures you control")); //When Prime Speaker Zegana enters the battlefield, draw cards equal to its power. this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(new SourcePermanentPowerCount()))); } diff --git a/Mage.Sets/src/mage/sets/guildpact/GruulScrapper.java b/Mage.Sets/src/mage/sets/guildpact/GruulScrapper.java new file mode 100644 index 00000000000..a0bed3827a3 --- /dev/null +++ b/Mage.Sets/src/mage/sets/guildpact/GruulScrapper.java @@ -0,0 +1,72 @@ +/* + * 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.guildpact; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.ManaWasSpentCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.watchers.common.ManaSpentToCastWatcher; + +/** + * + * @author Styxo + */ +public class GruulScrapper extends CardImpl { + + public GruulScrapper(UUID ownerId) { + super(ownerId, 89, "Gruul Scrapper", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{3}{G}"); + this.expansionSetCode = "GPT"; + this.subtype.add("Human"); + this.subtype.add("Berserker"); + + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + //When Gruul Scrapper enters the battlefield, if Red was spent to cast Gruul Scrapper, it gains haste until end of turn. + this.addAbility(new EntersBattlefieldTriggeredAbility(new ConditionalContinuousEffect(new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.EndOfTurn), new ManaWasSpentCondition(ColoredManaSymbol.R), " if {R} was spent to cast {this}, it gains haste until end of turn")), new ManaSpentToCastWatcher()); + + } + + public GruulScrapper(final GruulScrapper card) { + super(card); + } + + @Override + public GruulScrapper copy() { + return new GruulScrapper(this); + } +} diff --git a/Mage.Sets/src/mage/sets/innistrad/CurseOfThePiercedHeart.java b/Mage.Sets/src/mage/sets/innistrad/CurseOfThePiercedHeart.java index 881076d620b..caa3e447c01 100644 --- a/Mage.Sets/src/mage/sets/innistrad/CurseOfThePiercedHeart.java +++ b/Mage.Sets/src/mage/sets/innistrad/CurseOfThePiercedHeart.java @@ -58,7 +58,6 @@ public class CurseOfThePiercedHeart extends CardImpl { this.subtype.add("Aura"); this.subtype.add("Curse"); - // Enchant player TargetPlayer target = new TargetPlayer(); this.getSpellAbility().addTarget(target); @@ -97,7 +96,7 @@ class CurseOfThePiercedHeartAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.DRAW_STEP_PRE; + return event.getType() == EventType.UPKEEP_STEP_PRE; } @Override @@ -118,4 +117,4 @@ class CurseOfThePiercedHeartAbility extends TriggeredAbilityImpl { return "At the beginning of enchanted player's upkeep, Curse of the Pierced Heart deals 1 damage to that player."; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/innistrad/HamletCaptain.java b/Mage.Sets/src/mage/sets/innistrad/HamletCaptain.java index 9aa23edb7cf..267970091be 100644 --- a/Mage.Sets/src/mage/sets/innistrad/HamletCaptain.java +++ b/Mage.Sets/src/mage/sets/innistrad/HamletCaptain.java @@ -28,13 +28,13 @@ package mage.sets.innistrad; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Rarity; import mage.MageInt; import mage.abilities.common.AttacksOrBlocksTriggeredAbility; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.SubtypePredicate; @@ -44,7 +44,7 @@ import mage.filter.predicate.mageobject.SubtypePredicate; */ public class HamletCaptain extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Human creatures"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Humans"); static { filter.add(new SubtypePredicate("Human")); diff --git a/Mage.Sets/src/mage/sets/judgment/CabalTherapy.java b/Mage.Sets/src/mage/sets/judgment/CabalTherapy.java index 524c6e205e3..53508b3973e 100644 --- a/Mage.Sets/src/mage/sets/judgment/CabalTherapy.java +++ b/Mage.Sets/src/mage/sets/judgment/CabalTherapy.java @@ -29,19 +29,17 @@ package mage.sets.judgment; import java.util.UUID; import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.NameACardEffect; +import mage.abilities.keyword.FlashbackAbility; +import mage.cards.Card; +import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.TimingRule; -import mage.abilities.Ability; -import mage.abilities.costs.common.SacrificeTargetCost; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.keyword.FlashbackAbility; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.repository.CardRepository; -import mage.choices.Choice; -import mage.choices.ChoiceImpl; import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.players.Player; @@ -59,12 +57,13 @@ public class CabalTherapy extends CardImpl { this.expansionSetCode = "JUD"; // Name a nonland card. Target player reveals his or her hand and discards all cards with that name. + this.getSpellAbility().addEffect((new NameACardEffect(NameACardEffect.TypeOfName.NON_LAND_NAME))); this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new CabalTherapyEffect()); - + // Flashback-Sacrifice a creature. this.addAbility(new FlashbackAbility( - new SacrificeTargetCost(new TargetControlledCreaturePermanent(1,1,new FilterControlledCreaturePermanent("a creature"), true)), + new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, new FilterControlledCreaturePermanent("a creature"), true)), TimingRule.SORCERY)); } @@ -95,25 +94,12 @@ class CabalTherapyEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); if (targetPlayer != null && controller != null && sourceObject != null) { - Choice cardChoice = new ChoiceImpl(true); - cardChoice.setMessage("Name a nonland card."); - cardChoice.setChoices(CardRepository.instance.getNonLandNames()); - cardChoice.clearChoice(); - - while (!controller.choose(Outcome.Discard, cardChoice, game)) { - if (!controller.canRespond()) { - return false; - } - } - - String cardName = cardChoice.getChoice(); - game.informPlayers(sourceObject.getLogName() + ", named card: [" + cardName + "]"); + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + NameACardEffect.INFO_KEY); for (Card card : targetPlayer.getHand().getCards(game)) { if (card.getName().equals(cardName)) { targetPlayer.discard(card, source, game); } } - controller.lookAtCards(sourceObject.getName() + " Hand", targetPlayer.getHand(), game); } return true; diff --git a/Mage.Sets/src/mage/sets/judgment/PrismaticStrands.java b/Mage.Sets/src/mage/sets/judgment/PrismaticStrands.java index 14b1fdbf946..379d7d8a0df 100644 --- a/Mage.Sets/src/mage/sets/judgment/PrismaticStrands.java +++ b/Mage.Sets/src/mage/sets/judgment/PrismaticStrands.java @@ -56,8 +56,9 @@ import mage.target.common.TargetControlledCreaturePermanent; * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public class PrismaticStrands extends CardImpl { - + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped white creature you control"); + static { filter.add(Predicates.not(new TappedPredicate())); filter.add(new ColorPredicate(ObjectColor.WHITE)); @@ -69,9 +70,9 @@ public class PrismaticStrands extends CardImpl { // Prevent all damage that sources of the color of your choice would deal this turn. this.getSpellAbility().addEffect(new PrismaticStrandsEffect()); - + // Flashback-Tap an untapped white creature you control. - this.addAbility(new FlashbackAbility(new TapTargetCost(new TargetControlledCreaturePermanent(1,1,filter,false)), TimingRule.INSTANT)); + this.addAbility(new FlashbackAbility(new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, false)), TimingRule.INSTANT)); } public PrismaticStrands(final PrismaticStrands card) { @@ -85,36 +86,42 @@ public class PrismaticStrands extends CardImpl { } class PrismaticStrandsEffect extends OneShotEffect { - + PrismaticStrandsEffect() { super(Outcome.PreventDamage); this.staticText = "Prevent all damage that sources of the color of your choice would deal this turn"; } - + PrismaticStrandsEffect(final PrismaticStrandsEffect effect) { super(effect); } - + @Override public PrismaticStrandsEffect copy() { return new PrismaticStrandsEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null && sourceObject != null) { ChoiceColor choice = new ChoiceColor(); controller.choose(Outcome.PreventDamage, choice, game); - game.addEffect(new PrismaticStrandsPreventionEffect(choice.getColor()), source); - return true; + if (choice.isChosen()) { + if (!game.isSimulation()) { + game.informPlayers(sourceObject.getLogName() + ": " + controller.getLogName() + " has chosen sources of the color " + choice.getChoice()); + } + game.addEffect(new PrismaticStrandsPreventionEffect(choice.getColor()), source); + return true; + } } return false; } } class PrismaticStrandsPreventionEffect extends PreventionEffectImpl { - + private final ObjectColor color; PrismaticStrandsPreventionEffect(ObjectColor color) { @@ -135,8 +142,8 @@ class PrismaticStrandsPreventionEffect extends PreventionEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { if (super.applies(event, source, game)) { - if (event.getType().equals(GameEvent.EventType.DAMAGE_PLAYER) - || event.getType().equals(GameEvent.EventType.DAMAGE_CREATURE) + if (event.getType().equals(GameEvent.EventType.DAMAGE_PLAYER) + || event.getType().equals(GameEvent.EventType.DAMAGE_CREATURE) || event.getType().equals(GameEvent.EventType.DAMAGE_PLANESWALKER)) { MageObject sourceObject = game.getObject(event.getSourceId()); if (sourceObject != null && sourceObject.getColor(game).shares(this.color)) { diff --git a/Mage.Sets/src/mage/sets/legions/CallerOfTheClaw.java b/Mage.Sets/src/mage/sets/legions/CallerOfTheClaw.java index b1f7f440a77..576fc5d4dbd 100644 --- a/Mage.Sets/src/mage/sets/legions/CallerOfTheClaw.java +++ b/Mage.Sets/src/mage/sets/legions/CallerOfTheClaw.java @@ -86,7 +86,7 @@ class CallerOfTheClawWatcher extends Watcher { private int creaturesCount = 0; public CallerOfTheClawWatcher() { - super("YourCreaturesDied", WatcherScope.PLAYER); + super(CallerOfTheClawWatcher.class.getName(), WatcherScope.PLAYER); condition = true; } @@ -107,7 +107,7 @@ class CallerOfTheClawWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.ZONE_CHANGE && ((ZoneChangeEvent) event).isDiesEvent()) { - Permanent card = (Permanent)game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); + Permanent card = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); if (card != null && card.getOwnerId().equals(this.controllerId) && card.getCardType().contains(CardType.CREATURE) && !(card instanceof PermanentToken)) { creaturesCount++; } @@ -123,7 +123,6 @@ class CallerOfTheClawWatcher extends Watcher { class CallerOfTheClawDynamicValue implements DynamicValue { - @Override public CallerOfTheClawDynamicValue copy() { return new CallerOfTheClawDynamicValue(); @@ -141,7 +140,7 @@ class CallerOfTheClawDynamicValue implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - CallerOfTheClawWatcher watcher = (CallerOfTheClawWatcher) game.getState().getWatchers().get("YourCreaturesDied", sourceAbility.getControllerId()); + CallerOfTheClawWatcher watcher = (CallerOfTheClawWatcher) game.getState().getWatchers().get(CallerOfTheClawWatcher.class.getName(), sourceAbility.getControllerId()); if (watcher != null) { return watcher.getCreaturesCount(); } diff --git a/Mage.Sets/src/mage/sets/limitedalpha/WinterOrb.java b/Mage.Sets/src/mage/sets/limitedalpha/WinterOrb.java index 9f9aac413b7..182540f6ac7 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/WinterOrb.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/WinterOrb.java @@ -39,6 +39,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledLandPermanent; import mage.filter.common.FilterControlledPermanent; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; /** @@ -51,6 +52,7 @@ public class WinterOrb extends CardImpl { super(ownerId, 275, "Winter Orb", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{2}"); this.expansionSetCode = "LEA"; + // As long as Winter Orb is untapped, players can't untap more than one land during their untap steps. // Players can't untap more than one land during their untap steps. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new WinterOrbEffect())); @@ -72,7 +74,7 @@ class WinterOrbEffect extends RestrictionUntapNotMoreThanEffect { public WinterOrbEffect() { super(Duration.WhileOnBattlefield, 1, filter); - staticText = "Players can't untap more than one land during their untap steps"; + staticText = "As long as Winter Orb is untapped, players can't untap more than one land during their untap steps"; } public WinterOrbEffect(final WinterOrbEffect effect) { @@ -81,8 +83,8 @@ class WinterOrbEffect extends RestrictionUntapNotMoreThanEffect { @Override public boolean applies(Player player, Ability source, Game game) { - // applied to all players - return true; + Permanent sourceObject = game.getPermanent(source.getSourceId()); + return sourceObject != null && !sourceObject.isTapped(); } @Override diff --git a/Mage.Sets/src/mage/sets/lorwyn/HealTheScars.java b/Mage.Sets/src/mage/sets/lorwyn/HealTheScars.java new file mode 100644 index 00000000000..be364232a1b --- /dev/null +++ b/Mage.Sets/src/mage/sets/lorwyn/HealTheScars.java @@ -0,0 +1,98 @@ +/* + * 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.lorwyn; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.RegenerateTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Styxo + */ +public class HealTheScars extends CardImpl { + + public HealTheScars(UUID ownerId) { + super(ownerId, 217, "Heal the Scars", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{3}{G}"); + this.expansionSetCode = "LRW"; + + // Regenerate target creature. You gain life equal to that creature's toughness. + this.getSpellAbility().addEffect(new RegenerateTargetEffect()); + this.getSpellAbility().addEffect(new HealTheScarsEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + } + + public HealTheScars(final HealTheScars card) { + super(card); + } + + @Override + public HealTheScars copy() { + return new HealTheScars(this); + } +} + +class HealTheScarsEffect extends OneShotEffect { + + public HealTheScarsEffect() { + super(Outcome.GainLife); + staticText = "You gain life equal to that creature's toughness"; + } + + public HealTheScarsEffect(final HealTheScarsEffect effect) { + super(effect); + } + + @Override + public HealTheScarsEffect copy() { + return new HealTheScarsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + if (permanent != null) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + player.gainLife(permanent.getToughness().getValue(), game); + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/lorwyn/HunterOfEyeblights.java b/Mage.Sets/src/mage/sets/lorwyn/HunterOfEyeblights.java new file mode 100644 index 00000000000..b5d2b0d5a33 --- /dev/null +++ b/Mage.Sets/src/mage/sets/lorwyn/HunterOfEyeblights.java @@ -0,0 +1,93 @@ +/* + * 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.lorwyn; + +import java.util.UUID; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +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.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.filter.predicate.permanent.CounterAnyPredicate; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Styxo + */ +public class HunterOfEyeblights extends CardImpl { + + private static final FilterCreaturePermanent filter1 = new FilterCreaturePermanent("creature you don't control"); + private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creature with a counter on it"); + + static { + filter1.add(new ControllerPredicate(TargetController.NOT_YOU)); + filter2.add(new CounterAnyPredicate()); + + } + + public HunterOfEyeblights(UUID ownerId) { + super(ownerId, 119, "Hunter Of Eyeblights", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); + this.expansionSetCode = "LRW"; + this.subtype.add("Elf"); + this.subtype.add("Assassin"); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Hunter of Eyeblights enters the battlefield, put a +1/+1 counter on target creature you don't control + Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + ability.addTarget(new TargetCreaturePermanent(filter1)); + this.addAbility(ability); + + //{B}{2},{T}: Destroy target creature with a counter on it. + Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{2}{B}")); + ability2.addCost(new TapSourceCost()); + ability2.addTarget(new TargetCreaturePermanent(filter2)); + this.addAbility(ability2); + } + + public HunterOfEyeblights(final HunterOfEyeblights card) { + super(card); + } + + @Override + public HunterOfEyeblights copy() { + return new HunterOfEyeblights(this); + } +} diff --git a/Mage.Sets/src/mage/sets/magic2010/ChandraNalaar.java b/Mage.Sets/src/mage/sets/magic2010/ChandraNalaar.java index b1ced672549..f1bc12fcbc0 100644 --- a/Mage.Sets/src/mage/sets/magic2010/ChandraNalaar.java +++ b/Mage.Sets/src/mage/sets/magic2010/ChandraNalaar.java @@ -59,14 +59,17 @@ public class ChandraNalaar extends CardImpl { this.addAbility(new PlanswalkerEntersWithLoyalityCountersAbility(6)); + // +1: Chandra Nalaar deals 1 damage to target player. LoyaltyAbility ability1 = new LoyaltyAbility(new DamageTargetEffect(1), 1); ability1.addTarget(new TargetPlayer()); this.addAbility(ability1); + // -X: Chandra Nalaar deals X damage to target creature. LoyaltyAbility ability2 = new LoyaltyAbility(new DamageTargetEffect(ChandraNalaarXValue.getDefault())); ability2.addTarget(new TargetCreaturePermanent()); this.addAbility(ability2); + // -8: Chandra Nalaar deals 10 damage to target player and each creature he or she controls. Effects effects1 = new Effects(); effects1.add(new DamageTargetEffect(10)); effects1.add(new DamageAllControlledTargetEffect(10, new FilterCreaturePermanent())); diff --git a/Mage.Sets/src/mage/sets/magic2010/Fireball.java b/Mage.Sets/src/mage/sets/magic2010/Fireball.java index b4a3a67ae3b..73f76bc9c54 100644 --- a/Mage.Sets/src/mage/sets/magic2010/Fireball.java +++ b/Mage.Sets/src/mage/sets/magic2010/Fireball.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,12 +20,11 @@ * 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.magic2010; import java.util.ArrayList; @@ -33,13 +32,13 @@ import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Rarity; import mage.abilities.Ability; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -55,6 +54,8 @@ public class Fireball extends CardImpl { super(ownerId, 136, "Fireball", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{X}{R}"); this.expansionSetCode = "M10"; + // Fireball deals X damage divided evenly, rounded down, among any number of target creatures and/or players. + // Fireball costs 1 more to cast for each target beyond the first. this.getSpellAbility().addTarget(new FireballTargetCreatureOrPlayer(0, Integer.MAX_VALUE)); this.getSpellAbility().addEffect(new FireballEffect()); } @@ -93,14 +94,13 @@ class FireballEffect extends OneShotEffect { int numTargets = targetPointer.getTargets(game, source).size(); int damage = source.getManaCostsToPay().getX(); if (numTargets > 0) { - int damagePer = damage/numTargets; + int damagePer = damage / numTargets; if (damagePer > 0) { - for (UUID targetId: targetPointer.getTargets(game, source)) { + for (UUID targetId : targetPointer.getTargets(game, source)) { Permanent permanent = game.getPermanent(targetId); if (permanent != null) { permanent.damage(damagePer, source.getSourceId(), game, false, true); - } - else { + } else { Player player = game.getPlayer(targetId); if (player != null) { player.damage(damagePer, source.getSourceId(), game, false, true); @@ -130,7 +130,6 @@ class FireballTargetCreatureOrPlayer extends TargetCreatureOrPlayer { super(target); } - /** * This is only used by AI players * @@ -140,25 +139,25 @@ class FireballTargetCreatureOrPlayer extends TargetCreatureOrPlayer { */ @Override public List getTargetOptions(Ability source, Game game) { - + List options = new ArrayList<>(); - int xVal = source.getManaCostsToPay().getX(); + int xVal = source.getManaCostsToPay().getX(); if (xVal < 1) { return options; } - for (int numberTargets = 1; numberTargets == 1 || xVal / (numberTargets - 1) > 1 ; numberTargets++) { + for (int numberTargets = 1; numberTargets == 1 || xVal / (numberTargets - 1) > 1; numberTargets++) { Set possibleTargets = possibleTargets(source.getSourceId(), source.getControllerId(), game); // less possible targets than we're trying to set if (possibleTargets.size() < numberTargets) { return options; } // less than 1 damage per target = 0, add no such options - if ((xVal -(numberTargets -1))/numberTargets < 1) { + if ((xVal - (numberTargets - 1)) / numberTargets < 1) { continue; } - + possibleTargets.removeAll(getTargets()); Iterator it = possibleTargets.iterator(); while (it.hasNext()) { @@ -173,7 +172,7 @@ class FireballTargetCreatureOrPlayer extends TargetCreatureOrPlayer { if (!target.isChosen()) { Iterator it2 = possibleTargets.iterator(); - while (it2.hasNext()&& !target.isChosen()) { + while (it2.hasNext() && !target.isChosen()) { UUID nextTargetId = it2.next(); target.addTarget(nextTargetId, source, game, true); @@ -191,9 +190,8 @@ class FireballTargetCreatureOrPlayer extends TargetCreatureOrPlayer { return options; } - @Override public FireballTargetCreatureOrPlayer copy() { return new FireballTargetCreatureOrPlayer(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/magic2012/FrostBreath.java b/Mage.Sets/src/mage/sets/magic2012/FrostBreath.java index 7f5215bbd6f..4d6e0e2b670 100644 --- a/Mage.Sets/src/mage/sets/magic2012/FrostBreath.java +++ b/Mage.Sets/src/mage/sets/magic2012/FrostBreath.java @@ -28,11 +28,11 @@ package mage.sets.magic2012; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Rarity; import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; import mage.abilities.effects.common.TapTargetEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; import mage.target.common.TargetCreaturePermanent; /** @@ -49,7 +49,7 @@ public class FrostBreath extends CardImpl { // Tap up to two target creatures. Those creatures don't untap during their controller's next untap step. this.getSpellAbility().addEffect(new TapTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2)); - this.getSpellAbility().addEffect(new DontUntapInControllersNextUntapStepTargetEffect()); + this.getSpellAbility().addEffect(new DontUntapInControllersNextUntapStepTargetEffect("Those creatures")); } public FrostBreath(final FrostBreath card) { diff --git a/Mage.Sets/src/mage/sets/magic2015/StainTheMind.java b/Mage.Sets/src/mage/sets/magic2015/StainTheMind.java index 02b214e584a..76771f3a2f2 100644 --- a/Mage.Sets/src/mage/sets/magic2015/StainTheMind.java +++ b/Mage.Sets/src/mage/sets/magic2015/StainTheMind.java @@ -28,20 +28,14 @@ package mage.sets.magic2015; import java.util.UUID; -import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.Mode; +import mage.abilities.effects.common.NameACardEffect; import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect; import mage.abilities.keyword.ConvokeAbility; import mage.cards.CardImpl; -import mage.cards.repository.CardRepository; -import mage.choices.Choice; -import mage.choices.ChoiceImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.game.Game; -import mage.players.Player; import mage.target.TargetPlayer; /** @@ -57,6 +51,7 @@ public class StainTheMind extends CardImpl { // Convoke this.addAbility(new ConvokeAbility()); // Name a nonland card. Search target player's graveyard, hand, and library for any number of card's with that name and exile them. Then that player shuffles his or her library. + this.getSpellAbility().addEffect((new NameACardEffect(NameACardEffect.TypeOfName.NON_LAND_NAME))); this.getSpellAbility().addEffect(new StainTheMindEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); } @@ -83,29 +78,8 @@ class StainTheMindEffect extends SearchTargetGraveyardHandLibraryForCardNameAndE @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(targetPointer.getFirst(game, source)); - Player controller = game.getPlayer(source.getControllerId()); - if (player != null && controller != null) { - Choice cardChoice = new ChoiceImpl(); - cardChoice.setChoices(CardRepository.instance.getNonLandNames()); - cardChoice.clearChoice(); - cardChoice.setMessage("Name a nonland card"); - - while (!controller.choose(Outcome.Exile, cardChoice, game)) { - if (!controller.canRespond()) { - return false; - } - } - String cardName; - cardName = cardChoice.getChoice(); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (sourceObject != null) { - game.informPlayers(sourceObject.getName() + " named card: [" + cardName + "]"); - } - - super.applySearchAndExile(game, source, cardName, player.getId()); - } - return true; + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + NameACardEffect.INFO_KEY); + return super.applySearchAndExile(game, source, cardName, targetPointer.getFirst(game, source)); } @Override @@ -113,12 +87,4 @@ class StainTheMindEffect extends SearchTargetGraveyardHandLibraryForCardNameAndE return new StainTheMindEffect(this); } - @Override - public String getText(Mode mode) { - StringBuilder sb = new StringBuilder(); - sb.append("Name a nonland card. "); - sb.append(super.getText(mode)); - return sb.toString(); - } - } diff --git a/Mage.Sets/src/mage/sets/magicorigins/ThopterEngineer.java b/Mage.Sets/src/mage/sets/magicorigins/ThopterEngineer.java index a3baa7eef35..cd65ce12e76 100644 --- a/Mage.Sets/src/mage/sets/magicorigins/ThopterEngineer.java +++ b/Mage.Sets/src/mage/sets/magicorigins/ThopterEngineer.java @@ -49,7 +49,7 @@ import mage.game.permanent.token.ThopterColorlessToken; */ public class ThopterEngineer extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Artifact creatures"); + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Artifact creatures you control"); static { filter.add(new CardTypePredicate(CardType.ARTIFACT)); @@ -66,7 +66,7 @@ public class ThopterEngineer extends CardImpl { // When Thopter Engineer enters the battlefield, put a 1/1 colorless Thopter artifact creature token with flying onto the battlefield. this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new ThopterColorlessToken(), 1))); - // Artifact creatures you control have haste. + // Artifact creatures you control have haste. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter, false))); } diff --git a/Mage.Sets/src/mage/sets/mediainserts/ThaliaHereticCathar.java b/Mage.Sets/src/mage/sets/mediainserts/ThaliaHereticCathar.java new file mode 100644 index 00000000000..f732165a657 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mediainserts/ThaliaHereticCathar.java @@ -0,0 +1,120 @@ +/* + * 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.mediainserts; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +/** + * + * @author fireshoes + */ +public class ThaliaHereticCathar extends CardImpl { + + public ThaliaHereticCathar(UUID ownerId) { + super(ownerId, 156, "Thalia, Heretic Cathar", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{W}"); + this.expansionSetCode = "MBP"; + this.supertype.add("Legendary"); + this.subtype.add("Human"); + this.subtype.add("Soldier"); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // Creatures and nonbasic lands your opponents control enter the battlefield tapped. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ThaliaTapEffect())); + } + + public ThaliaHereticCathar(final ThaliaHereticCathar card) { + super(card); + } + + @Override + public ThaliaHereticCathar copy() { + return new ThaliaHereticCathar(this); + } +} + +class ThaliaTapEffect extends ReplacementEffectImpl { + + ThaliaTapEffect() { + super(Duration.WhileOnBattlefield, Outcome.Tap); + staticText = "Creatures and nonbasic lands your opponents control enter the battlefield tapped"; + } + + ThaliaTapEffect(final ThaliaTapEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); + if (target != null) { + target.setTapped(true); + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { + Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); + if (permanent != null && (permanent.getCardType().contains(CardType.CREATURE) || + (permanent.getCardType().contains(CardType.LAND) && !permanent.getSupertype().contains("Basic")))) { + return true; + } + } + return false; + } + + @Override + public ThaliaTapEffect copy() { + return new ThaliaTapEffect(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mirrodin/Glimmervoid.java b/Mage.Sets/src/mage/sets/mirrodin/Glimmervoid.java index 75ab8e50f04..2b09baaad75 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/Glimmervoid.java +++ b/Mage.Sets/src/mage/sets/mirrodin/Glimmervoid.java @@ -68,6 +68,8 @@ public class Glimmervoid extends CardImpl { class GlimmervoidTriggeredAbility extends TriggeredAbilityImpl { + static final FilterArtifactPermanent filter = new FilterArtifactPermanent(); + GlimmervoidTriggeredAbility() { super(Zone.BATTLEFIELD, new SacrificeSourceEffect()); } @@ -81,6 +83,11 @@ class GlimmervoidTriggeredAbility extends TriggeredAbilityImpl { return new GlimmervoidTriggeredAbility(this); } + @java.lang.Override + public boolean checkInterveningIfClause(Game game) { + return game.getBattlefield().countAll(filter, controllerId, game) == 0; + } + @java.lang.Override public boolean checkEventType(GameEvent event, Game game) { return event.getType() == EventType.END_TURN_STEP_PRE; @@ -88,11 +95,7 @@ class GlimmervoidTriggeredAbility extends TriggeredAbilityImpl { @java.lang.Override public boolean checkTrigger(GameEvent event, Game game) { - FilterArtifactPermanent filter = new FilterArtifactPermanent(); - if (!game.getBattlefield().contains(filter, controllerId, 1, game)) { - return true; - } - return false; + return true; } @java.lang.Override diff --git a/Mage.Sets/src/mage/sets/mirrodin/SwordOfKaldra.java b/Mage.Sets/src/mage/sets/mirrodin/SwordOfKaldra.java index c6d692fdd65..94632edf8cb 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/SwordOfKaldra.java +++ b/Mage.Sets/src/mage/sets/mirrodin/SwordOfKaldra.java @@ -60,7 +60,7 @@ public class SwordOfKaldra extends CardImpl { this.subtype.add("Equipment"); // Equipped creature gets +5/+5. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(5,5, Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(5, 5, Duration.WhileOnBattlefield))); // Whenever equipped creature deals damage to a creature, exile that creature. this.addAbility(new SwordOfKaldraTriggeredAbility()); // Equip {4} @@ -80,16 +80,16 @@ public class SwordOfKaldra extends CardImpl { class SwordOfKaldraTriggeredAbility extends TriggeredAbilityImpl { public SwordOfKaldraTriggeredAbility() { - super(Zone.BATTLEFIELD, new ExileTargetEffect("exile that creature"), false); + super(Zone.BATTLEFIELD, new ExileTargetEffect("exile that creature. (Exile it only if it's still on the battlefield.)"), false); } public SwordOfKaldraTriggeredAbility(final SwordOfKaldraTriggeredAbility ability) { - super(ability); + super(ability); } @java.lang.Override public SwordOfKaldraTriggeredAbility copy() { - return new SwordOfKaldraTriggeredAbility(this); + return new SwordOfKaldraTriggeredAbility(this); } @java.lang.Override @@ -101,8 +101,8 @@ class SwordOfKaldraTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { Permanent equipment = game.getPermanent(this.getSourceId()); if (equipment != null - && equipment.getAttachedTo() != null - && event.getSourceId().equals(equipment.getAttachedTo())) { + && equipment.getAttachedTo() != null + && event.getSourceId().equals(equipment.getAttachedTo())) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getTargetId())); } @@ -113,7 +113,7 @@ class SwordOfKaldraTriggeredAbility extends TriggeredAbilityImpl { @java.lang.Override public String getRule() { - return "Whenever equipped creature deals damage to a creature, " + super.getRule(); + return "Whenever equipped creature deals damage to a creature, " + super.getRule(); } } diff --git a/Mage.Sets/src/mage/sets/mirrodin/SynodSanctum.java b/Mage.Sets/src/mage/sets/mirrodin/SynodSanctum.java new file mode 100644 index 00000000000..2313b4d2018 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirrodin/SynodSanctum.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.mirrodin; + +import java.util.UUID; + +/** + * + * @author spjspj + */ +public class SynodSanctum extends mage.sets.archenemy.SynodSanctum { + + public SynodSanctum(UUID ownerId) { + super(ownerId); + this.cardNumber = 252; + this.expansionSetCode = "MRD"; + } + + public SynodSanctum(final SynodSanctum card) { + super(card); + } + + @java.lang.Override + public SynodSanctum copy() { + return new SynodSanctum(this); + } +} diff --git a/Mage.Sets/src/mage/sets/newphyrexia/FreshMeat.java b/Mage.Sets/src/mage/sets/newphyrexia/FreshMeat.java index e3d8b953a1c..35d5919d656 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/FreshMeat.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/FreshMeat.java @@ -27,25 +27,17 @@ */ package mage.sets.newphyrexia; -import mage.MageObject; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.constants.WatcherScope; -import mage.constants.Zone; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.events.ZoneChangeEvent; import mage.game.permanent.token.BeastToken; -import mage.watchers.Watcher; - -import java.util.UUID; +import mage.watchers.common.CreaturesDiedWatcher; /** * @@ -57,8 +49,8 @@ public class FreshMeat extends CardImpl { super(ownerId, 109, "Fresh Meat", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{3}{G}"); this.expansionSetCode = "NPH"; - - this.getSpellAbility().addWatcher(new FreshMeatWatcher()); + // Put a 3/3 green Beast creature token onto the battlefield for each creature put into your graveyard from the battlefield this turn. + this.getSpellAbility().addWatcher(new CreaturesDiedWatcher()); this.getSpellAbility().addEffect(new CreateTokenEffect(new BeastToken(), new FreshMeatDynamicValue())); } @@ -72,53 +64,13 @@ public class FreshMeat extends CardImpl { } } -class FreshMeatWatcher extends Watcher { - - private int creaturesCount = 0; - - public FreshMeatWatcher() { - super("YourCreaturesDied", WatcherScope.PLAYER); - condition = true; - } - - public FreshMeatWatcher(final FreshMeatWatcher watcher) { - super(watcher); - this.creaturesCount = watcher.creaturesCount; - } - - @Override - public FreshMeatWatcher copy() { - return new FreshMeatWatcher(this); - } - - public int getCreaturesCount() { - return creaturesCount; - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == EventType.ZONE_CHANGE && ((ZoneChangeEvent) event).isDiesEvent()) { - MageObject card = game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); - if (card != null && ((Card)card).getOwnerId().equals(this.controllerId) && card.getCardType().contains(CardType.CREATURE)) { - creaturesCount++; - } - } - } - - @Override - public void reset() { - super.reset(); - creaturesCount = 0; - } -} - class FreshMeatDynamicValue implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - FreshMeatWatcher watcher = (FreshMeatWatcher) game.getState().getWatchers().get("YourCreaturesDied", sourceAbility.getControllerId()); + CreaturesDiedWatcher watcher = (CreaturesDiedWatcher) game.getState().getWatchers().get("CreaturesDiedWatcher"); if (watcher != null) { - return watcher.getCreaturesCount(); + return watcher.getAmountOfCreaturesDiesThisTurn(sourceAbility.getControllerId()); } return 0; } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/PhyrexianIngester.java b/Mage.Sets/src/mage/sets/newphyrexia/PhyrexianIngester.java index 4f8bd2d31f4..261751821ca 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/PhyrexianIngester.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/PhyrexianIngester.java @@ -28,13 +28,6 @@ package mage.sets.newphyrexia; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.constants.SubLayer; -import mage.constants.Zone; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -44,6 +37,13 @@ import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.SubLayer; +import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TokenPredicate; @@ -113,11 +113,11 @@ class PhyrexianIngesterImprintEffect extends OneShotEffect { if (controller != null && sourceObject != null) { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); Permanent targetPermanent = game.getPermanent(source.getFirstTarget()); - if (targetPermanent != null) { - controller.moveCardToExileWithInfo(targetPermanent, getId(), sourceObject.getIdName() + " (Imprint)", source.getSourceId(), game, Zone.BATTLEFIELD, true); + if (sourcePermanent != null && targetPermanent != null) { + controller.moveCardToExileWithInfo(targetPermanent, getId(), sourceObject.getIdName() + " (Imprint)", source.getSourceId(), game, Zone.BATTLEFIELD, true); sourcePermanent.imprint(targetPermanent.getId(), game); return true; - } + } } return false; } diff --git a/Mage.Sets/src/mage/sets/oathofthegatewatch/IonasBlessing.java b/Mage.Sets/src/mage/sets/oathofthegatewatch/IonasBlessing.java index dcd4428192b..29c30688707 100644 --- a/Mage.Sets/src/mage/sets/oathofthegatewatch/IonasBlessing.java +++ b/Mage.Sets/src/mage/sets/oathofthegatewatch/IonasBlessing.java @@ -92,7 +92,7 @@ class IonasBlessingEffect extends ContinuousEffectImpl { public IonasBlessingEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = ", and can block an additional creature"; + staticText = ", and can block an additional creature each combat"; } public IonasBlessingEffect(final IonasBlessingEffect effect) { diff --git a/Mage.Sets/src/mage/sets/odyssey/BraidsCabalMinion.java b/Mage.Sets/src/mage/sets/odyssey/BraidsCabalMinion.java index efafb8f586c..d4af0921090 100644 --- a/Mage.Sets/src/mage/sets/odyssey/BraidsCabalMinion.java +++ b/Mage.Sets/src/mage/sets/odyssey/BraidsCabalMinion.java @@ -44,17 +44,16 @@ import mage.filter.predicate.mageobject.CardTypePredicate; * * @author cbt33, North (Karma) */ - public class BraidsCabalMinion extends CardImpl { - - public static final FilterPermanent filter = new FilterPermanent("artifact, creature, or land"); - - static{ - filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.CREATURE), - new CardTypePredicate(CardType.LAND))); - } - + + public static final FilterPermanent filter = new FilterPermanent("artifact, creature, or land"); + + static { + filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), + new CardTypePredicate(CardType.CREATURE), + new CardTypePredicate(CardType.LAND))); + } + public BraidsCabalMinion(UUID ownerId) { super(ownerId, 117, "Braids, Cabal Minion", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); this.expansionSetCode = "ODY"; @@ -66,8 +65,8 @@ public class BraidsCabalMinion extends CardImpl { this.toughness = new MageInt(2); // At the beginning of each player's upkeep, that player sacrifices an artifact, creature, or land. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new SacrificeEffect(filter, 1, ""), TargetController.ANY, false)); - + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new SacrificeEffect(filter, 1, "that player"), TargetController.ANY, false)); + } public BraidsCabalMinion(final BraidsCabalMinion card) { diff --git a/Mage.Sets/src/mage/sets/planarchaos/VoidstoneGargoyle.java b/Mage.Sets/src/mage/sets/planarchaos/VoidstoneGargoyle.java index 9f7344ebfe0..b5fa763ce86 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/VoidstoneGargoyle.java +++ b/Mage.Sets/src/mage/sets/planarchaos/VoidstoneGargoyle.java @@ -34,13 +34,9 @@ import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.NameACardEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; -import mage.cards.repository.CardRepository; -import mage.choices.Choice; -import mage.choices.ChoiceImpl; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; @@ -49,9 +45,6 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.util.CardUtil; /** * @@ -87,46 +80,6 @@ public class VoidstoneGargoyle extends CardImpl { } } -class VoidstoneGargoyleChooseCardEffect extends OneShotEffect { - - public VoidstoneGargoyleChooseCardEffect() { - super(Outcome.Detriment); - staticText = "name a nonland card"; - } - - public VoidstoneGargoyleChooseCardEffect(final VoidstoneGargoyleChooseCardEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanentEntering(source.getSourceId()); - if (controller != null && permanent != null) { - Choice cardChoice = new ChoiceImpl(); - cardChoice.setChoices(CardRepository.instance.getNonLandNames()); - cardChoice.clearChoice(); - while (!controller.choose(Outcome.Detriment, cardChoice, game)) { - if (!controller.canRespond()) { - return false; - } - } - String cardName = cardChoice.getChoice(); - game.informPlayers(permanent.getLogName() + ", named card: [" + cardName + "]"); - game.getState().setValue(source.getSourceId().toString(), cardName); - permanent.addInfo("named card", CardUtil.addToolTipMarkTags("Named card: [" + cardName + "]"), game); - return true; - } - return false; - } - - @Override - public VoidstoneGargoyleChooseCardEffect copy() { - return new VoidstoneGargoyleChooseCardEffect(this); - } - -} - class VoidstoneGargoyleReplacementEffect1 extends ContinuousRuleModifyingEffectImpl { public VoidstoneGargoyleReplacementEffect1() { diff --git a/Mage.Sets/src/mage/sets/planechase2012/SakashimasStudent.java b/Mage.Sets/src/mage/sets/planechase2012/SakashimasStudent.java index 015ae116754..dd198dcdef6 100644 --- a/Mage.Sets/src/mage/sets/planechase2012/SakashimasStudent.java +++ b/Mage.Sets/src/mage/sets/planechase2012/SakashimasStudent.java @@ -60,7 +60,7 @@ public class SakashimasStudent extends CardImpl { // You may have Sakashima's Student enter the battlefield as a copy of any creature on the battlefield, except it's still a Ninja in addition to its other creature types. Effect effect = new CopyPermanentEffect(new FilterCreaturePermanent(), new AddSubtypeApplier("Ninja")); - effect.setText("as a copy of any creature on the battlefield, except it's still a Ninja in addition to its other creature types"); + effect.setText("as a copy of any creature on the battlefield, except it's a Ninja in addition to its other creature types"); this.addAbility(new EntersBattlefieldAbility(effect, true)); } diff --git a/Mage.Sets/src/mage/sets/ravnica/CleansingBeam.java b/Mage.Sets/src/mage/sets/ravnica/CleansingBeam.java index 1b19610a606..e5fcfb6b3e6 100644 --- a/Mage.Sets/src/mage/sets/ravnica/CleansingBeam.java +++ b/Mage.Sets/src/mage/sets/ravnica/CleansingBeam.java @@ -32,6 +32,7 @@ import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; @@ -54,6 +55,7 @@ public class CleansingBeam extends CardImpl { // Radiance — Cleansing Beam deals 2 damage to target creature and each other creature that shares a color with it. this.getSpellAbility().addEffect(new CleansingBeamEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().setAbilityWord(AbilityWord.RADIANCE); } public CleansingBeam(final CleansingBeam card) { @@ -67,6 +69,7 @@ public class CleansingBeam extends CardImpl { } class CleansingBeamEffect extends OneShotEffect { + static final FilterPermanent filter = new FilterPermanent("creature"); static { @@ -75,7 +78,7 @@ class CleansingBeamEffect extends OneShotEffect { CleansingBeamEffect() { super(Outcome.Damage); - staticText = "Radiance - Cleansing Beam deals 2 damage to target creature and each other creature that shares a color with it"; + staticText = "{this} deals 2 damage to target creature and each other creature that shares a color with it"; } CleansingBeamEffect(final CleansingBeamEffect effect) { @@ -89,7 +92,7 @@ class CleansingBeamEffect extends OneShotEffect { ObjectColor color = target.getColor(game); target.damage(2, source.getSourceId(), game, false, true); for (Permanent p : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { - if (p.getColor(game).shares(color)) { + if (!target.getId().equals(p.getId()) && p.getColor(game).shares(color)) { p.damage(2, source.getSourceId(), game, false, true); } } diff --git a/Mage.Sets/src/mage/sets/ravnica/MoltenSentry.java b/Mage.Sets/src/mage/sets/ravnica/MoltenSentry.java index fe9f25f3b27..75acb0e3fae 100644 --- a/Mage.Sets/src/mage/sets/ravnica/MoltenSentry.java +++ b/Mage.Sets/src/mage/sets/ravnica/MoltenSentry.java @@ -52,10 +52,10 @@ import mage.players.Player; * @author fireshoes */ public class MoltenSentry extends CardImpl { - + private final static String rule = "As Molten Sentry enters the battlefield, flip a coin. If the coin comes up heads, Molten Sentry enters the battlefield as a " + "5/2 creature with haste. If it comes up tails, Molten Sentry enters the battlefield as a 2/5 creature with defender."; - + public MoltenSentry(UUID ownerId) { super(ownerId, 136, "Molten Sentry", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{R}"); this.expansionSetCode = "RAV"; @@ -63,7 +63,7 @@ public class MoltenSentry extends CardImpl { this.power = new MageInt(0); this.toughness = new MageInt(0); - // As Molten Sentry enters the battlefield, flip a coin. If the coin comes up heads, Molten Sentry enters the battlefield as a 5/2 creature with haste. + // As Molten Sentry enters the battlefield, flip a coin. If the coin comes up heads, Molten Sentry enters the battlefield as a 5/2 creature with haste. // If it comes up tails, Molten Sentry enters the battlefield as a 2/5 creature with defender. this.addAbility(new EntersBattlefieldAbility(new MoltenSentryEffect(), null, rule, "")); } @@ -91,7 +91,7 @@ class MoltenSentryEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(source.getSourceId()); + Permanent permanent = game.getPermanentEntering(source.getSourceId()); if (controller != null && permanent != null) { if (controller.flipCoin(game)) { game.informPlayers("Heads: Molten Sentry enters the battlefield as a 5/2 creature with haste"); diff --git a/Mage.Sets/src/mage/sets/ravnica/WoodwraithStrangler.java b/Mage.Sets/src/mage/sets/ravnica/WoodwraithStrangler.java new file mode 100644 index 00000000000..e19cac7d6f6 --- /dev/null +++ b/Mage.Sets/src/mage/sets/ravnica/WoodwraithStrangler.java @@ -0,0 +1,69 @@ +/* + * 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.ravnica; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.effects.common.RegenerateSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterCreatureCard; +import mage.target.common.TargetCardInYourGraveyard; + +/** + * + * @author fireshoes + */ +public class WoodwraithStrangler extends CardImpl { + + public WoodwraithStrangler(UUID ownerId) { + super(ownerId, 241, "Woodwraith Strangler", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{B}{G}"); + this.expansionSetCode = "RAV"; + this.subtype.add("Plant"); + this.subtype.add("Zombie"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Exile a creature card from your graveyard: Regenerate Woodwraith Strangler. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), + new ExileFromGraveCost(new TargetCardInYourGraveyard(new FilterCreatureCard("a creature card from your graveyard"))))); + } + + public WoodwraithStrangler(final WoodwraithStrangler card) { + super(card); + } + + @Override + public WoodwraithStrangler copy() { + return new WoodwraithStrangler(this); + } +} diff --git a/Mage.Sets/src/mage/sets/returntoravnica/EpicExperiment.java b/Mage.Sets/src/mage/sets/returntoravnica/EpicExperiment.java index d66e668851b..1224ee031d2 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/EpicExperiment.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/EpicExperiment.java @@ -119,7 +119,10 @@ class EpicExperimentEffect extends OneShotEffect { } } // move cards not cast to graveyard - controller.moveCards(game.getExile().getExileZone(source.getSourceId()).getCards(game), Zone.GRAVEYARD, source, game); + ExileZone exileZone = game.getExile().getExileZone(source.getSourceId()); + if (exileZone != null) { + controller.moveCards(exileZone.getCards(game), Zone.GRAVEYARD, source, game); + } return true; } return false; diff --git a/Mage.Sets/src/mage/sets/returntoravnica/SlaughterGames.java b/Mage.Sets/src/mage/sets/returntoravnica/SlaughterGames.java index 0a87e6f0008..22711dad7bc 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/SlaughterGames.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/SlaughterGames.java @@ -28,23 +28,17 @@ package mage.sets.returntoravnica; import java.util.UUID; -import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CantBeCounteredSourceEffect; +import mage.abilities.effects.common.NameACardEffect; import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect; import mage.cards.CardImpl; -import mage.cards.repository.CardRepository; -import mage.choices.Choice; -import mage.choices.ChoiceImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.game.Game; -import mage.players.Player; import mage.target.common.TargetOpponent; /** @@ -65,9 +59,9 @@ public class SlaughterGames extends CardImpl { this.addAbility(ability); // Name a nonland card. Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. Then that player shuffles his or her library. + this.getSpellAbility().addEffect(new NameACardEffect(NameACardEffect.TypeOfName.NON_LAND_NAME)); this.getSpellAbility().addEffect(new SlaughterGamesEffect()); this.getSpellAbility().addTarget(new TargetOpponent()); - } public SlaughterGames(final SlaughterGames card) { @@ -92,29 +86,8 @@ class SlaughterGamesEffect extends SearchTargetGraveyardHandLibraryForCardNameAn @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(targetPointer.getFirst(game, source)); - Player controller = game.getPlayer(source.getControllerId()); - if (player != null && controller != null) { - Choice cardChoice = new ChoiceImpl(); - cardChoice.setChoices(CardRepository.instance.getNonLandNames()); - cardChoice.clearChoice(); - cardChoice.setMessage("Name a nonland card"); - - while (!controller.choose(Outcome.Exile, cardChoice, game)) { - if (!controller.canRespond()) { - return false; - } - } - String cardName; - cardName = cardChoice.getChoice(); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (sourceObject != null) { - game.informPlayers(sourceObject.getName() + " named card: [" + cardName + "]"); - } - - super.applySearchAndExile(game, source, cardName, player.getId()); - } - return true; + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + NameACardEffect.INFO_KEY); + return super.applySearchAndExile(game, source, cardName, targetPointer.getFirst(game, source)); } @Override @@ -122,12 +95,4 @@ class SlaughterGamesEffect extends SearchTargetGraveyardHandLibraryForCardNameAn return new SlaughterGamesEffect(this); } - @Override - public String getText(Mode mode) { - StringBuilder sb = new StringBuilder(); - sb.append("Name a nonland card. "); - sb.append(super.getText(mode)); - return sb.toString(); - } - } diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/Memoricide.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/Memoricide.java index 319abd5e941..ca9634238da 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/Memoricide.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/Memoricide.java @@ -28,19 +28,14 @@ package mage.sets.scarsofmirrodin; import java.util.UUID; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; +import mage.abilities.effects.common.NameACardEffect; import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect; import mage.cards.CardImpl; -import mage.cards.repository.CardRepository; -import mage.choices.Choice; -import mage.choices.ChoiceImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.game.Game; -import mage.players.Player; import mage.target.TargetPlayer; /** @@ -55,6 +50,7 @@ public class Memoricide extends CardImpl { // Name a nonland card. Search target player's graveyard, hand, and library for any number of cards with // that name and exile them. Then that player shuffles his or her library + this.getSpellAbility().addEffect((new NameACardEffect(NameACardEffect.TypeOfName.NON_LAND_NAME))); this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new MemoricideEffect()); } @@ -82,27 +78,8 @@ class MemoricideEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExi @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(this.getTargetPointer().getFirst(game, source)); - Player controller = game.getPlayer(source.getControllerId()); - if (player != null && controller != null) { - Choice cardChoice = new ChoiceImpl(); - cardChoice.setChoices(CardRepository.instance.getNonLandNames()); - cardChoice.clearChoice(); - cardChoice.setMessage("Name a nonland card"); - - while (!controller.choose(Outcome.Exile, cardChoice, game)) { - if (!controller.canRespond()) { - return false; - } - } - String cardName = cardChoice.getChoice(); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (sourceObject != null) { - game.informPlayers(sourceObject.getName() + " named card: [" + cardName + "]"); - } - super.applySearchAndExile(game, source, cardName, player.getId()); - } - return true; + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + NameACardEffect.INFO_KEY); + return super.applySearchAndExile(game, source, cardName, targetPointer.getFirst(game, source)); } @Override diff --git a/Mage.Sets/src/mage/sets/shadowmoor/DinOfTheFireherd.java b/Mage.Sets/src/mage/sets/shadowmoor/DinOfTheFireherd.java index 377ff8fce9f..c2120812fde 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/DinOfTheFireherd.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/DinOfTheFireherd.java @@ -60,7 +60,6 @@ public class DinOfTheFireherd extends CardImpl { // Put a 5/5 black and red Elemental creature token onto the battlefield. Target opponent sacrifices a creature for each black creature you control, then sacrifices a land for each red creature you control. this.getSpellAbility().addEffect(new DinOfTheFireherdEffect()); this.getSpellAbility().addTarget(new TargetOpponent()); - } public DinOfTheFireherd(final DinOfTheFireherd card) { @@ -74,44 +73,44 @@ public class DinOfTheFireherd extends CardImpl { } class DinOfTheFireherdEffect extends OneShotEffect { - + private final static FilterControlledCreaturePermanent blackCreatureFilter = new FilterControlledCreaturePermanent("black creatures you control"); private final static FilterControlledCreaturePermanent redCreatureFilter = new FilterControlledCreaturePermanent("red creatures you control"); - + static { blackCreatureFilter.add(new ColorPredicate(ObjectColor.BLACK)); redCreatureFilter.add(new ColorPredicate(ObjectColor.RED)); } - + public DinOfTheFireherdEffect() { super(Outcome.Neutral); this.staticText = "Put a 5/5 black and red Elemental creature token onto the battlefield. Target opponent sacrifices a creature for each black creature you control, then sacrifices a land for each red creature you control"; } - + public DinOfTheFireherdEffect(final DinOfTheFireherdEffect effect) { super(effect); } - + @Override public DinOfTheFireherdEffect copy() { return new DinOfTheFireherdEffect(this); } - + @Override public boolean apply(Game game, Ability source) { - boolean applied = false; - int blackCreaturesControllerControls = game.getBattlefield().countAll(blackCreatureFilter, source.getControllerId(), game); - int redCreaturesControllerControls = game.getBattlefield().countAll(redCreatureFilter, source.getControllerId(), game); - + boolean applied; Token token = new DinOfTheFireherdToken(); applied = token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); - + + int blackCreaturesControllerControls = game.getBattlefield().countAll(blackCreatureFilter, source.getControllerId(), game); + int redCreaturesControllerControls = game.getBattlefield().countAll(redCreatureFilter, source.getControllerId(), game); + Player targetOpponent = game.getPlayer(targetPointer.getFirst(game, source)); if (targetOpponent != null) { Effect effect = new SacrificeEffect(new FilterControlledCreaturePermanent(), blackCreaturesControllerControls, "Target Opponent"); effect.setTargetPointer(new FixedTarget(targetOpponent.getId())); effect.apply(game, source); - + Effect effect2 = new SacrificeEffect(new FilterControlledLandPermanent(), redCreaturesControllerControls, "Target Opponent"); effect2.setTargetPointer(new FixedTarget(targetOpponent.getId())); effect2.apply(game, source); @@ -122,6 +121,7 @@ class DinOfTheFireherdEffect extends OneShotEffect { } class DinOfTheFireherdToken extends Token { + public DinOfTheFireherdToken() { super("", "5/5 black and red Elemental creature"); cardType.add(CardType.CREATURE); @@ -131,4 +131,4 @@ class DinOfTheFireherdToken extends Token { power = new MageInt(5); toughness = new MageInt(5); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/shadowsoverinnistrad/GryffsBoon.java b/Mage.Sets/src/mage/sets/shadowsoverinnistrad/GryffsBoon.java index 97f5a3b1d10..848941da599 100644 --- a/Mage.Sets/src/mage/sets/shadowsoverinnistrad/GryffsBoon.java +++ b/Mage.Sets/src/mage/sets/shadowsoverinnistrad/GryffsBoon.java @@ -47,10 +47,8 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -71,16 +69,17 @@ public class GryffsBoon extends CardImpl { this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); - + // Enchanted creature gets +1/+0 and has flying. ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(1, 0, Duration.WhileOnBattlefield)); Effect effect = new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA); effect.setText("and has flying"); ability.addEffect(effect); - this.addAbility(ability); - + this.addAbility(ability); + // {3}{W}: Return Gryff's Boon from your graveyard to the battlefield attached to target creature. Activate this ability only any time you could cast a sorcery. ability = new ActivateAsSorceryActivatedAbility(Zone.GRAVEYARD, new GryffsBoonEffect(), new ManaCostsImpl("{3}{W}")); + ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } @@ -110,19 +109,11 @@ class GryffsBoonEffect extends OneShotEffect { Card aura = game.getCard(source.getSourceId()); if (aura != null && game.getState().getZone(aura.getId()).equals(Zone.GRAVEYARD)) { - Player controller = game.getPlayer(source.getControllerId()); - - FilterCreaturePermanent filter = new FilterCreaturePermanent(); - TargetPermanent target = new TargetPermanent(filter); - target.setNotTarget(true); - if (controller != null - && controller.choose(Outcome.PutCardInPlay, target, source.getSourceId(), game)) { - Permanent targetPermanent = game.getPermanent(target.getFirstTarget()); - if (!targetPermanent.cantBeEnchantedBy(aura, game)) { - game.getState().setValue("attachTo:" + aura.getId(), targetPermanent); - aura.putOntoBattlefield(game, Zone.GRAVEYARD, source.getSourceId(), controller.getId()); - return targetPermanent.addAttachment(aura.getId(), game); - } + Permanent targetPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (!targetPermanent.cantBeEnchantedBy(aura, game)) { + game.getState().setValue("attachTo:" + aura.getId(), targetPermanent); + aura.putOntoBattlefield(game, Zone.GRAVEYARD, source.getSourceId(), source.getControllerId()); + return targetPermanent.addAttachment(aura.getId(), game); } } return false; @@ -132,4 +123,4 @@ class GryffsBoonEffect extends OneShotEffect { public GryffsBoonEffect copy() { return new GryffsBoonEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/tempest/Kindle.java b/Mage.Sets/src/mage/sets/tempest/Kindle.java index 7a4a1acac86..e5a25737382 100644 --- a/Mage.Sets/src/mage/sets/tempest/Kindle.java +++ b/Mage.Sets/src/mage/sets/tempest/Kindle.java @@ -61,7 +61,7 @@ public class Kindle extends CardImpl { // Kindle deals X damage to target creature or player, where X is 2 plus the number of cards named Kindle in all graveyards. Effect effect = new DamageTargetEffect(new KindleCardsInAllGraveyardsCount(filter)); - effect.setText("{this} deals X damage to target creature or player, where X is 2 plus the number of cards named Kindle in all graveyards"); + effect.setText("{this} deals X damage to target creature or player, where X is 2 plus the number of cards named {source} in all graveyards"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); } diff --git a/Mage.Sets/src/mage/sets/timespiral/VesuvanShapeshifter.java b/Mage.Sets/src/mage/sets/timespiral/VesuvanShapeshifter.java index 4714a0e5b88..54f5bb2188f 100644 --- a/Mage.Sets/src/mage/sets/timespiral/VesuvanShapeshifter.java +++ b/Mage.Sets/src/mage/sets/timespiral/VesuvanShapeshifter.java @@ -89,7 +89,7 @@ public class VesuvanShapeshifter extends CardImpl { // At the beginning of your upkeep, you may turn this creature face down effect = new VesuvanShapeshifterFaceDownEffect(); - ability = new BeginningOfUpkeepTriggeredAbility(effect, TargetController.YOU, true); + ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, effect, TargetController.YOU, true); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/torment/InsidiousDreams.java b/Mage.Sets/src/mage/sets/torment/InsidiousDreams.java new file mode 100644 index 00000000000..bc36e509262 --- /dev/null +++ b/Mage.Sets/src/mage/sets/torment/InsidiousDreams.java @@ -0,0 +1,171 @@ +/* + * 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.torment; + +import java.util.List; +import java.util.UUID; +import mage.MageObject; + +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.VariableCostImpl; +import mage.abilities.costs.common.DiscardTargetCost; +import mage.abilities.dynamicvalue.common.GetXValue; +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.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetCardInLibrary; + +/** + * + * @author spjspj + */ +public class InsidiousDreams extends CardImpl { + + public InsidiousDreams(UUID ownerId) { + super(ownerId, 66, "Insidious Dreams", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{3}{B}"); + this.expansionSetCode = "TOR"; + + // As an additional cost to cast Insidious Dreams, discard X cards. + this.getSpellAbility().addCost(new InsidiousDreamsAdditionalCost()); + + // Search your library for X cards. Then shuffle your library and put those cards on top of it in any order. + this.getSpellAbility().addEffect(new InsidiousDreamsEffect()); + + } + + public InsidiousDreams(final InsidiousDreams card) { + super(card); + } + + @Override + public InsidiousDreams copy() { + return new InsidiousDreams(this); + } +} + +class InsidiousDreamsEffect extends OneShotEffect { + + static final private String textTop = "card to put on your library (last chosen will be on top)"; + + public InsidiousDreamsEffect() { + super(Outcome.Benefit); + this.staticText = "Search your library for up to X cards. Then shuffle your library and put those cards on top of it in any order"; + } + + public InsidiousDreamsEffect(final InsidiousDreamsEffect effect) { + super(effect); + } + + @Override + public InsidiousDreamsEffect copy() { + return new InsidiousDreamsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + int amount = new GetXValue().calculate(game, source, this); + + if (controller != null && sourceObject != null) { + TargetCardInLibrary target = new TargetCardInLibrary(0, amount, new FilterCard()); + if (controller.searchLibrary(target, game)) { + Cards chosen = new CardsImpl(); + for (UUID cardId : (List) target.getTargets()) { + Card card = controller.getLibrary().remove(cardId, game); + chosen.add(card); + } + controller.shuffleLibrary(source, game); + + TargetCard targetToLib = new TargetCard(Zone.LIBRARY, new FilterCard(textTop)); + + while (chosen.size() > 1 && controller.canRespond()) { + controller.choose(Outcome.Neutral, chosen, targetToLib, game); + Card card = chosen.get(targetToLib.getFirstTarget(), game); + if (card != null) { + chosen.remove(card); + controller.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.LIBRARY, true, false); + + } + targetToLib.clearChosen(); + } + + if (chosen.size() == 1) { + Card card = chosen.get(chosen.iterator().next(), game); + controller.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.LIBRARY, true, false); + } + } + return true; + } + return false; + } +} + +class InsidiousDreamsAdditionalCost extends VariableCostImpl { + + InsidiousDreamsAdditionalCost() { + super("cards to discard"); + this.text = "As an additional cost to cast {this}, discard X cards"; + } + + InsidiousDreamsAdditionalCost(final InsidiousDreamsAdditionalCost cost) { + super(cost); + } + + @Override + public InsidiousDreamsAdditionalCost copy() { + return new InsidiousDreamsAdditionalCost(this); + } + + @Override + public int getMaxValue(Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + return controller.getHand().size(); + } + return 0; + } + + @Override + public Cost getFixedCostsFromAnnouncedValue(int xValue) { + TargetCardInHand target = new TargetCardInHand(xValue, new FilterCard()); + return new DiscardTargetCost(target); + } +} diff --git a/Mage.Sets/src/mage/sets/vintagemasters/ThopterSquadron.java b/Mage.Sets/src/mage/sets/vintagemasters/ThopterSquadron.java index d95541f9e12..c0c4b71993e 100644 --- a/Mage.Sets/src/mage/sets/vintagemasters/ThopterSquadron.java +++ b/Mage.Sets/src/mage/sets/vintagemasters/ThopterSquadron.java @@ -75,15 +75,15 @@ public class ThopterSquadron extends CardImpl { // Thopter Squadron enters the battlefield with three +1/+1 counters on it. this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(3)), "with three +1/+1 counters on it")); - // {1}, Remove a +1/+1 counter from Thopter Squadron: Put a 1/1 colorless Thopter artifact creature token with flying onto the battlefield. Activate this ability only any time you could cast a sorcery. + // {1}, Remove a +1/+1 counter from Thopter Squadron: Put a 1/1 colorless Thopter artifact creature token with flying onto the battlefield. Activate this secondAbility only any time you could cast a sorcery. Ability firstAbility = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new ThopterColorlessToken(), 1), new GenericManaCost(1)); firstAbility.addCost(new RemoveCountersSourceCost(CounterType.P1P1.createInstance(1))); this.addAbility(firstAbility); - // {1}, Sacrifice another Thopter: Put a +1/+1 counter on Thopter Squadron. Activate this ability only any time you could cast a sorcery. - Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance(), true), new ManaCostsImpl("{1}")); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); - + // {1}, Sacrifice another Thopter: Put a +1/+1 counter on Thopter Squadron. Activate this secondAbility only any time you could cast a sorcery. + Ability secondAbility = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance(), true), new ManaCostsImpl("{1}")); + secondAbility.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); + this.addAbility(secondAbility); } public ThopterSquadron(final ThopterSquadron card) { diff --git a/Mage.Sets/src/mage/sets/visions/Fireblast.java b/Mage.Sets/src/mage/sets/visions/Fireblast.java index ddc68e0bc09..44203dea41c 100644 --- a/Mage.Sets/src/mage/sets/visions/Fireblast.java +++ b/Mage.Sets/src/mage/sets/visions/Fireblast.java @@ -28,12 +28,12 @@ package mage.sets.visions; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Rarity; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.target.common.TargetControlledPermanent; @@ -42,12 +42,12 @@ import mage.target.common.TargetCreatureOrPlayer; /** * * @author jeffwadsworth - + * */ public class Fireblast extends CardImpl { - - private static final FilterControlledPermanent filter = new FilterControlledPermanent("Mountain"); - + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("two Mountains"); + static { filter.add(new SubtypePredicate("Mountain")); } @@ -56,14 +56,13 @@ public class Fireblast extends CardImpl { super(ownerId, 79, "Fireblast", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{4}{R}{R}"); this.expansionSetCode = "VIS"; - // You may sacrifice two Mountains rather than pay Fireblast's mana cost. this.addAbility(new AlternativeCostSourceAbility(new SacrificeTargetCost(new TargetControlledPermanent(2, 2, filter, true)))); - + // Fireblast deals 4 damage to target creature or player. this.getSpellAbility().addEffect(new DamageTargetEffect(4)); this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); - + } public Fireblast(final Fireblast card) { diff --git a/Mage.Sets/src/mage/sets/zendikar/InfernoTrap.java b/Mage.Sets/src/mage/sets/zendikar/InfernoTrap.java index 0dd863a919e..2d7c397e47f 100644 --- a/Mage.Sets/src/mage/sets/zendikar/InfernoTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/InfernoTrap.java @@ -90,7 +90,7 @@ class InfernoTrapCondition implements Condition { InfernoTrapWatcher watcher = (InfernoTrapWatcher) game.getState().getWatchers().get(InfernoTrapWatcher.class.getName()); if (watcher != null) { Set damagingCreatures = watcher.getDamagingCreatures(source.getControllerId()); - return !damagingCreatures.isEmpty() && damagingCreatures.size() > 1; + return damagingCreatures != null && damagingCreatures.size() > 1; } return false; } diff --git a/Mage/src/main/java/mage/abilities/LoyaltyAbility.java b/Mage/src/main/java/mage/abilities/LoyaltyAbility.java index 677e4276bf4..084bdc37010 100644 --- a/Mage/src/main/java/mage/abilities/LoyaltyAbility.java +++ b/Mage/src/main/java/mage/abilities/LoyaltyAbility.java @@ -24,16 +24,15 @@ * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. -*/ - + */ package mage.abilities; -import mage.constants.TimingRule; -import mage.constants.Zone; import mage.abilities.costs.common.PayLoyaltyCost; import mage.abilities.costs.common.PayVariableLoyaltyCost; import mage.abilities.effects.Effect; import mage.abilities.effects.Effects; +import mage.constants.TimingRule; +import mage.constants.Zone; /** * @@ -60,6 +59,7 @@ public class LoyaltyAbility extends ActivatedAbilityImpl { super(Zone.BATTLEFIELD, effects, new PayVariableLoyaltyCost()); this.timing = TimingRule.SORCERY; } + public LoyaltyAbility(LoyaltyAbility ability) { super(ability); } diff --git a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAbility.java b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAbility.java index 40231800591..1c064ddfc59 100644 --- a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAbility.java +++ b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAbility.java @@ -49,7 +49,7 @@ public class EntersBattlefieldAbility extends StaticAbility { /** * * @param effect effect that happens when the permanent enters the - * battlefiely + * battlefield * @param optional */ public EntersBattlefieldAbility(Effect effect, boolean optional) { diff --git a/Mage/src/main/java/mage/abilities/condition/common/TenOrLessLifeCondition.java b/Mage/src/main/java/mage/abilities/condition/common/TenOrLessLifeCondition.java index d8c7bfe6e85..8b727aa593e 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/TenOrLessLifeCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/TenOrLessLifeCondition.java @@ -31,6 +31,8 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.condition.Condition; import mage.game.Game; +import mage.players.Player; +import mage.players.PlayerList; /** * @@ -38,7 +40,7 @@ import mage.game.Game; */ public class TenOrLessLifeCondition implements Condition { - public static enum CheckType { AN_OPPONENT, CONTROLLER, TARGET_OPPONENT }; + public static enum CheckType { AN_OPPONENT, CONTROLLER, TARGET_OPPONENT, EACH_PLAYER }; private final CheckType type; @@ -62,6 +64,19 @@ public class TenOrLessLifeCondition implements Condition { case TARGET_OPPONENT: //TODO: Implement this. break; + case EACH_PLAYER: + int maxLife = 0; + PlayerList playerList = game.getState().getPlayersInRange(source.getControllerId(), game); + for ( UUID pid : playerList ) { + Player p = game.getPlayer(pid); + if (p != null) { + if (maxLife < p.getLife()) { + maxLife = p.getLife(); + } + } + } + conditionApplies |= maxLife <= 10; + break; } return conditionApplies; diff --git a/Mage/src/main/java/mage/abilities/costs/common/SacrificeTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/SacrificeTargetCost.java index 6e8211707bf..7e385b60420 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/SacrificeTargetCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/SacrificeTargetCost.java @@ -51,7 +51,9 @@ public class SacrificeTargetCost extends CostImpl { public SacrificeTargetCost(TargetControlledPermanent target) { this.addTarget(target); target.setNotTarget(true); // sacrifice is never targeted - this.text = "sacrifice " + (target.getTargetName().startsWith("an") || target.getTargetName().startsWith("a ") ? "" : "a ") + target.getTargetName(); + this.text = "sacrifice " + + ((target.getNumberOfTargets() != 1 || (target.getTargetName().startsWith("an") || target.getTargetName().startsWith("a "))) + ? "" : "a ") + target.getTargetName(); target.setTargetName(target.getTargetName() + " (to sacrifice)"); } @@ -101,13 +103,16 @@ public class SacrificeTargetCost extends CostImpl { } int validTargets = 0; + int neededtargets = targets.get(0).getNumberOfTargets(); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(((TargetControlledPermanent) targets.get(0)).getFilter(), controllerId, game)) { if (game.getPlayer(activator).canPaySacrificeCost(permanent, sourceId, controllerId, game)) { validTargets++; + if (validTargets >= neededtargets) { + return true; + } } } - - return validTargets > 0; + return false; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/ClashEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ClashEffect.java index 780ba36a976..0d42a862724 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ClashEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ClashEffect.java @@ -165,6 +165,7 @@ public class ClashEffect extends OneShotEffect implements MageSingleton { // decide to put the cards on top or on the buttom of library in turn order beginning with the active player in turn order PlayerList playerList = game.getPlayerList().copy(); playerList.setCurrent(game.getActivePlayerId()); + Player nextPlayer; do { Player current = playerList.getCurrent(game); if (cardController != null && current.getId().equals(controller.getId())) { @@ -173,7 +174,8 @@ public class ClashEffect extends OneShotEffect implements MageSingleton { if (cardOpponent != null && current.getId().equals(opponent.getId())) { topOpponent = current.chooseUse(Outcome.Detriment, "Put " + cardOpponent.getLogName() + " back on top of your library? (otherwise it goes to bottom)", source, game); } - } while (!playerList.getNext(game).getId().equals(game.getActivePlayerId())); + nextPlayer = playerList.getNext(game); + } while (nextPlayer != null && !nextPlayer.getId().equals(game.getActivePlayerId())); // put the cards back to library if (cardController != null) { controller.moveCardToLibraryWithInfo(cardController, source.getSourceId(), game, Zone.LIBRARY, topController, true); diff --git a/Mage/src/main/java/mage/abilities/effects/common/TransformSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/TransformSourceEffect.java index cb872c30840..9ac6c1405f9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/TransformSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/TransformSourceEffect.java @@ -32,6 +32,8 @@ import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentCard; +import org.apache.log4j.Logger; /** * @@ -39,6 +41,8 @@ import mage.game.permanent.Permanent; */ public class TransformSourceEffect extends OneShotEffect { + private static final Logger logger = Logger.getLogger(TransformSourceEffect.class); + private boolean withoutTrigger; private boolean fromDayToNight; @@ -82,9 +86,13 @@ public class TransformSourceEffect extends OneShotEffect { } if (!game.isSimulation()) { if (fromDayToNight) { - game.informPlayers(permanent.getIdName() + " transforms into " + permanent.getSecondCardFace().getIdName()); + if (permanent.getSecondCardFace() != null) { + if (permanent instanceof PermanentCard) { + game.informPlayers(((PermanentCard) permanent).getCard().getLogName() + " transforms into " + permanent.getSecondCardFace().getLogName()); + } + } } else { - game.informPlayers(permanent.getSecondCardFace().getIdName() + " transforms into " + permanent.getIdName()); + game.informPlayers(permanent.getSecondCardFace().getLogName() + " transforms into " + permanent.getLogName()); } } } diff --git a/Mage/src/main/java/mage/constants/PlayerAction.java b/Mage/src/main/java/mage/constants/PlayerAction.java index cf79e59620b..c21bf05d458 100644 --- a/Mage/src/main/java/mage/constants/PlayerAction.java +++ b/Mage/src/main/java/mage/constants/PlayerAction.java @@ -38,6 +38,7 @@ public enum PlayerAction { PASS_PRIORITY_UNTIL_TURN_END_STEP, PASS_PRIORITY_UNTIL_NEXT_MAIN_PHASE, PASS_PRIORITY_UNTIL_NEXT_TURN, + PASS_PRIORITY_UNTIL_NEXT_TURN_SKIP_STACK, PASS_PRIORITY_UNTIL_STACK_RESOLVED, PASS_PRIORITY_UNTIL_END_STEP_BEFORE_MY_NEXT_TURN, PASS_PRIORITY_CANCEL_ALL_ACTIONS, diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java index fb917ef5f48..47bfd2ed07c 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java @@ -31,7 +31,7 @@ import mage.MageObject; import mage.abilities.Mode; import mage.filter.predicate.Predicate; import mage.game.Game; -import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.target.Target; /** @@ -48,10 +48,10 @@ public class NumberOfTargetsPredicate implements Predicate { @Override public boolean apply(MageObject input, Game game) { - Spell spell = game.getStack().getSpell(input.getId()); - if (spell != null) { + StackObject stackObject = game.getState().getStack().getStackObject(input.getId()); + if (stackObject != null) { int numberOfTargets = 0; - for (Mode mode : spell.getSpellAbility().getModes().getSelectedModes()) { + for (Mode mode : stackObject.getStackAbility().getModes().getSelectedModes()) { for (Target target : mode.getTargets()) { numberOfTargets += target.getTargets().size(); } @@ -60,6 +60,18 @@ public class NumberOfTargetsPredicate implements Predicate { return true; } } +// Spell spell = game.getStack().getSpell(input.getId()); +// if (spell != null) { +// int numberOfTargets = 0; +// for (Mode mode : spell.getSpellAbility().getModes().getSelectedModes()) { +// for (Target target : mode.getTargets()) { +// numberOfTargets += target.getTargets().size(); +// } +// } +// if (numberOfTargets == targets) { +// return true; +// } +// } return false; } diff --git a/Mage/src/main/java/mage/game/combat/CombatGroup.java b/Mage/src/main/java/mage/game/combat/CombatGroup.java index 0dfafdcb247..1d3904c7c64 100644 --- a/Mage/src/main/java/mage/game/combat/CombatGroup.java +++ b/Mage/src/main/java/mage/game/combat/CombatGroup.java @@ -487,6 +487,9 @@ public class CombatGroup implements Serializable, Copyable { attackerPerms.add(game.getPermanent(attackerId)); } UUID attackerId = player.chooseAttackerOrder(attackerPerms, game); + if (!player.isInGame()) { + break; + } attackerOrder.add(attackerId); attackerList.remove(attackerId); } diff --git a/Mage/src/main/java/mage/game/draft/DraftCube.java b/Mage/src/main/java/mage/game/draft/DraftCube.java index b45a2fa09e6..e5df9e66808 100644 --- a/Mage/src/main/java/mage/game/draft/DraftCube.java +++ b/Mage/src/main/java/mage/game/draft/DraftCube.java @@ -139,4 +139,17 @@ public abstract class DraftCube { return booster; } + + void removeFromLeftCards(CardIdentity cardId) { + if (cardId == null) { + return; + } + + for (int i = leftCubeCards.size() - 1; i >= 0; i--) { + if (leftCubeCards.get(i) == cardId) { + leftCubeCards.remove(i); + return; + } + } + } } diff --git a/Mage/src/main/java/mage/game/draft/DraftPlayer.java b/Mage/src/main/java/mage/game/draft/DraftPlayer.java index 1e87b0db1f3..14e61b04562 100644 --- a/Mage/src/main/java/mage/game/draft/DraftPlayer.java +++ b/Mage/src/main/java/mage/game/draft/DraftPlayer.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,12 +20,11 @@ * 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.draft; import java.util.ArrayList; @@ -69,12 +68,12 @@ public class DraftPlayer { public void prepareDeck() { if (!hiddenCards.isEmpty()) { Set cardsToDeck = new HashSet<>(); - for(Card card: deck.getSideboard()) { + for (Card card : deck.getSideboard()) { if (!hiddenCards.contains(card.getId())) { cardsToDeck.add(card); } } - for(Card card: cardsToDeck) { + for (Card card : cardsToDeck) { deck.getSideboard().remove(card); deck.getCards().add(card); } @@ -90,7 +89,7 @@ public class DraftPlayer { if (hiddenCards != null) { this.hiddenCards = hiddenCards; } - synchronized(booster) { + synchronized (booster) { booster.remove(card); } picking = false; @@ -101,13 +100,15 @@ public class DraftPlayer { // booster = set.createBooster(); // } // } - public void setBooster(List booster) { this.booster = booster; } public List getBooster() { - synchronized(booster) { + if (booster == null) { + return null; + } + synchronized (booster) { return new ArrayList<>(booster); } } diff --git a/Mage/src/main/java/mage/game/draft/RichManCubeBoosterDraft.java b/Mage/src/main/java/mage/game/draft/RichManCubeBoosterDraft.java new file mode 100644 index 00000000000..548b366ab81 --- /dev/null +++ b/Mage/src/main/java/mage/game/draft/RichManCubeBoosterDraft.java @@ -0,0 +1,135 @@ +/* + * 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.game.draft; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import mage.cards.Card; +import mage.cards.ExpansionSet; +import mage.game.draft.DraftCube.CardIdentity; + +/** + * + * @author spjspj + */ +public class RichManCubeBoosterDraft extends DraftImpl { + + protected int[] richManTimes = {75, 70, 65, 60, 55, 50, 45, 40, 35, 35, 35, 35, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25}; + protected final Map cardsInCube = new LinkedHashMap<>(); + + public RichManCubeBoosterDraft(DraftOptions options, List sets) { + super(options, sets); + } + + @Override + public void start() { + cardNum = 0; + while (!isAbort() && cardNum < 36) { + openBooster(); + cardNum = 0; + while (!isAbort() && pickCards()) { + passLeft(); + fireUpdatePlayersEvent(); + } + } + resetBufferedCards(); + this.fireEndDraftEvent(); + } + + @Override + protected void passLeft() { + synchronized (players) { + UUID startId = table.get(0); + UUID currentId = startId; + UUID nextId = table.getNext(); + DraftPlayer next = players.get(nextId); + draftCube.leftCubeCards.clear(); + draftCube.leftCubeCards.addAll(draftCube.getCubeCards()); + cardsInCube.clear(); + for (CardIdentity card : draftCube.leftCubeCards) { + cardsInCube.put(card.getName(), card); + } + + while (true) { + for (DraftPlayer player : players.values()) { + if (player != null && player.getDeck() != null) { + for (Card card : player.getDeck().getSideboard()) { + if (cardsInCube.get(card.getName()) != null) { + draftCube.removeFromLeftCards(cardsInCube.get(card.getName())); + } + } + } + } + + List nextBooster = draftCube.createBooster(); + next.setBooster(nextBooster); + if (nextId == startId) { + break; + } + nextId = table.getNext(); + next = players.get(nextId); + } + } + } + + @Override + protected boolean pickCards() { + cardNum++; + for (DraftPlayer player : players.values()) { + if (cardNum > 36) { + return false; + } + player.setPicking(); + player.getPlayer().pickCard(player.getBooster(), player.getDeck(), this); + } + synchronized (this) { + while (!donePicking()) { + try { + this.wait(); + } catch (InterruptedException ex) { + } + } + } + return true; + } + + @Override + public void firePickCardEvent(UUID playerId) { + DraftPlayer player = players.get(playerId); + if (cardNum > 36) { + cardNum = 36; + } + if (cardNum <= 0) { + cardNum = 1; + } + int time = richManTimes[cardNum - 1] * timing.getFactor(); + playerQueryEventSource.pickCard(playerId, "Pick card", player.getBooster(), time); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/ZombieToken.java b/Mage/src/main/java/mage/game/permanent/token/ZombieToken.java index e8736132126..3f803a61dad 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ZombieToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ZombieToken.java @@ -43,7 +43,7 @@ public class ZombieToken extends Token { final static private List tokenImageSets = new ArrayList<>(); static { - tokenImageSets.addAll(Arrays.asList("10E", "M10", "M11", "M12", "M13", "M14", "M15", "MBS", "ALA", "ISD", "C14", "C15", "CNS", "MMA", "BNG", "KTK", "DTK", "ORI", "OGW", "SOI")); + tokenImageSets.addAll(Arrays.asList("10E", "M10", "M11", "M12", "M13", "M14", "M15", "MBS", "ALA", "ISD", "C14", "C15", "CNS", "MMA", "BNG", "KTK", "DTK", "ORI", "OGW", "SOI", "EMN")); } public ZombieToken() { diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java index 15cb0d4a0f2..46e0a94458e 100644 --- a/Mage/src/main/java/mage/game/stack/StackAbility.java +++ b/Mage/src/main/java/mage/game/stack/StackAbility.java @@ -178,7 +178,9 @@ public class StackAbility extends StackObjImpl implements Ability { @Override public Abilities getAbilities() { - return emptyAbilites; + Abilities abilities = new AbilitiesImpl<>(); + abilities.add(ability); + return abilities; } @Override diff --git a/Mage/src/main/java/mage/game/stack/StackObjImpl.java b/Mage/src/main/java/mage/game/stack/StackObjImpl.java index e04169cd925..2286fd06ae5 100644 --- a/Mage/src/main/java/mage/game/stack/StackObjImpl.java +++ b/Mage/src/main/java/mage/game/stack/StackObjImpl.java @@ -113,7 +113,7 @@ public abstract class StackObjImpl implements StackObject { if (this instanceof Spell) { objectAbilities.addAll(((Spell) this).getSpellAbilities()); } else { - objectAbilities.addAll(getAbilities()); + objectAbilities.add(getStackAbility()); } for (Ability ability : objectAbilities) { // Some spells can have more than one mode @@ -210,34 +210,36 @@ public abstract class StackObjImpl implements StackObject { again = true; } } else // if possible add the alternate Target - it may not be included in the old definition nor in the already selected targets of the new definition - if (newTarget.getTargets().contains(tempTarget.getFirstTarget()) || target.getTargets().contains(tempTarget.getFirstTarget())) { - if (targetController.isHuman()) { - if (targetController.chooseUse(Outcome.Benefit, "This target was already selected from origin spell. Reset to original target?", ability, game)) { - // use previous target no target was selected - newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); + { + if (newTarget.getTargets().contains(tempTarget.getFirstTarget()) || target.getTargets().contains(tempTarget.getFirstTarget())) { + if (targetController.isHuman()) { + if (targetController.chooseUse(Outcome.Benefit, "This target was already selected from origin spell. Reset to original target?", ability, game)) { + // use previous target no target was selected + newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); + } else { + again = true; + } } else { + newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); + } + } else if (!target.canTarget(getControllerId(), tempTarget.getFirstTarget(), ability, game)) { + if (targetController.isHuman()) { + game.informPlayer(targetController, "This target is not valid!"); + again = true; + } else { + // keep the old + newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); + } + } else if (newTarget.getFirstTarget() != null && filterNewTarget != null) { + Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget()); + if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) { + game.informPlayer(targetController, "This target does not fullfil the target requirements (" + filterNewTarget.getMessage() + ")"); again = true; } } else { - newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); + // valid target was selected, add it to the new target definition + newTarget.addTarget(tempTarget.getFirstTarget(), target.getTargetAmount(targetId), ability, game, false); } - } else if (!target.canTarget(getControllerId(), tempTarget.getFirstTarget(), ability, game)) { - if (targetController.isHuman()) { - game.informPlayer(targetController, "This target is not valid!"); - again = true; - } else { - // keep the old - newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); - } - } else if (newTarget.getFirstTarget() != null && filterNewTarget != null) { - Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget()); - if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) { - game.informPlayer(targetController, "This target does not fullfil the target requirements (" + filterNewTarget.getMessage() + ")"); - again = true; - } - } else { - // valid target was selected, add it to the new target definition - newTarget.addTarget(tempTarget.getFirstTarget(), target.getTargetAmount(targetId), ability, game, false); } } while (again && targetController.canRespond()); } diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 39aa8162124..eaee1467ca0 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -28,6 +28,7 @@ package mage.players; import java.io.Serializable; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -139,6 +140,7 @@ public abstract class PlayerImpl implements Player, Serializable { private static final Logger logger = Logger.getLogger(PlayerImpl.class); private static Random rnd = new Random(); + private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); /** * Used to cancel waiting requests send to the player @@ -167,11 +169,12 @@ public abstract class PlayerImpl implements Player, Serializable { // priority control protected boolean passed; // player passed priority protected boolean passedTurn; // F4 + protected boolean passedTurnSkipStack; // F6 protected boolean passedUntilEndOfTurn; // F5 - protected boolean passedUntilNextMain; // F6 - protected boolean passedUntilStackResolved; // F8 + protected boolean passedUntilNextMain; // F7 + protected boolean passedUntilStackResolved; // F10 protected boolean passedUntilEndStepBeforeMyTurn; // F11 - protected Date dateLastAddedToStack; // F8 + protected Date dateLastAddedToStack; // F10 protected boolean skippedAtLeastOnce; // used to track if passed started in specific phase /** * This indicates that player passed all turns until his own turn starts @@ -203,7 +206,7 @@ public abstract class PlayerImpl implements Player, Serializable { protected boolean canPayLifeCost = true; protected boolean loseByZeroOrLessLife = true; protected boolean canPlayCardsFromGraveyard = true; - + protected FilterPermanent sacrificeCostFilter; protected final List alternativeSourceCosts = new ArrayList<>(); @@ -317,6 +320,7 @@ public abstract class PlayerImpl implements Player, Serializable { this.passed = player.passed; this.passedTurn = player.passedTurn; + this.passedTurnSkipStack = player.passedTurnSkipStack; this.passedUntilEndOfTurn = player.passedUntilEndOfTurn; this.passedUntilNextMain = player.passedUntilNextMain; this.skippedAtLeastOnce = player.skippedAtLeastOnce; @@ -445,6 +449,7 @@ public abstract class PlayerImpl implements Player, Serializable { this.playersUnderYourControl.clear(); this.passed = false; this.passedTurn = false; + this.passedTurnSkipStack = false; this.passedUntilEndOfTurn = false; this.passedUntilNextMain = false; this.skippedAtLeastOnce = false; @@ -609,6 +614,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public void endOfTurn(Game game) { this.passedTurn = false; + this.passedTurnSkipStack = false; } @Override @@ -1232,8 +1238,7 @@ public abstract class PlayerImpl implements Player, Serializable { } if (!ability.isUsesStack()) { ability.resolve(game); - } - else { + } else { game.fireEvent(new GameEvent(EventType.TRIGGERED_ABILITY, ability.getId(), ability.getSourceId(), ability.getControllerId())); } game.removeBookmark(bookmark); @@ -1956,6 +1961,7 @@ public abstract class PlayerImpl implements Player, Serializable { public void resetPlayerPassedActions() { this.passed = false; this.passedTurn = false; + this.passedTurnSkipStack = false; this.passedUntilEndOfTurn = false; this.passedUntilNextMain = false; this.passedUntilStackResolved = false; @@ -2004,6 +2010,7 @@ public abstract class PlayerImpl implements Player, Serializable { passedUntilNextMain = false; passedUntilEndOfTurn = false; passedTurn = false; + passedTurnSkipStack = false; passedAllTurns = true; passedUntilStackResolved = false; passedUntilEndStepBeforeMyTurn = false; @@ -2012,6 +2019,7 @@ public abstract class PlayerImpl implements Player, Serializable { case PASS_PRIORITY_UNTIL_TURN_END_STEP: // F5 passedUntilNextMain = false; passedTurn = false; + passedTurnSkipStack = false; passedAllTurns = false; passedUntilEndOfTurn = true; passedUntilStackResolved = false; @@ -2026,11 +2034,23 @@ public abstract class PlayerImpl implements Player, Serializable { passedUntilStackResolved = false; passedUntilEndStepBeforeMyTurn = false; passedTurn = true; + passedTurnSkipStack = false; + this.skip(); + break; + case PASS_PRIORITY_UNTIL_NEXT_TURN_SKIP_STACK: // F6 + passedUntilNextMain = false; + passedAllTurns = false; + passedUntilEndOfTurn = false; + passedUntilStackResolved = false; + passedUntilEndStepBeforeMyTurn = false; + passedTurn = false; + passedTurnSkipStack = true; this.skip(); break; case PASS_PRIORITY_UNTIL_NEXT_MAIN_PHASE: //F7 passedAllTurns = false; passedTurn = false; + passedTurnSkipStack = false; passedUntilEndOfTurn = false; passedUntilNextMain = true; passedUntilStackResolved = false; @@ -2041,6 +2061,7 @@ public abstract class PlayerImpl implements Player, Serializable { case PASS_PRIORITY_UNTIL_STACK_RESOLVED: //F8 passedAllTurns = false; passedTurn = false; + passedTurnSkipStack = false; passedUntilEndOfTurn = false; passedUntilNextMain = false; passedUntilStackResolved = true; @@ -2051,6 +2072,7 @@ public abstract class PlayerImpl implements Player, Serializable { case PASS_PRIORITY_UNTIL_END_STEP_BEFORE_MY_NEXT_TURN: //F11 passedAllTurns = false; passedTurn = false; + passedTurnSkipStack = false; passedUntilEndOfTurn = false; passedUntilNextMain = false; passedUntilStackResolved = false; @@ -2060,6 +2082,7 @@ public abstract class PlayerImpl implements Player, Serializable { case PASS_PRIORITY_CANCEL_ALL_ACTIONS: passedAllTurns = false; passedTurn = false; + passedTurnSkipStack = false; passedUntilEndOfTurn = false; passedUntilNextMain = false; passedUntilStackResolved = false; @@ -2950,10 +2973,10 @@ public abstract class PlayerImpl implements Player, Serializable { public void setCanPaySacrificeCostFilter(FilterPermanent filter) { this.sacrificeCostFilter = filter; } - + @Override public FilterPermanent getSacrificeCostFilter() { - return sacrificeCostFilter; + return sacrificeCostFilter; } @Override @@ -3016,7 +3039,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.LOOK_AT_FACE_DOWN, this.getId(), game)) { if (chooseUse(Outcome.Benefit, "Look at that card?", null, game)) { Cards cards = new CardsImpl(card); - this.lookAtCards(getName(), cards, game); + this.lookAtCards(getName() + " - " + sdf.format(System.currentTimeMillis()), cards, game); return true; } } diff --git a/Mage/src/main/java/mage/watchers/common/CreaturesDiedWatcher.java b/Mage/src/main/java/mage/watchers/common/CreaturesDiedWatcher.java index 23198a8e295..12daad0fc01 100644 --- a/Mage/src/main/java/mage/watchers/common/CreaturesDiedWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CreaturesDiedWatcher.java @@ -27,6 +27,8 @@ */ package mage.watchers.common; +import java.util.HashMap; +import java.util.UUID; import mage.constants.CardType; import mage.constants.WatcherScope; import mage.game.Game; @@ -41,6 +43,7 @@ import mage.watchers.Watcher; public class CreaturesDiedWatcher extends Watcher { private int amountOfCreaturesThatDied; + private final HashMap amountOfCreaturesThatDiedByController = new HashMap<>(); public CreaturesDiedWatcher() { super("CreaturesDiedWatcher", WatcherScope.GAME); @@ -49,6 +52,7 @@ public class CreaturesDiedWatcher extends Watcher { public CreaturesDiedWatcher(final CreaturesDiedWatcher watcher) { super(watcher); this.amountOfCreaturesThatDied = watcher.amountOfCreaturesThatDied; + this.amountOfCreaturesThatDiedByController.putAll(watcher.amountOfCreaturesThatDiedByController); } @Override @@ -57,6 +61,11 @@ public class CreaturesDiedWatcher extends Watcher { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; if (zEvent.isDiesEvent() && zEvent.getTarget() != null && zEvent.getTarget().getCardType().contains(CardType.CREATURE)) { amountOfCreaturesThatDied++; + int amount = 0; + if (amountOfCreaturesThatDiedByController.containsKey(zEvent.getTarget().getControllerId())) { + amount = amountOfCreaturesThatDiedByController.get(zEvent.getTarget().getControllerId()); + } + amountOfCreaturesThatDiedByController.put(zEvent.getTarget().getControllerId(), amount + 1); } } } @@ -64,12 +73,20 @@ public class CreaturesDiedWatcher extends Watcher { @Override public void reset() { amountOfCreaturesThatDied = 0; + amountOfCreaturesThatDiedByController.clear(); } public int getAmountOfCreaturesDiesThisTurn() { return amountOfCreaturesThatDied; } + public int getAmountOfCreaturesDiesThisTurn(UUID playerId) { + if (amountOfCreaturesThatDiedByController.containsKey(playerId)) { + return amountOfCreaturesThatDiedByController.get(playerId); + } + return 0; + } + @Override public CreaturesDiedWatcher copy() { return new CreaturesDiedWatcher(this); diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 96016d1af72..e12fdc8ef97 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -52320,6 +52320,7 @@ Sandsteppe Mastodon|Launch Party|29|R|{5}{G}{G}|Creature - Elephant|5|5|Reach$Wh Deathbringer Regent|Launch Party|30|R|{5}{B}{B}|Creature - Dragon|5|6|Flying$When Deathbringer Regent 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.| Mizzium Meddler|Launch Party|31|R|{2}{U}|Creature - Vedalken Wizard|1|4|Flash (You may cast this spell any time you could cast an instant.)$When Mizzium Meddler enters the battlefield, you may change a target of target spell or ability to Mizzium Meddler.| Blight Herder|Launch Party|32|R|{5}|Creature - Eldrazi Processor|4|5|When you cast Blight Herder, you may put two cards your opponents own from exile into their owners' graveyards. If you do, put three 1/1 colorless Eldrazi Scion creature tokens onto the battlefield. They have "Sacrifice this creature: Add {C} to your mana pool."| +Identity Thief|Launch Party|33|R|{2}{U}{U}|Creature - Shapeshifter|0|3|Whenever Identity Thief attacks, you may exile another target non-token creature. If you do, Identity Thief becomes a copy of that creature until end of turn. Return the exiled card to the battlefield under its owner's control at the beginning of the next end step.| Ancestral Recall|Vintage Masters|1|Bonus|{U}|Instant|||Target player draws three cards.| Afterlife|Vintage Masters|10|C|{2}{W}|Instant|||Destroy target creature. It can't be regenerated. Its controller puts a 1/1 white Spirit creature token with flying onto the battlefield.| Upheaval|Vintage Masters|100|M|{4}{U}{U}|Sorcery|||Return all permanents to their owners' hands.| @@ -53699,6 +53700,7 @@ Soul Swallower|Media Inserts|153|R|{2}{G}{G}|Creature - Wurm|3|3|Trample$Deli Elusive Tormentor|Media Inserts|154|R|{2}{B}{B}|Creature - Vampire Wizard|4|4|{1}, Discard a card: Transform Elusive Tormentor.| Insidious Mist|Media Inserts|154|R||Creature - Elemental|0|1|Hexproof, indestructible$Insideous Mist can't block and can't be blocked.$Whenever Insideous Mist attacks and isn't blocked, you may pay {2}{B}. If you do, transform it.| Ravenous Bloodseeker|Media Inserts|155|U|{1}{R}|Creature - Vampire Berserker|1|3|Discard a card: Ravenous Bloodseeker gets +2/-2 until end of turn.| +Thalia, Heretical Cathar|Media Inserts|156|R|{2}{W}|Legendary Creature - Human Soldier|3|2|First strike$Creatures and nonbasic lands your opponents control enter the battlefield tapped.| Kytheon, Hero of Akros|Media Inserts|994|Special|{W}|Legendary Creature - Human Soldier|2|1|At end of combat, if Kytheon, Hero of Akros and at least two other creatures attacked this combat, exile Kytheon, then return him to the battlefield transformed under his owner's control.${2}{W}: Kytheon gains indestructible until end of turn.| Gideon, Battle-Forged|Media Inserts|994|Special||Planeswalker - Gideon|3|+2: Up to one target creature an opponent controls attacks Gideon, Battle-Forged during its controller's next turn if able.$+1: Until your next turn, target creature gains indestructible. Untap that creature.$0: Until end of turn, Gideon, Battle-Forged becomes a 4/4 Human Soldier creature with indestructible that's still a planeswalker. Prevent all damage that would be dealt to him this turn.| Jace, Vryn's Prodigy|Media Inserts|995|Special|{1}{U}|Legendary Creature - Human Wizard|0|2|{T}: Draw a card, then discard a card. If there are five or more cards in your graveyard, exile Jace, Vryn''s Prodigy, then return him to the battefield transformed under his owner's control. | @@ -57293,3 +57295,60 @@ Shivan Dragon|Welcome Deck 2016|13|R|{4}{R}{R}|Creature - Dragon|5|5|Flying${R}: Soul of the Harvest|Welcome Deck 2016|16|R|{4}{G}{G}|Creature - Elemental|6|6|Trample$Whenever another nontoken creature enters the battlefield under your control, you may draw a card.| Sphinx of Magosi|Welcome Deck 2016|6|R|{3}{U}{U}{U}|Creature - Sphinx|6|6|Flying${2}{U}: Draw a card, then put a +1/+1 counter on Sphinx of Magosi.| Walking Corpse|Welcome Deck 2016|10|C|{1}{B}|Creature - Zombie|2|2|| +Elder Deep-Fiend|Eldritch Moon|5|R|{8}|Creature - Eldrazi Octopus|5|6|Flash$Emerge {5}{U}{U} (You may cast this spell by sacrificing a creature and paying the emerge cost reduced by that creature's converted mana cost)$When you cast Elder Deep-Fiend, tap up to four target permanents.| +Emrakul, the Promised End|Eldritch Moon|6|M|{13}|Legendary Creature - Eldrazi|13|13|Emrakul, the Promised End costs {1} less to cast for each card type among cards in your graveyard.$When you cast Emrakul, you gain control of target opponent during that player's next turn. After that turn, that player takes an extra turn.$Flying, trample, protection from instants| +Wretched Gryff|Eldritch Moon|12|C|{7}|Creature - Eldrazi Hippogriff|3|4|Emerge {5}{U} (You may cast this spell by sacrificing a creature and paying the emerge cost reduced by that creature's converted mana cost.)$When you cast Wretched Gryff, draw a card.$Flying| +Blessed Alliance|Eldritch Moon|13|U|{1}{W}|Instant|||Escalate {2} (Pay this cost for each mode chosen beyond the first.)$Choose one or more &mdash Target player gains 4 life. Untap up to two target creatures. Target opponent sacrifices an attacking creature.| +Bruna, the Fading Light|Eldritch Moon|15|R|{5}{W}{W}|Legendary Creature - Angel Horror|5|7|When you cast Bruna, the Fading Light, you may return target Angel or Human creature card from your graveyard to the battlefield.$Flying, vigilance$(Melds with Gisela, the Broken Blade.)| +Brisela, Voice of Nightmares|Eldritch Moon|15|M||Legendary Creature - Eldrazi Angel|9|10|Flying, first strike, vigilance, lifelink$Your opponents can't cast spells with converted mana cost 3 or less.| +Dawn Gryff|Eldritch Moon|19|C|{2}{W}|Creature - Hippogriff|2|2|Flying| +Gisela, the Broken Blade|Eldritch Moon|28|M|{2}{W}{W}|Legendary Creature - Angel Horror|4|3|Flying, first strike, lifelink$At the beginning of your end step, if you both own and control Gisela, the Broken Blade and a creature named Bruna, the Fading Light, exile them, then meld them into Brisela, Voice of Nightmares.| +Lone Rider|Eldritch Moon|33|U|{1}{W}|Creature - Human Knight|1|1|First strike, lifelink$At the beginning of the end step, if you gained 3 or more life this turn, transform Lone Rider.| +It That Rides as One|Eldritch Moon|33|U||Creature - Eldrazi Horror|4|4|First strike, trample, lifelink| +Long Road Home|Eldritch Moon|34|U|{1}{W}|Instant|||Exile target creature. At the beginning of the next end step, return that card to the battlefield under its owner's control with a +1/+1 counter on it.| +Sanctifier of Souls|Eldritch Moon|39|R|{3}{W}|Creature - Human Cleric|2|3|Whenever another creature enters the battlefield under your control, Sanctifier of Souls gets +1/+1 until end of turn.${2}{W}, Exile a creature card from your graveyard: Put a 1/1 white Spirit creature token with flying onto the battlefield.| +Thalia, Heretic Cathar|Eldritch Moon|46|R|{2}{W}|Legendary Creature - Human Soldier|3|2|First strike$Creatures and nonbasic lands your opponents control enter the battlefield tapped.| +Thalia's Lancers|Eldritch Moon|47|R|{3}{W}{W}|Creature - Human Knight|4|4|First strike$When Thalia's Lancers enters the battlefield, you may search your library for a legendary card, reveal it, put it into your hand, then shuffle your library.| +Chilling Grasp|Eldritch Moon|50|U|{2}{U}|Instant|||Tap up to two target creatures. Those creatures don't untap during their controller's next uptap step.$Madness {3}{U} (If you discard this card, discard it into exile. When you do, cast it for its madness cost or put it into your graveyard.)| +Coax from the Blind Eternities|Eldritch Moon|51|R|{2}{U}|Sorcery|||You may choose an Eldrazi card you own from outside the game or in exile, reveal that card, and put it into your hand.| +Curious Homunculus|Eldritch Moon|54|U|{1}{U}|Creature - Homunculus|1|1|{T}: Add {C} to your mana pool. Spend this mana only to cast an instant or sorcery spell.$At the beginning of your upkeep, if there are three or more instant and/or sorcery cards in your graveyard, transform Curious Homunculus.| +Voracious Reader|Eldritch Moon|54|U||Creature - Eldrazi Homunculus|3|4|Prowess (Whenever you cast a noncreature spell, this creature gets +1/+1 until end of turn.)$Instant and sorcery spells you cast cost {1} less to cast.| +Docent of Perfection|Eldritch Moon|56|R|{3}{U}{U}|Creature - Insect Horror|5|4|Flying$Whenever you cast an instant or sorcery spell, put a 1/1 blue Human Wizard creature token onto the battlefield. Then if you control three or more Wizards, transform Docent of Perfection.| +Final Iteration|Eldritch Moon|56|R||Creature - Eldrazi Insect|6|5|Flying$Wizards you control get +2/+1 and have flying.$Whenever you cast an instant or sorcery spell, put a 1/1 blue Human Wizard creature token onto the battlefield.| +Grizzled Angler|Eldritch Moon|63|U|{2}{U}|Creature - Human|2|3|{T}: Put the top two cards of your library into your graveyard. Then if there is a colorless creature card in your graveyard, transform Grizzled Angler.| +Grisly Anglerfish|Eldritch Moon|63|U||Creature - Eldrazi Fish|4|5|{6}: Creatures your opponents control attack this turn if able.| +Identity Thief|Eldritch Moon|64|R|{2}{U}{U}|Creature - Shapeshifter|0|3|Whenever Identity Thief attacks, you may exile another target non-token creature. If you do, Identity Thief becomes a copy of that creature until end of turn. Return the exiled card to the battlefield under its owner's control at the beginning of the next end step.| +Ingenius Skaab|Eldritch Moon|66|C|{2}{U}|Creature - Zombie Horror|2|3|Prowess (Whenever you cast a noncreature spell, this creature gets +1/+1 until end of turn.)${U}: Ingenius Skaab gets +1/-1 until end of turn.| +Niblis of Frost|Eldritch Moon|72|R|{2}{U}{U}|Creature - Spirit|3|3|Flying$Prowess (Whenever you cast a noncreature spell, this creature gets +1/+1 until end of turn.)$Whenever you cast an instant or sorcery spell, tap target creature an opponent controls. That creature doesn't untap during its controller's next untap step.| +Take Inventory|Eldritch Moon|76|C|{1}{U}|Sorcery|||Draw a card, then draw cards equal to the number of cards named Take Inventory in your graveyard.| +Unsubstantiate|Eldritch Moon|79|U|{1}{U}|Instant|||Return target spell or creature to its owner's hand.| +Borrowed Malevolence|Eldritch Moon|82|C|{B}|Instant|||Escalate {2} (Pay this cost for each mode chosen beyond the first.)$Choose one or both &mdash Target creature gets +1/+1 until end of turn. Target creature gets -1/-1 until end of turn.| +Cemetery Recruitment|Eldritch Moon|83|C|{1}{B}|Sorcery|||Return target creature card from your graveyard to your hand. If it's a Zombie card, draw a card.| +Graf Rats|Eldritch Moon|91|C|{1}{B}|Creature - Rat|2|1|At the beginning of combat on your turn, if you both own and control Graf Rats and a creature named Midnight Scavengers, exile them, then meld them into Chittering Host.| +Haunted Dead|Eldritch Moon|92|U|{3}{B}|Creature - Zombie|2|2|When Haunted Dead enters the battlefield, put a 1/1 white Spirit creature token with flying onto the battlefield.${1}{B}, Discard two cards: Return Haunted Dead from your graveyard to the battlefield tapped.| +Midnight Scavengers|Eldritch Moon|96|C|{4}{B}|Creature - Human Rogue|3|3|When Midnight Scavengers enters the battlefield, you may return target creature card with converted mana cost 3 or less from your graveyard to your hand.$(Melds with Graf Rats.)| +Noosegraf Mob|Eldritch Moon|98|R|{4}{B}{B}|Creature - Zombie|0|0|Noosegraf Mob enters the battlefield with five +1/+1 counters on it.$Whenever a player casts a spell, remove a +1/+1 counter from Noosegraf Mob. If you do, put a 2/2 black Zombie creature token onto the battlefield.| +Voldaren Pariah|Eldritch Moon|111|R|{3}{B}{B}|Creature - Vampire Horror|3|3|Flying$Sacrifice three other creatures: Transform Voldaren Pariah.$Madness {B}{B}{B} (If you discard this card, discard it into exile. When you do, cast it for its madness cost or put it into your graveyard.)| +Abolisher of Bloodlines|Eldritch Moon|111|R||Creature - Eldrazi Vampire|6|5|Flying$When this creature transforms into Abolisher of Bloodlines, target opponent sacrifices three creatures.| +Assembled Alphas|Eldritch Moon|117|R|{5}{R}|Creature - Wolf|5|5|Whenever Assembled Alphas blocks or becomes blocked by a creature, Assembled Alphas deals 3 damage to that creature and 3 damage to that creature's controller.| +Galvanic Bombardment|Eldritch Moon|129|C|{R}|Instant|||Galvanic Bombardment deals X damage to target creature, where X is 2 plus the number of cards named Galvanic Bombardment in your graveyard.| +Hanweir Garrison|Eldritch Moon|130|R|{2}{R}|Creature - Human Soldier|2|3|Whenever Hanweir Garrison attacks, put two 1/1 red Human creature tokens onto the battlefield tapped and attacking.$(Melds with Hanweir Battlements.)| +Hanweir, the Writhing Township|Eldritch Moon|130|R||Legendary Creature - Eldrazi Ooze|7|4|Trample, haste$Whenever Hanweir, the Writhing Township attacks, put two 3/2 colorless Eldrazi Horror creature tokens onto the battlefield tapped and attacking.| +Smoldering Werewolf|Eldritch Moon|142|U|{2}{R}{R}|Creature - Werewolf Horror|3|2|When Smoldering Werewolf enters the battlefield, it deals 1 damage to each of up to two target creatures.${4}{R}{R}: Transform Smoldering Werewolf.| +Erupting Dreadwolf|Eldritch Moon|142|U||Creature - Eldrazi Werewolf|6|4|Whenever Erupting Dreadwolf attacks, it deals 2 damage to target creature or player.| +Vildin-Pack Outcast|Eldritch Moon|148|C|{4}{R}|Creature - Werewolf Horror|4|4|Trample${R}: Vildin-Pack Outcast gets +1/-1 until end of turn.${5}{R}{R}: Transform Vildin-Pack Outcast.| +Dronepack Kindred|Eldritch Moon|148|C||Creature - Eldrazi Werewolf|5|7|Trample${1}: Dronepack Kindred gets +1/+0 until end of turn.| +Emrakul's Influence|Eldritch Moon|157|U|{2}{G}{G}|Enchantment|||Whenever you cast an Eldrazi creature spell with converted mana cost 7 or greater, draw two cards.| +Gnarlwood Dryad|Eldritch Moon|159|U|{G}|Creature - Dryad Horror|1|1|Deathtouch$Delirium &mdash Gnarlwood Dryad gets +2/+2 as long as there are four or more card types among cards in your graveyard.| +Kessig Prowler|Eldritch Moon|163|U|{G}|Creature - Werewolf Horror|2|1|{4}{G}: Transform Kessig Prowler.| +Sinous Predator|Eldritch Moon|163|U||Creature - Eldrazi Werewolf|4|4|Sinous Predator can't be blocked by more than one creature.| +Ulvenwald Observer|Eldritch Moon|176|R|{4}{G}{G}|Creature - Treefolk|6|6|Whenever a creature you control with toughness 4 or greater dies, draw a card.| +Bloodhall Priest|Eldritch Moon|181|R|{2}{B}{R}|Creature - Vampire Cleric|4|4|Whenever Bloodhall Priest enters the battlefield or attacks, if you have no cards in hand, Bloodhall Priest deals 2 damage to target creature or player.$Madness {1}{B}{R} (If you discard this card, discard it into exile. When you do, cast it for its madness cost or put it into your graveyard.)| +Gisa and Geralf|Eldritch Moon|183|M|{2}{U}{B}|Legendary Creature - Human Wizard|4|4|When Gisa and Geralf enters the battlefield, put the top four cards of your library into your graveyard.$During each of your turns, you may cast a Zombie creature card from your graveyard.| +Ulrich of the Krallenhorde|Eldritch Moon|191|M|{3}{R}{G}|Legendary Creature - Human Werewolf|4|4|Whenever this creature enters the battlefield or transforms into Ulrich of the Krallenhorde, target creature gets +4/+4 until end of turn.$At the beginning of each upkeep, if no spells were cast last turn, transform Ulrich of the Krallenhorde.| +Ulrich, Uncontested Alpha|Eldritch Moon|191|M||Legendary Creature - Werewolf|6|6|Whenever this creature transforms into Ulrich, Uncontested Alpha, you may have it fight target non-Werewolf creature you don't control.$At the beginning of each upkeep, if a player cast two or more spells last turn, transform Ulrich, Uncontested Alpha.| +Cryptolith Fragment|Eldritch Moon|193|U|{3}|Artifact|||Cryptolith Fragment enters the battlefield tapped.${T}: Add one mana of any color to your mana pool. Each player loses 1 life.$At the beginning of your upkeep, if each player has 10 or less life, transform Cryptolith Fragment.| +Aurora of Emrakul|Eldritch Moon|193|U||Creature - Eldrazi Reflection|1|4|Flying, deathtouch$Whenever Aurora of Emrakul attacks, each opponent loses 3 life.| +Soul Separator|Eldritch Moon|199|R|{3}|Artifact|||{5}, {T}, Sacrifice Soul Separator: Exile target creature card from your graveyard. Put a token onto the battlefield that's a copy of that card except it's 1/1, it's a Spirit in addition to its other types, and it has flying. Put a black Zombie creature token onto the battlefield with power equal to that card's power and toughness equal that card's toughness.| +Stitcher's Graft|Eldritch Moon|200|R|{1}|Artifact - Equipment|||Equipped creature gets +3/+3.$Whenever equipped creature attacks, it doesn't untap during its controller's next untap step.$Whenever Stitcher's Graft becomes unequipped from a creature, sacrifice that creature.$Equip {2}| +Hanweir Battlements|Eldritch Moon|204|R||Land|||{T}: Add {C} to your mana pool.${R},{T}: Target creature gains haste until end of turn.${3}{R}{R},{T}: If you both own and control Hanweir Battlements and a creature named Hanweir Garrison, exile them, then meld them into Hanweir, the Writhing Township.| \ No newline at end of file