Merge branch 'master' into giant-oyster

This commit is contained in:
theelk801 2018-06-24 22:24:25 -04:00 committed by GitHub
commit b1be4ad7d5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
100 changed files with 3683 additions and 421 deletions

View file

@ -8,7 +8,6 @@ import java.io.IOException;
import java.util.HashMap;
/**
*
* @author spjspj
*/
public enum AltMtgOnlTokensImageSource implements CardImageSource {
@ -60,7 +59,7 @@ public enum AltMtgOnlTokensImageSource implements CardImageSource {
}
@Override
public String generateURL(CardDownloadData card) throws Exception {
public CardImageUrls generateURL(CardDownloadData card) throws Exception {
return null;
}
@ -122,7 +121,7 @@ public enum AltMtgOnlTokensImageSource implements CardImageSource {
}
@Override
public String generateTokenUrl(CardDownloadData card) throws IOException {
public CardImageUrls generateTokenUrl(CardDownloadData card) throws IOException {
if (copyUrlToImage == null) {
setupLinks();
}
@ -139,12 +138,12 @@ public enum AltMtgOnlTokensImageSource implements CardImageSource {
}
return -1;
}
@Override
public boolean isTokenSource() {
return true;
}
@Override
public void doPause(String httpImageUrl) {
}

View file

@ -9,9 +9,9 @@ import org.mage.plugins.card.images.CardDownloadData;
*/
public interface CardImageSource {
String generateURL(CardDownloadData card) throws Exception;
CardImageUrls generateURL(CardDownloadData card) throws Exception;
String generateTokenUrl(CardDownloadData card) throws Exception;
CardImageUrls generateTokenUrl(CardDownloadData card) throws Exception;
String getNextHttpImageUrl();

View file

@ -0,0 +1,59 @@
package org.mage.plugins.card.dl.sources;
import java.util.ArrayList;
import java.util.List;
/**
* @author JayDi85
*/
public class CardImageUrls {
public String baseUrl;
public List<String> alternativeUrls;
public CardImageUrls() {
this.baseUrl = null;
this.alternativeUrls = new ArrayList<>();
}
public CardImageUrls(String baseUrl) {
this(baseUrl, null);
}
public CardImageUrls(String baseUrl, String alternativeUrl) {
this();
this.baseUrl = baseUrl;
if (alternativeUrl != null && !alternativeUrl.isEmpty()) {
this.alternativeUrls.add(alternativeUrl);
}
}
public List<String> getDownloadList() {
List<String> downloadUrls = new ArrayList<>();
if (this.baseUrl != null && !this.baseUrl.isEmpty()) {
downloadUrls.add(this.baseUrl);
}
// no needs in base url duplicate
if (this.alternativeUrls != null) {
for (String url : this.alternativeUrls) {
if (!url.equals(this.baseUrl)) {
downloadUrls.add(url);
}
}
}
return downloadUrls;
}
public void addAlternativeUrl(String url) {
if (url != null && !url.isEmpty()) {
this.alternativeUrls.add(url);
} else {
throw new IllegalArgumentException();
}
}
}

View file

@ -0,0 +1,255 @@
package org.mage.plugins.card.dl.sources;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JOptionPane;
import mage.cards.Sets;
import org.mage.plugins.card.images.CardDownloadData;
/**
*
* @author spjspj
*/
public enum CopyPasteImageSource implements CardImageSource {
instance;
private Set<String> supportedSets = new LinkedHashSet<String>();
private Set<String> missingCards = new LinkedHashSet<String>();
HashMap<String, String> singleLinks = null;
boolean loadedFromDialog = false;
boolean viewMissingCards = true;
HashMap<String, Integer> singleLinksDone = null;
private static int maxTimes = 2;
@Override
public String getSourceName() {
return "";
}
@Override
public float getAverageSize() {
return 260.7f;
}
@Override
public String getNextHttpImageUrl() {
if (singleLinks == null) {
setupLinks();
}
for (String key : singleLinksDone.keySet()) {
if (singleLinksDone.get(key) < maxTimes) {
singleLinksDone.put(key, maxTimes);
return key;
}
}
if (maxTimes < 2) {
maxTimes++;
}
for (String key : singleLinksDone.keySet()) {
if (singleLinksDone.get(key) < maxTimes) {
singleLinksDone.put(key, maxTimes);
return key;
}
}
return null;
}
@Override
public String getFileForHttpImage(String httpImageUrl) {
String copy = httpImageUrl;
if (copy != null) {
return singleLinks.get(copy);
}
return null;
}
@Override
public CardImageUrls generateURL(CardDownloadData card) throws Exception {
if (singleLinks == null) {
setupLinks();
}
String url = singleLinks.get(card.getSet() + "/" + card.getName());
if (url != null && url.length() > 0) {
return new CardImageUrls(url);
}
url = singleLinks.get(card.getSet() + "/" + card.getName() + "." + card.getCollectorId());
if (url != null && url.length() > 0) {
return new CardImageUrls(url);
}
return null;
}
int ls_size_mc = 0;
int ls_size_ss = 0;
int ls_size_sl = 0;
int num_nos = 0;
private boolean isDifferent() {
boolean isdiff = false;
if (ls_size_mc != missingCards.size()) {
ls_size_mc = missingCards.size();
isdiff = true;
}
if (ls_size_ss != supportedSets.size()) {
ls_size_ss = supportedSets.size();
isdiff = true;
}
if (ls_size_sl != singleLinks.size()) {
ls_size_sl = singleLinks.size();
isdiff = true;
}
num_nos++;
if (num_nos > 2) {
num_nos = 0;
isdiff = true;
}
return isdiff;
}
private void setupLinks() {
if (singleLinks != null && loadedFromDialog) {
if (!viewMissingCards) {
if (isDifferent() && JOptionPane.showConfirmDialog(null,
"View your missing cards and reset the list of card images to download again?",
"View missing cards (found " + missingCards.size() + ") / Reset URLs to download ", JOptionPane.YES_NO_OPTION)
== JOptionPane.YES_OPTION) {
viewMissingCards = true;
singleLinks.clear();
loadedFromDialog = false;
supportedSets.clear();
} else {
return;
}
}
if (!(viewMissingCards && missingCards.size() > 0)) {
return;
}
}
singleLinks = new HashMap<>();
loadedFromDialog = false;
final CopyPasteImageSourceDialog dialog = new CopyPasteImageSourceDialog();
dialog.pack();
int count = 0;
if (viewMissingCards && missingCards.size() > 0 && singleLinks.size() == 0) {
viewMissingCards = false;
String displayMissingCardsStr = "Up to the first 20 cards are:\n";
String missingCardsStr = "";
if (this.missingCards != null) {
for (String card : this.missingCards) {
if (count < 20) {
displayMissingCardsStr = displayMissingCardsStr + card + "\n";
}
missingCardsStr = missingCardsStr + card + "\n";
count++;
}
}
StringSelection stringSelection = new StringSelection(missingCardsStr);
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(stringSelection, null);
if (isDifferent() && JOptionPane.showConfirmDialog(null,
displayMissingCardsStr + "\n\nReset the list again?\n(NB: The full list has been copied to the clipboard)",
"Your missing cards (found " + missingCards.size() + "): ", JOptionPane.YES_NO_OPTION)
== JOptionPane.YES_OPTION) {
viewMissingCards = true;
singleLinks.clear();
loadedFromDialog = false;
supportedSets.clear();
} else {
return;
}
}
dialog.setVisible(true);
String[] lines = dialog.getPastedData().split(System.getProperty("line.separator"));
for (String line : lines) {
// Break into >> "\1", "\2"
Pattern regex = Pattern.compile("\\s*\"(.*?)/(.*?)\"\\s*,\\s*\"(.*?)\"");
Matcher regexMatcher = regex.matcher(line);
while (regexMatcher.find()) {
String setCode = regexMatcher.group(1);
String cardName = regexMatcher.group(2);
String imageURL = regexMatcher.group(3);
supportedSets.add(setCode);
singleLinks.put(setCode + "/" + cardName, imageURL);
isDifferent();
}
}
loadedFromDialog = true;
if (lines.length == 0) {
loadedFromDialog = false;
viewMissingCards = true;
}
}
@Override
public CardImageUrls generateTokenUrl(CardDownloadData card) throws IOException {
try {
return generateURL(card);
} catch (Exception ex) {
}
return null;
}
@Override
public int getTotalImages() {
if (singleLinks == null) {
setupLinks();
}
if (singleLinks != null) {
return singleLinks.size();
}
return -1;
}
@Override
public boolean isTokenSource() {
return false;
}
@Override
public ArrayList<String> getSupportedSets() {
setupLinks();
ArrayList<String> supportedSetsCopy = new ArrayList<>();
if (supportedSets.size() == 0) {
for (String setCode : Sets.getInstance().keySet()) {
supportedSets.add(setCode);
}
}
supportedSetsCopy.addAll(supportedSets);
return supportedSetsCopy;
}
@Override
public void doPause(String httpImageUrl) {
}
@Override
public boolean isImageProvided(String setCode, String cardName) {
missingCards.add(setCode + "/" + cardName);
if (singleLinks != null) {
return singleLinks.containsKey(setCode + "/" + cardName) || singleLinks.containsKey(setCode + "/" + cardName + "-a");
}
return false;
}
@Override
public boolean isSetSupportedComplete(String setCode) {
return false;
}
}

View file

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.mage.plugins.card.dl.sources.CopyPasteImageSourceDialog">
<grid id="cbd77" binding="contentPane" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="10" left="10" bottom="10" right="10"/>
<constraints>
<xy x="48" y="54" width="540" height="500"/>
</constraints>
<properties>
<minimumSize width="540" height="450"/>
</properties>
<border type="none"/>
<children>
<grid id="94766" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<hspacer id="98af6">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
<grid id="9538f" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="true" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="e7465" class="javax.swing.JButton" binding="buttonOK">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Import"/>
</properties>
</component>
<component id="5723f" class="javax.swing.JButton" binding="buttonCancel">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Cancel"/>
</properties>
</component>
</children>
</grid>
</children>
</grid>
<grid id="e3588" layout-manager="FormLayout">
<rowspec value="center:d:grow"/>
<colspec value="fill:d:noGrow"/>
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="f8bac" class="javax.swing.JEditorPane" binding="txtDeckList">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
<preferred-size width="150" height="50"/>
</grid>
<forms defaultalign-horz="false" defaultalign-vert="false"/>
</constraints>
<properties>
<minimumSize width="250" height="400"/>
<preferredSize width="550" height="400"/>
</properties>
</component>
</children>
</grid>
</children>
</grid>
</form>

View file

@ -0,0 +1,182 @@
package org.mage.plugins.card.dl.sources;
import mage.util.StreamUtils;
import java.awt.*;
import java.awt.event.*;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Set;
import javax.swing.*;
public class CopyPasteImageSourceDialog extends JDialog {
private JPanel contentPane;
private JButton buttonOK;
private JButton buttonCancel;
private JEditorPane txtDeckList;
private String tmpPath;
public CopyPasteImageSourceDialog() {
initComponents();
setContentPane(contentPane);
setModal(true);
getRootPane().setDefaultButton(buttonOK);
buttonOK.addActionListener(e -> onOK());
buttonCancel.addActionListener(e -> onCancel());
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
onCancel();
}
});
// Close on "ESC"
contentPane.registerKeyboardAction(e -> onCancel(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
}
private void onOK() {
BufferedWriter bw = null;
try {
File temp = File.createTempFile("import_images_from_url", ".txt");
bw = new BufferedWriter(new FileWriter(temp));
bw.write(txtDeckList.getText());
tmpPath = temp.getPath();
} catch (IOException e) {
e.printStackTrace();
} finally {
StreamUtils.closeQuietly(bw);
}
dispose();
}
private void onCancel() {
dispose();
}
public String getTmpPath() {
return tmpPath;
}
private void initComponents() {
contentPane = new JPanel();
JPanel panel1 = new JPanel();
JPanel panel2 = new JPanel();
buttonOK = new JButton();
buttonCancel = new JButton();
JPanel panel3 = new JPanel();
txtDeckList = new JEditorPane();
{
contentPane.setMinimumSize(new Dimension(540, 450));
contentPane.setBorder(new javax.swing.border.CompoundBorder(
new javax.swing.border.TitledBorder(new javax.swing.border.EmptyBorder(0, 0, 0, 0),
"Download Images from Copy/Pasted Text", javax.swing.border.TitledBorder.CENTER,
javax.swing.border.TitledBorder.TOP, new java.awt.Font("Dialog", java.awt.Font.PLAIN, 12),
java.awt.Color.BLACK), contentPane.getBorder()));
contentPane.addPropertyChangeListener(e -> {
if ("border".equals(e.getPropertyName())) {
throw new RuntimeException();
}
});
contentPane.addPropertyChangeListener(e -> {
if ("border".equals(e.getPropertyName())) {
throw new RuntimeException();
}
});
contentPane.setLayout(new GridBagLayout());
((GridBagLayout) contentPane.getLayout()).columnWidths = new int[]{0, 0};
((GridBagLayout) contentPane.getLayout()).rowHeights = new int[]{0, 0, 0};
((GridBagLayout) contentPane.getLayout()).columnWeights = new double[]{0.01, 1.0E-4};
((GridBagLayout) contentPane.getLayout()).rowWeights = new double[]{0.01, 0.0, 1.0E-4};
{
panel1.setLayout(new GridBagLayout());
((GridBagLayout) panel1.getLayout()).columnWidths = new int[]{0, 0, 0};
((GridBagLayout) panel1.getLayout()).rowHeights = new int[]{0, 0};
((GridBagLayout) panel1.getLayout()).columnWeights = new double[]{0.0, 0.01, 1.0E-4};
((GridBagLayout) panel1.getLayout()).rowWeights = new double[]{0.01, 1.0E-4};
{
panel2.setLayout(new GridBagLayout());
((GridBagLayout) panel2.getLayout()).columnWidths = new int[]{0, 4, 0, 0};
((GridBagLayout) panel2.getLayout()).rowHeights = new int[]{0, 0};
((GridBagLayout) panel2.getLayout()).columnWeights = new double[]{0.01, 0.0, 0.01, 1.0E-4};
((GridBagLayout) panel2.getLayout()).rowWeights = new double[]{0.0, 1.0E-4};
//---- buttonOK ----
buttonOK.setText("Download from URLs");
panel2.add(buttonOK, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
new Insets(0, 0, 0, 0), 0, 0));
//---- buttonCancel ----
buttonCancel.setText("Cancel");
panel2.add(buttonCancel, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
new Insets(0, 0, 0, 0), 0, 0));
}
panel1.add(panel2, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
}
contentPane.add(panel1, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
{
panel3.setLayout(new GridBagLayout());
((GridBagLayout) panel3.getLayout()).columnWidths = new int[]{0, 0};
((GridBagLayout) panel3.getLayout()).rowHeights = new int[]{0, 0};
((GridBagLayout) panel3.getLayout()).columnWeights = new double[]{0.0, 1.0E-4};
((GridBagLayout) panel3.getLayout()).rowWeights = new double[]{1.0, 1.0E-4};
txtDeckList.setMinimumSize(new Dimension(250, 400));
txtDeckList.setPreferredSize(new Dimension(550, 400));
txtDeckList.setText("// Example follows. \nNB: **DELETE ALL TEXT AND GO SELECT THIS SOURCE AGAIN TO SEE THE NAMES CARDS YOU'RE MISSING IMAGES FOR!!!***\n\"SWS/Might of the Wild\", \"http://i.imgur.com/eNXOdxp.jpg\"\n\"PTC/Wolf of Devil's Breach\", \"https://img.scryfall.com/cards/large/en/psoi/192s.jpg\"\n\nExpected columns: Name of Card (Set Trigraph\\Name), URL of image\n\n\n");
JScrollPane txtScrollableDeckList = new JScrollPane(txtDeckList);
panel3.add(txtScrollableDeckList, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
}
contentPane.add(panel3, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 5, 0), 0, 0));
}
}
Set<String> missingCards = null;
public void addMissingCards(Set<String> missingCards) {
this.missingCards = missingCards;
String missingCardsStr = "";
boolean found = false;
if (this.missingCards != null) {
for (String card : this.missingCards) {
found = true;
missingCardsStr = missingCardsStr + card + "\n";
}
}
if (found == false) {
missingCardsStr = "\n\nNote: Leave blank to see your missing card names!\n";
}
txtDeckList.setText(txtDeckList.getText() + "\n\nYour missing card images are:\n" + missingCardsStr);
}
public String getPastedData() {
return txtDeckList.getText();
}
}

View file

@ -8,11 +8,11 @@ import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.apache.log4j.Logger;
import org.mage.plugins.card.images.CardDownloadData;
/**
*
* @author spjspj
*/
public enum GrabbagImageSource implements CardImageSource {
@ -48,7 +48,7 @@ public enum GrabbagImageSource implements CardImageSource {
}
@Override
public String generateURL(CardDownloadData card) throws Exception {
public CardImageUrls generateURL(CardDownloadData card) throws Exception {
if (singleLinks == null) {
setupLinks();
}
@ -63,7 +63,7 @@ public enum GrabbagImageSource implements CardImageSource {
}
if (url != null) {
return getSourceName(card, url) + url;
return new CardImageUrls(getSourceName(card, url) + url);
}
return null;
}
@ -375,7 +375,7 @@ public enum GrabbagImageSource implements CardImageSource {
}
@Override
public String generateTokenUrl(CardDownloadData card) throws IOException {
public CardImageUrls generateTokenUrl(CardDownloadData card) throws IOException {
try {
return generateURL(card);
} catch (Exception ex) {

View file

@ -6,12 +6,12 @@ import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import mage.client.dialog.PreferencesDialog;
import org.mage.plugins.card.images.CardDownloadData;
import org.mage.plugins.card.utils.CardImageUtils;
/**
*
* @author North
*/
public enum MagicCardsImageSource implements CardImageSource {
@ -342,6 +342,7 @@ public enum MagicCardsImageSource implements CardImageSource {
put("WWK", "worldwake");
put("ZEN", "zendikar");
}
private static final long serialVersionUID = 1L;
};
@ -361,7 +362,7 @@ public enum MagicCardsImageSource implements CardImageSource {
}
@Override
public String generateURL(CardDownloadData card) throws Exception {
public CardImageUrls generateURL(CardDownloadData card) throws Exception {
String collectorId = card.getCollectorId();
String cardSet = card.getSet();
if (collectorId == null || cardSet == null) {
@ -389,11 +390,11 @@ public enum MagicCardsImageSource implements CardImageSource {
}
url.append(".jpg");
return url.toString();
return new CardImageUrls(url.toString());
}
@Override
public String generateTokenUrl(CardDownloadData card) {
public CardImageUrls generateTokenUrl(CardDownloadData card) {
String name = card.getName();
// add type to name if it's not 0
if (card.getType() > 0) {
@ -406,7 +407,7 @@ public enum MagicCardsImageSource implements CardImageSource {
} else {
set += '-' + card.getSet();
}
return "http://magiccards.info/extras/token/" + set + '/' + name + ".jpg";
return new CardImageUrls("http://magiccards.info/extras/token/" + set + '/' + name + ".jpg");
}
@Override

View file

@ -8,10 +8,10 @@ import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.mage.plugins.card.images.CardDownloadData;
/**
*
* @author Pete Rossi
*/
public enum MagidexImageSource implements CardImageSource {
@ -233,7 +233,7 @@ public enum MagidexImageSource implements CardImageSource {
}
@Override
public String generateURL(CardDownloadData card) throws Exception {
public CardImageUrls generateURL(CardDownloadData card) throws Exception {
String cardDownloadName = card.getDownloadName().toLowerCase(Locale.ENGLISH);
String cardSet = card.getSet();
@ -247,7 +247,7 @@ public enum MagidexImageSource implements CardImageSource {
// This will properly escape the url
URI uri = new URI("http", "magidex.com", "/extstatic/card/" + formatSetName(cardSet) + '/' + cardDownloadName + ".jpg", null, null);
return uri.toASCIIString();
return new CardImageUrls(uri.toASCIIString());
}
private String formatSetName(String setName) {
@ -264,7 +264,7 @@ public enum MagidexImageSource implements CardImageSource {
};
@Override
public String generateTokenUrl(CardDownloadData card) {
public CardImageUrls generateTokenUrl(CardDownloadData card) {
return null;
}

View file

@ -2,14 +2,12 @@
package org.mage.plugins.card.dl.sources;
import java.util.Locale;
import org.mage.plugins.card.images.CardDownloadData;
/**
* Site was shutdown by wizards Feb. 2015
*
*
*
*
* @author LevelX2
*/
public enum MtgImageSource implements CardImageSource {
@ -32,7 +30,7 @@ public enum MtgImageSource implements CardImageSource {
}
@Override
public String generateURL(CardDownloadData card) throws Exception {
public CardImageUrls generateURL(CardDownloadData card) throws Exception {
String collectorId = card.getCollectorId();
String cardSet = card.getSet();
if (collectorId == null || cardSet == null) {
@ -59,11 +57,11 @@ public enum MtgImageSource implements CardImageSource {
}
url.append(".jpg");
return url.toString();
return new CardImageUrls(url.toString());
}
@Override
public String generateTokenUrl(CardDownloadData card) {
public CardImageUrls generateTokenUrl(CardDownloadData card) {
return null;
}

View file

@ -3,11 +3,11 @@ package org.mage.plugins.card.dl.sources;
import java.io.IOException;
import java.util.HashMap;
import org.apache.log4j.Logger;
import org.mage.plugins.card.images.CardDownloadData;
/**
*
* @author spjspj
*/
public enum MtgOnlTokensImageSource implements CardImageSource {
@ -59,7 +59,7 @@ public enum MtgOnlTokensImageSource implements CardImageSource {
}
@Override
public String generateURL(CardDownloadData card) throws Exception {
public CardImageUrls generateURL(CardDownloadData card) throws Exception {
return null;
}
@ -324,7 +324,7 @@ public enum MtgOnlTokensImageSource implements CardImageSource {
}
@Override
public String generateTokenUrl(CardDownloadData card) throws IOException {
public CardImageUrls generateTokenUrl(CardDownloadData card) throws IOException {
if (copyUrlToImage == null) {
setupLinks();
}

View file

@ -18,6 +18,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.prefs.Preferences;
import mage.client.MageFrame;
import mage.remote.Connection;
import mage.remote.Connection.ProxyType;
@ -28,7 +29,6 @@ import org.jsoup.select.Elements;
import org.mage.plugins.card.images.CardDownloadData;
/**
*
* @author LevelX2
*/
public enum MythicspoilerComSource implements CardImageSource {
@ -329,7 +329,7 @@ public enum MythicspoilerComSource implements CardImageSource {
}
private Map<String, String> getSetLinksFromPage(String cardSet, Set<String> aliasesStart, Preferences prefs,
ProxyType proxyType, String baseUrl, String pageUrl) throws IOException {
ProxyType proxyType, String baseUrl, String pageUrl) throws IOException {
Map<String, String> pageLinks = new HashMap<>();
String urlDocument;
@ -391,7 +391,7 @@ public enum MythicspoilerComSource implements CardImageSource {
}
@Override
public String generateURL(CardDownloadData card) throws Exception {
public CardImageUrls generateURL(CardDownloadData card) throws Exception {
String collectorId = card.getCollectorId();
String cardSet = card.getSet();
if (collectorId == null || cardSet == null) {
@ -410,11 +410,11 @@ public enum MythicspoilerComSource implements CardImageSource {
.replaceAll(",", "")
.replaceAll("/", "");
String link = setLinks.get(searchName);
return link;
return new CardImageUrls(link);
}
@Override
public String generateTokenUrl(CardDownloadData card
public CardImageUrls generateTokenUrl(CardDownloadData card
) {
return null;
}

View file

@ -6,19 +6,34 @@ import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import mage.client.dialog.PreferencesDialog;
import org.mage.plugins.card.images.CardDownloadData;
/**
* @author Quercitron, JayDi85
*
*/
public enum ScryfallImageSource implements CardImageSource {
instance;
private final Set<String> supportedSets;
private final Map<String, String> languageAliases;
ScryfallImageSource() {
// https://scryfall.com/docs/api/languages
languageAliases = new HashMap<>();
languageAliases.put("en", "en");
languageAliases.put("es", "es");
languageAliases.put("jp", "ja");
languageAliases.put("it", "it");
languageAliases.put("fr", "fr");
languageAliases.put("cn", "zhs"); // Simplified Chinese
languageAliases.put("de", "de");
languageAliases.put("ko", "ko");
languageAliases.put("pt", "pt");
languageAliases.put("ru", "ru");
supportedSets = new LinkedHashSet<>();
// supportedSets.add("PTC"); //
supportedSets.add("LEA");
@ -214,33 +229,52 @@ public enum ScryfallImageSource implements CardImageSource {
supportedSets.add("BBD");
// supportedSets.add("CM2");
supportedSets.add("M19");
}
@Override
public String generateURL(CardDownloadData card) throws Exception {
public CardImageUrls generateURL(CardDownloadData card) throws Exception {
String preferredLanguage = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PREF_LANGUAGE, "en");
String defaultCode = "en";
String localizedCode = languageAliases.getOrDefault(preferredLanguage, defaultCode);
// loc example: https://api.scryfall.com/cards/xln/121/ru?format=image
// TODO: do not use API at all? It's can help with scryfall request limits (1 request instead 2)
String baseUrl = null;
String alternativeUrl = null;
// special card number like "103a" already compatible
if (card.isCollectorIdWithStr()) {
return "https://img.scryfall.com/cards/large/en/" + formatSetName(card.getSet()) + "/"
if (baseUrl == null && card.isCollectorIdWithStr()) {
baseUrl = "https://img.scryfall.com/cards/large/" + localizedCode + "/" + formatSetName(card.getSet()) + "/"
+ card.getCollectorId() + ".jpg";
alternativeUrl = "https://img.scryfall.com/cards/large/" + defaultCode + "/" + formatSetName(card.getSet()) + "/"
+ card.getCollectorId() + ".jpg";
}
// double faced cards do not supporte by API (need direct link for img)
// double faced cards do not supports by API (need direct link for img)
// example: https://img.scryfall.com/cards/large/en/xln/173b.jpg
if (card.isTwoFacedCard()) {
return "https://img.scryfall.com/cards/large/en/" + formatSetName(card.getSet()) + "/"
if (baseUrl == null && card.isTwoFacedCard()) {
baseUrl = "https://img.scryfall.com/cards/large/" + localizedCode + "/" + formatSetName(card.getSet()) + "/"
+ card.getCollectorId() + (card.isSecondSide() ? "b" : "a") + ".jpg";
alternativeUrl = "https://img.scryfall.com/cards/large/" + defaultCode + "/" + formatSetName(card.getSet()) + "/"
+ card.getCollectorId() + (card.isSecondSide() ? "b" : "a") + ".jpg";
}
// basic cards by api call (redirect to img link)
// example: https://api.scryfall.com/cards/xln/121?format=image
return "https://api.scryfall.com/cards/" + formatSetName(card.getSet()) + "/"
+ card.getCollectorId() + "?format=image";
// example: https://api.scryfall.com/cards/xln/121/en?format=image
if (baseUrl == null) {
baseUrl = "https://api.scryfall.com/cards/" + formatSetName(card.getSet()) + "/"
+ card.getCollectorId() + "/" + localizedCode + "?format=image";
alternativeUrl = "https://api.scryfall.com/cards/" + formatSetName(card.getSet()) + "/"
+ card.getCollectorId() + "/" + defaultCode + "?format=image";
}
return new CardImageUrls(baseUrl, alternativeUrl);
}
@Override
public String generateTokenUrl(CardDownloadData card) throws Exception {
public CardImageUrls generateTokenUrl(CardDownloadData card) throws Exception {
return null;
}

View file

@ -16,6 +16,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import mage.constants.SubType;
import org.apache.log4j.Logger;
import org.mage.plugins.card.images.CardDownloadData;
@ -23,7 +24,6 @@ import org.mage.plugins.card.images.DownloadPictures;
import org.mage.plugins.card.utils.CardImageUtils;
/**
*
* @author Quercitron
*/
public enum TokensMtgImageSource implements CardImageSource {
@ -58,7 +58,7 @@ public enum TokensMtgImageSource implements CardImageSource {
}
@Override
public String generateURL(CardDownloadData card) throws Exception {
public CardImageUrls generateURL(CardDownloadData card) throws Exception {
return null;
}
@ -80,7 +80,7 @@ public enum TokensMtgImageSource implements CardImageSource {
}
@Override
public String generateTokenUrl(CardDownloadData card) throws IOException {
public CardImageUrls generateTokenUrl(CardDownloadData card) throws IOException {
String name = card.getName();
String set = card.getSet();
int type = card.getType();
@ -125,7 +125,7 @@ public enum TokensMtgImageSource implements CardImageSource {
String url = "http://tokens.mtg.onl/tokens/" + tokenData.getExpansionSetCode().trim() + '_'
+ tokenData.getNumber().trim() + '-' + tokenData.getName().trim() + ".jpg";
url = url.replace(' ', '-');
return url;
return new CardImageUrls(url);
}
@Override
@ -248,7 +248,7 @@ public enum TokensMtgImageSource implements CardImageSource {
List<TokenData> newTokensData = new ArrayList<>();
try (InputStreamReader inputReader = new InputStreamReader(inputStream, "Cp1252");
BufferedReader reader = new BufferedReader(inputReader)) {
BufferedReader reader = new BufferedReader(inputReader)) {
// we have to specify encoding to read special comma
reader.readLine(); // skip header

View file

@ -20,6 +20,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.prefs.Preferences;
import mage.cards.Sets;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
@ -462,7 +463,7 @@ public enum WizardCardsImageSource implements CardImageSource {
}
@Override
public String generateURL(CardDownloadData card) throws Exception {
public CardImageUrls generateURL(CardDownloadData card) throws Exception {
String collectorId = card.getCollectorId();
String cardSet = card.getSet();
if (collectorId == null || cardSet == null) {
@ -495,7 +496,7 @@ public enum WizardCardsImageSource implements CardImageSource {
List<String> l = new ArrayList<>(setLinks.values());
if (l.size() >= number) {
link = l.get(number - 1);
} else {;
} else {
link = l.get(number - 21);
if (link != null) {
link = link.replace(Integer.toString(number - 20), (Integer.toString(number - 20) + 'a'));
@ -508,8 +509,12 @@ public enum WizardCardsImageSource implements CardImageSource {
if (link != null && !link.startsWith("http://")) {
link = "http://gatherer.wizards.com" + link;
}
return link;
if (link != null) {
return new CardImageUrls(link);
} else {
return null;
}
}
private Map<String, String> getSetLinks(String cardSet) {
@ -703,7 +708,7 @@ public enum WizardCardsImageSource implements CardImageSource {
}
@Override
public String generateTokenUrl(CardDownloadData card) {
public CardImageUrls generateTokenUrl(CardDownloadData card) {
return null;
}
@ -712,7 +717,7 @@ public enum WizardCardsImageSource implements CardImageSource {
return 60.0f;
}
// private final class GetImageLinkTask implements Runnable {
// private final class GetImageLinkTask implements Runnable {
//
// private int multiverseId;
// private String cardName;

View file

@ -15,6 +15,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
import mage.cards.ExpansionSet;
import mage.cards.Sets;
import mage.cards.repository.CardCriteria;
@ -33,6 +34,7 @@ import org.apache.log4j.Logger;
import org.mage.plugins.card.dl.sources.*;
import org.mage.plugins.card.properties.SettingsManager;
import org.mage.plugins.card.utils.CardImageUtils;
import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
public class DownloadPictures extends DefaultBoundedRangeModel implements Runnable {
@ -42,9 +44,9 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
private static final Logger logger = Logger.getLogger(DownloadPictures.class);
public static final String ALL_IMAGES = "- All images from that source";
public static final String ALL_STANDARD_IMAGES = "- All images from standard from that source";
public static final String ALL_TOKENS = "- Only all token images from that source";
public static final String ALL_IMAGES = "- ALL images from selected source (CAN BE VERY SLOW)";
public static final String ALL_STANDARD_IMAGES = "- Only images from STANDARD sets";
public static final String ALL_TOKENS = "- Only token images from selected source";
private JDialog dialog;
private final JProgressBar bar;
@ -76,14 +78,15 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
private Proxy p = Proxy.NO_PROXY;
enum DownloadSources {
WIZARDS("wizards.com", WizardCardsImageSource.instance),
MYTHICSPOILER("mythicspoiler.com", MythicspoilerComSource.instance),
TOKENS("tokens.mtg.onl", TokensMtgImageSource.instance),
WIZARDS("1. wizards.com - low quality CARDS, multi-language, can be SLOW", WizardCardsImageSource.instance),
TOKENS("2. tokens.mtg.onl - high quality TOKENS", TokensMtgImageSource.instance),
SCRYFALL("3. scryfall.com - high quality CARDS, multi-language", ScryfallImageSource.instance),
MAGIDEX("4. magidex.com - high quality CARDS", MagidexImageSource.instance),
GRAB_BAG("5. GrabBag - STAR WARS cards and tokens", GrabbagImageSource.instance),
MYTHICSPOILER("6. mythicspoiler.com", MythicspoilerComSource.instance),
ALTERNATIVE("7. alternative.mtg.onl", AltMtgOnlTokensImageSource.instance),
COPYPASTE("8. Copy and Paste Image URLs", CopyPasteImageSource.instance);
// MTG_ONL("mtg.onl", MtgOnlTokensImageSource.instance), Not working correctly yet
ALTERNATIVE("alternative.mtg.onl", AltMtgOnlTokensImageSource.instance),
GRAB_BAG("GrabBag", GrabbagImageSource.instance),
MAGIDEX("magidex.com", MagidexImageSource.instance),
SCRYFALL("scryfall.com", ScryfallImageSource.instance);
// MAGICCARDS("magiccards.info", MagicCardsImageSource.instance)
private final String text;
@ -485,7 +488,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
}
try (InputStreamReader input = new InputStreamReader(in);
BufferedReader reader = new BufferedReader(input)) {
BufferedReader reader = new BufferedReader(input)) {
String line = reader.readLine();
while (line != null) {
@ -597,25 +600,25 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
logger.debug("Downloading image: " + card.getName() + " (" + card.getSet() + ')');
String url;
CardImageUrls urls;
if (ignoreUrls.contains(card.getSet()) || card.isToken()) {
if (!"0".equals(card.getCollectorId())) {
continue;
}
url = cardImageSource.generateTokenUrl(card);
urls = cardImageSource.generateTokenUrl(card);
} else {
url = cardImageSource.generateURL(card);
urls = cardImageSource.generateURL(card);
}
if (url == null) {
if (urls == null) {
String imageRef = cardImageSource.getNextHttpImageUrl();
String fileName = cardImageSource.getFileForHttpImage(imageRef);
if (imageRef != null && fileName != null) {
imageRef = cardImageSource.getSourceName() + imageRef;
try {
URL imageUrl = new URL(imageRef);
card.setToken(cardImageSource.isTokenSource());
Runnable task = new DownloadTask(card, imageUrl, fileName, cardImageSource.getTotalImages());
Runnable task = new DownloadTask(card, imageRef, fileName, cardImageSource.getTotalImages());
executor.execute(task);
} catch (Exception ex) {
}
@ -626,7 +629,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
}
}
} else {
Runnable task = new DownloadTask(card, new URL(url), cardsToDownload.size());
Runnable task = new DownloadTask(card, urls, cardsToDownload.size());
executor.execute(task);
}
@ -662,22 +665,26 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
private final class DownloadTask implements Runnable {
private final CardDownloadData card;
private final URL url;
private final CardImageUrls urls;
private final int count;
private final String actualFilename;
private final boolean useSpecifiedPaths;
DownloadTask(CardDownloadData card, URL url, int count) {
DownloadTask(CardDownloadData card, String baseUrl, int count) {
this(card, new CardImageUrls(baseUrl, null), count);
}
DownloadTask(CardDownloadData card, CardImageUrls urls, int count) {
this.card = card;
this.url = url;
this.urls = urls;
this.count = count;
this.actualFilename = "";
useSpecifiedPaths = false;
}
DownloadTask(CardDownloadData card, URL url, String actualFilename, int count) {
DownloadTask(CardDownloadData card, String baseUrl, String actualFilename, int count) {
this.card = card;
this.url = url;
this.urls = new CardImageUrls(baseUrl, null);
this.count = count;
this.actualFilename = actualFilename;
useSpecifiedPaths = true;
@ -751,14 +758,55 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
}
}
cardImageSource.doPause(url.getPath());
URLConnection httpConn = url.openConnection(p);
httpConn.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2");
httpConn.connect();
int responseCode = ((HttpURLConnection) httpConn).getResponseCode();
// can download images from many alternative urls
List<String> downloadUrls;
if (this.urls != null) {
downloadUrls = this.urls.getDownloadList();
} else {
downloadUrls = new ArrayList<>();
}
if (responseCode == 200) {
// download OK
boolean isDownloadOK = false;
URLConnection httpConn = null;
List<String> errorsList = new ArrayList<>();
for (String currentUrl : downloadUrls) {
URL url = new URL(currentUrl);
// on download cancel need to stop
if (cancel) {
return;
}
// download
cardImageSource.doPause(url.getPath());
httpConn = url.openConnection(p);
httpConn.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2");
httpConn.connect();
int responseCode = ((HttpURLConnection) httpConn).getResponseCode();
// check result
if (responseCode != 200) {
// show errors only on full fail (all urls were not work)
errorsList.add("Image download for " + card.getName()
+ (!card.getDownloadName().equals(card.getName()) ? " downloadname: " + card.getDownloadName() : "")
+ " (" + card.getSet() + ") failed - responseCode: " + responseCode + " url: " + url.toString());
if (logger.isDebugEnabled()) {
// Shows the returned html from the request to the web server
logger.debug("Returned HTML ERROR:\n" + convertStreamToString(((HttpURLConnection) httpConn).getErrorStream()));
}
// go to next try
continue;
} else {
// all fine
isDownloadOK = true;
break;
}
}
// can save result
if (isDownloadOK & httpConn != null) {
// save data to temp
OutputStream out = null;
OutputStream tfileout = null;
@ -792,8 +840,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
}
out.write(buf, 0, len);
}
}
finally {
} finally {
StreamUtils.closeQuietly(in);
StreamUtils.closeQuietly(out);
StreamUtils.closeQuietly(tfileout);
@ -815,15 +862,9 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
}
} else {
// download ERROR
logger.warn("Image download for " + card.getName()
+ (!card.getDownloadName().equals(card.getName()) ? " downloadname: " + card.getDownloadName() : "")
+ " (" + card.getSet() + ") failed - responseCode: " + responseCode + " url: " + url.toString()
);
if (logger.isDebugEnabled()) {
// Shows the returned html from the request to the web server
logger.debug("Returned HTML ERROR:\n" + convertStreamToString(((HttpURLConnection) httpConn).getErrorStream()));
// download errors
for (String err : errorsList) {
logger.warn(err);
}
}

View file

@ -4,11 +4,11 @@ import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.mage.plugins.card.dl.sources.CardImageSource;
import org.mage.plugins.card.dl.sources.CardImageUrls;
import org.mage.plugins.card.dl.sources.TokensMtgImageSource;
import org.mage.plugins.card.images.CardDownloadData;
/**
*
* @author Quercitron
*/
@Ignore
@ -18,15 +18,16 @@ public class TokensMtgImageSourceTest {
public void generateTokenUrlTest() throws Exception {
CardImageSource imageSource = TokensMtgImageSource.instance;
String url = imageSource.generateTokenUrl(new CardDownloadData("Thopter", "ORI", "0", false, 1, "ORI", ""));
Assert.assertEquals("http://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg", url);
CardImageUrls url = imageSource.generateTokenUrl(new CardDownloadData("Thopter", "ORI", "0", false, 1, "ORI", ""));
Assert.assertEquals("http://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg", url.baseUrl);
url = imageSource.generateTokenUrl(new CardDownloadData("Thopter", "ORI", "0", false, 2, "ORI", ""));
Assert.assertEquals("http://tokens.mtg.onl/tokens/ORI_011-Thopter.jpg", url);
Assert.assertEquals("http://tokens.mtg.onl/tokens/ORI_011-Thopter.jpg", url.baseUrl);
url = imageSource.generateTokenUrl(new CardDownloadData("Ashaya, the Awoken World", "ORI", "0", false, 0, "ORI", ""));
Assert.assertEquals("http://tokens.mtg.onl/tokens/ORI_007-Ashaya,-the-Awoken-World.jpg", url);
Assert.assertEquals("http://tokens.mtg.onl/tokens/ORI_007-Ashaya,-the-Awoken-World.jpg", url.baseUrl);
url = imageSource.generateTokenUrl(new CardDownloadData("Emblem Gideon, Ally of Zendikar", "BFZ", "0", false, 0, null, ""));
Assert.assertEquals("http://tokens.mtg.onl/tokens/BFZ_012-Gideon-Emblem.jpg", url);
Assert.assertEquals("http://tokens.mtg.onl/tokens/BFZ_012-Gideon-Emblem.jpg", url.baseUrl);
}
}

View file

@ -14,7 +14,7 @@ public class MageVersion implements Serializable, Comparable<MageVersion> {
public final static int MAGE_VERSION_MAJOR = 1;
public final static int MAGE_VERSION_MINOR = 4;
public final static int MAGE_VERSION_PATCH = 30;
public final static String MAGE_VERSION_MINOR_PATCH = "V5";
public final static String MAGE_VERSION_MINOR_PATCH = "V6";
public final static String MAGE_VERSION_INFO = "";
private final int major;

View file

@ -48,8 +48,8 @@ public class Standard extends Constructed {
if ((set.getSetType() == SetType.CORE
|| set.getSetType() == SetType.EXPANSION
|| set.getSetType() == SetType.SUPPLEMENTAL_STANDARD_LEGAL)
&& (!set.getReleaseDate().before(earliestDate)
&& !set.getReleaseDate().after(current.getTime()))) {
&& !set.getReleaseDate().before(earliestDate)) {
// && !set.getReleaseDate().after(current.getTime()))) {
setCodes.add(set.getCode());
}
}

View file

@ -133,6 +133,7 @@
<draftCube name="MTGO Vintage Cube November 2016" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCubeNovember2016"/>
<draftCube name="MTGO Vintage Cube June 2017" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCubeJune2017"/>
<draftCube name="MTGO Vintage Cube December 2017" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCubeDecember2017"/>
<draftCube name="MTGO Vintage Cube June 2018" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeJune2018"/>
<draftCube name="The Peasant's Toolbox" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.PeasantsToolboxCube"/>
<draftCube name="www.MTGCube.com" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.MTGCube"/>
<draftCube name="Cube From Deck" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.CubeFromDeck"/>

View file

@ -127,6 +127,7 @@
<draftCube name="MTGO Vintage Cube November 2016" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeNovember2016"/>
<draftCube name="MTGO Vintage Cube June 2017" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeJune2017"/>
<draftCube name="MTGO Vintage Cube December 2017" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeDecember2017"/>
<draftCube name="MTGO Vintage Cube June 2018" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeJune2018"/>
<draftCube name="The Peasant's Toolbox" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.PeasantsToolboxCube"/>
<draftCube name="www.MTGCube.com" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.MTGCube"/>
<draftCube name="Cube From Deck" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.CubeFromDeck"/>

View file

@ -0,0 +1,33 @@
package mage.cards.a;
import java.util.UUID;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author TheElk801
*/
public final class AegisOfTheHeavens extends CardImpl {
public AegisOfTheHeavens(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}");
// Target creature gets +1/+7 until end of turn.
this.getSpellAbility().addEffect(new BoostTargetEffect(1, 7, Duration.EndOfTurn));
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
}
public AegisOfTheHeavens(final AegisOfTheHeavens card) {
super(card);
}
@Override
public AegisOfTheHeavens copy() {
return new AegisOfTheHeavens(this);
}
}

View file

@ -0,0 +1,58 @@
package mage.cards.a;
import java.util.UUID;
import mage.constants.SubType;
import mage.target.common.TargetCreaturePermanent;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.AttachEffect;
import mage.abilities.effects.common.combat.CantBeBlockedAttachedEffect;
import mage.abilities.effects.common.continuous.BoostEnchantedEffect;
import mage.constants.Outcome;
import mage.target.TargetPermanent;
import mage.abilities.keyword.EnchantAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.AttachmentType;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Zone;
/**
*
* @author TheElk801
*/
public final class AetherTunnel extends CardImpl {
public AetherTunnel(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}");
this.subtype.add(SubType.AURA);
// Enchant creature
TargetPermanent auraTarget = new TargetCreaturePermanent();
this.getSpellAbility().addTarget(auraTarget);
this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
Ability ability = new EnchantAbility(auraTarget.getTargetName());
this.addAbility(ability);
// Enchanted creature gets +1/+0 and can't be blocked.
ability = new SimpleStaticAbility(
Zone.BATTLEFIELD, new BoostEnchantedEffect(1, 0, Duration.WhileOnBattlefield)
);
ability.addEffect(
new CantBeBlockedAttachedEffect(AttachmentType.AURA)
.setText("and can't be blocked")
);
this.addAbility(ability);
}
public AetherTunnel(final AetherTunnel card) {
super(card);
}
@Override
public AetherTunnel copy() {
return new AetherTunnel(this);
}
}

View file

@ -0,0 +1,63 @@
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfCombatTriggeredAbility;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.keyword.IndestructibleAbility;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.TargetController;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.target.common.TargetControlledCreaturePermanent;
/**
*
* @author TheElk801
*/
public final class AethershieldArtificer extends CardImpl {
private static final FilterControlledCreaturePermanent filter
= new FilterControlledCreaturePermanent("artifact creature you control");
static {
filter.add(new CardTypePredicate(CardType.ARTIFACT));
}
public AethershieldArtificer(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
this.subtype.add(SubType.DWARF);
this.subtype.add(SubType.ARTIFICER);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// At the beginning of combat on your turn, target artifact creature you control gets +2/+2 and gains indestructible until end of turn.
Ability ability = new BeginningOfCombatTriggeredAbility(
new BoostTargetEffect(2, 2, Duration.EndOfTurn)
.setText("target artifact creature you control gets +2/+2"),
TargetController.YOU, false
);
ability.addEffect(new GainAbilityTargetEffect(
IndestructibleAbility.getInstance(),
Duration.EndOfTurn
).setText("and gains indestructible until end of turn"));
ability.addTarget(new TargetControlledCreaturePermanent(filter));
this.addAbility(ability);
}
public AethershieldArtificer(final AethershieldArtificer card) {
super(card);
}
@Override
public AethershieldArtificer copy() {
return new AethershieldArtificer(this);
}
}

View file

@ -41,7 +41,7 @@ public final class AjaniAdversaryOfTyrants extends CardImpl {
this.addAbility(new PlanswalkerEntersWithLoyalityCountersAbility(4));
// +1: Put a +1/+1 counter on each of up to two target creatures.
Ability ability = new LoyaltyAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
Ability ability = new LoyaltyAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), 1);
ability.addTarget(new TargetCreaturePermanent(0, 2));
this.addAbility(ability);

View file

@ -62,7 +62,7 @@ class BurnAwayDelayedTriggeredAbility extends DelayedTriggeredAbility {
public boolean checkTrigger(GameEvent event, Game game) {
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
if (zEvent.isDiesEvent() && zEvent.getTarget() != null && zEvent.getTargetId().equals(getTargets().getFirstTarget())) {
this.getTargets().clear(); // else spell fizzels because target creature died
this.getTargets().clear(); // else spell fizzles because target creature died
Target target = new TargetPlayer();
target.add(zEvent.getTarget().getControllerId(), game);
this.addTarget(target);

View file

@ -1,4 +1,3 @@
package mage.cards.c;
import java.util.UUID;
@ -28,9 +27,9 @@ public final class CabalCoffers extends CardImpl {
}
public CabalCoffers(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.LAND},"");
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
// {2}, {tap}: Add {B} for each Swamp you control.
// {2}, {T}: Add {B} for each Swamp you control.
Ability ability = new DynamicManaAbility(Mana.BlackMana(1), new PermanentsOnBattlefieldCount(filter), new GenericManaCost(2));
ability.addCost(new TapSourceCost());
this.addAbility(ability);

View file

@ -1,6 +1,7 @@
package mage.cards.d;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.SacrificeTargetCost;
@ -35,6 +36,8 @@ public final class DarkDwellerOracle extends CardImpl {
this.subtype.add(SubType.GOBLIN);
this.subtype.add(SubType.SHAMAN);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// {1}, Sacrifice a creature: Exile the top card of your library. You may play that card this turn.
Ability ability = new SimpleActivatedAbility(

View file

@ -12,6 +12,7 @@ import mage.abilities.effects.Effect;
import mage.abilities.effects.common.AttachEffect;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
import mage.abilities.keyword.FortifyAbility;
import mage.abilities.keyword.IndestructibleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
@ -53,7 +54,7 @@ public final class DarksteelGarrison extends CardImpl {
this.addAbility(ability);
// Fortify {3}
this.addAbility(new FortifyAbility(Outcome.AddAbility, new GenericManaCost(3)));
this.addAbility(new FortifyAbility(3));
}
@ -66,49 +67,3 @@ public final class DarksteelGarrison extends CardImpl {
return new DarksteelGarrison(this);
}
}
class FortifyAbility extends ActivatedAbilityImpl {
private static final FilterControlledPermanent filter = new FilterControlledPermanent("land you control");
static {
filter.add(new CardTypePredicate(CardType.LAND));
}
public FortifyAbility(Outcome outcome, Cost cost) {
this(outcome, cost, new TargetControlledPermanent(filter));
}
public FortifyAbility(Outcome outcome, Cost cost, Target target) {
super(Zone.BATTLEFIELD, new AttachEffect(outcome, "Fortify"), cost);
this.addTarget(target);
this.timing = TimingRule.SORCERY;
}
@Override
public ActivationStatus canActivate(UUID playerId, Game game) {
ActivationStatus activationStatus = super.canActivate(playerId, game);
if (activationStatus.canActivate()) {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null && permanent.hasSubtype(SubType.FORTIFICATION, game)) {
return activationStatus;
}
}
return ActivationStatus.getFalse();
}
public FortifyAbility(final FortifyAbility ability) {
super(ability);
}
@Override
public FortifyAbility copy() {
return new FortifyAbility(this);
}
@Override
public String getRule() {
return "Fortify " + costs.getText() + manaCosts.getText() + " (" + manaCosts.getText() + ": <i>Attach to target land you control. Fortify only as a sorcery.)</i>";
}
}

View file

@ -0,0 +1,87 @@
package mage.cards.d;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.CastOnlyIfConditionIsTrueAbility;
import mage.abilities.condition.common.OnOpponentsTurnCondition;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.*;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerIsActivePlayerPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author noahg
*/
public final class Delirium extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
static {
filter.add(new ControllerIsActivePlayerPredicate());
}
public Delirium(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}{R}");
// Cast this spell only during an opponents turn.
this.addAbility(new CastOnlyIfConditionIsTrueAbility(OnOpponentsTurnCondition.instance, "Cast this spell only during an opponents turn."));
// Tap target creature that player controls. That creature deals damage equal to its power to the player. Prevent all combat damage that would be dealt to and dealt by the creature this turn.
this.getSpellAbility().addEffect(new TapTargetEffect().setText("target creature that player controls"));
this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter));
this.getSpellAbility().addEffect(new DeliriumEffect());
this.getSpellAbility().addEffect(new PreventDamageToTargetEffect(Duration.EndOfTurn, true).setText("Prevent all combat damage that would be dealt to"));
this.getSpellAbility().addEffect(new PreventDamageByTargetEffect(Duration.EndOfTurn, true).setText("and dealt by the creature this turn."));
}
public Delirium(final Delirium card) {
super(card);
}
@Override
public Delirium copy() {
return new Delirium(this);
}
}
class DeliriumEffect extends OneShotEffect {
public DeliriumEffect() {
super(Outcome.Damage);
this.staticText = "that creature deals damage equal to its power to the player";
}
public DeliriumEffect(DeliriumEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent creature = game.getPermanent(source.getFirstTarget());
if (creature != null) {
int amount = creature.getPower().getValue();
Player controller = game.getPlayer(creature.getControllerId());
if (controller != null) {
controller.damage(amount, creature.getId(), game, false, true);
return true;
}
}
return false;
}
@Override
public DeliriumEffect copy() {
return new DeliriumEffect(this);
}
}

View file

@ -20,6 +20,7 @@ import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.MageInt;
/**
*
@ -39,6 +40,9 @@ public final class DepartedDeckhand extends CardImpl {
this.subtype.add(SubType.SPIRIT);
this.subtype.add(SubType.PIRATE);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// When Departed Deckhand becomes the target of a spell, sacrifice it.
this.addAbility(new BecomesTargetTriggeredAbility(
new SacrificeSourceEffect(),

View file

@ -1,4 +1,4 @@
package mage.cards.i;
package mage.cards.d;
import java.util.UUID;
import mage.abilities.Ability;
@ -20,9 +20,9 @@ import mage.game.permanent.Permanent;
*
* @author TheElk801
*/
public final class IsolatedTower extends CardImpl {
public final class DetectionTower extends CardImpl {
public IsolatedTower(UUID ownerId, CardSetInfo setInfo) {
public DetectionTower(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
// {T}: Add {C}.
@ -30,26 +30,26 @@ public final class IsolatedTower extends CardImpl {
// {1}, {T}: Until end of turn, your opponents and creatures with hexproof they control can be the targets of spells and abilities you control as though they didn't have hexproof.
Ability ability = new SimpleActivatedAbility(
new IsolatedTowerEffect(),
new DetectionTowerEffect(),
new GenericManaCost(1)
);
ability.addCost(new TapSourceCost());
this.addAbility(ability);
}
public IsolatedTower(final IsolatedTower card) {
public DetectionTower(final DetectionTower card) {
super(card);
}
@Override
public IsolatedTower copy() {
return new IsolatedTower(this);
public DetectionTower copy() {
return new DetectionTower(this);
}
}
class IsolatedTowerEffect extends AsThoughEffectImpl {
class DetectionTowerEffect extends AsThoughEffectImpl {
public IsolatedTowerEffect() {
public DetectionTowerEffect() {
super(AsThoughEffectType.HEXPROOF, Duration.EndOfTurn, Outcome.Benefit);
staticText = "until end of turn, your opponents and "
+ "creatures with hexproof they control "
@ -57,7 +57,7 @@ class IsolatedTowerEffect extends AsThoughEffectImpl {
+ "you control as though they didn't have hexproof";
}
public IsolatedTowerEffect(final IsolatedTowerEffect effect) {
public DetectionTowerEffect(final DetectionTowerEffect effect) {
super(effect);
}
@ -67,8 +67,8 @@ class IsolatedTowerEffect extends AsThoughEffectImpl {
}
@Override
public IsolatedTowerEffect copy() {
return new IsolatedTowerEffect(this);
public DetectionTowerEffect copy() {
return new DetectionTowerEffect(this);
}
@Override

View file

@ -0,0 +1,83 @@
package mage.cards.d;
import java.util.UUID;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.CopyTargetSpellEffect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.Spell;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author TheElk801
*/
public final class Doublecast extends CardImpl {
public Doublecast(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}{R}");
// When you cast your next instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy.
this.getSpellAbility().addEffect(
new CreateDelayedTriggeredAbilityEffect(new DoublecastAbility())
.setText("When you cast your next instant or sorcery spell this turn, "
+ "copy that spell. You may choose new targets for the copy")
);
}
public Doublecast(final Doublecast card) {
super(card);
}
@Override
public Doublecast copy() {
return new Doublecast(this);
}
}
class DoublecastAbility extends DelayedTriggeredAbility {
public DoublecastAbility() {
super(new CopyTargetSpellEffect(true), Duration.EndOfTurn);
}
public DoublecastAbility(final DoublecastAbility ability) {
super(ability);
}
@Override
public DoublecastAbility copy() {
return new DoublecastAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.SPELL_CAST;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getPlayerId().equals(this.getControllerId())) {
Spell spell = game.getStack().getSpell(event.getTargetId());
if (spell != null && (spell.isInstant() || spell.isSorcery())) {
for (Effect effect : this.getEffects()) {
effect.setTargetPointer(new FixedTarget(event.getTargetId()));
}
return true;
}
}
return false;
}
@Override
public String getRule() {
return "When you cast your next instant or sorcery spell this turn, "
+ "copy that spell. You may choose new targets for the copy.";
}
}

View file

@ -1,4 +1,3 @@
package mage.cards.d;
import java.util.UUID;
@ -20,7 +19,7 @@ public final class DragonEgg extends CardImpl {
public DragonEgg(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}");
this.subtype.add(SubType.DRAGON);
this.subtype.add(SubType.DRAGON, SubType.EGG);
this.power = new MageInt(0);
this.toughness = new MageInt(2);

View file

@ -0,0 +1,86 @@
package mage.cards.d;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.ActivateIfConditionActivatedAbility;
import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
import mage.abilities.condition.Condition;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.constants.PhaseStep;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Zone;
import mage.filter.common.FilterAttackingOrBlockingCreature;
import mage.filter.predicate.permanent.ControllerControlsIslandPredicate;
import mage.game.Game;
import mage.target.common.TargetAttackingOrBlockingCreature;
/**
*
* @author noahg
*/
public final class DwarvenSeaClan extends CardImpl {
private static final FilterAttackingOrBlockingCreature filter = new FilterAttackingOrBlockingCreature();
static {
filter.add(new ControllerControlsIslandPredicate());
}
public DwarvenSeaClan(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}");
this.subtype.add(SubType.DWARF);
this.power = new MageInt(1);
this.toughness = new MageInt(1);
// {tap}: Choose target attacking or blocking creature whose controller controls an Island. Dwarven Sea Clan deals 2 damage to that creature at end of combat. Activate this ability only before the end of combat step.
Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DamageTargetEffect(2, true, "that creature")));
effect.setText("Choose target attacking or blocking creature whose controller controls an Island. Dwarven Sea Clan deals 2 damage to that creature at end of combat.");
Ability ability = new ActivateIfConditionActivatedAbility(
Zone.BATTLEFIELD,
effect,
new TapSourceCost(),
BeforeEndCombatCondition.getInstance()
);
ability.addTarget(new TargetAttackingOrBlockingCreature(1, 1, filter, false));
addAbility(ability);
}
public DwarvenSeaClan(final DwarvenSeaClan card) {
super(card);
}
@Override
public DwarvenSeaClan copy() {
return new DwarvenSeaClan(this);
}
}
class BeforeEndCombatCondition implements Condition {
private static final BeforeEndCombatCondition instance = new BeforeEndCombatCondition();
public static Condition getInstance() {
return instance;
}
@Override
public boolean apply(Game game, Ability source) {
PhaseStep phaseStep = game.getStep().getType();
if(phaseStep.getIndex() < PhaseStep.END_COMBAT.getIndex()) {
return true;
}
return false;
}
@Override
public String toString() {
return "before the end of combat step";
}
}

View file

@ -1,6 +1,7 @@
package mage.cards.f;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.DiscardsACardOpponentTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
@ -24,6 +25,8 @@ public final class FellSpecter extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}");
this.subtype.add(SubType.SPECTER);
this.power = new MageInt(1);
this.toughness = new MageInt(3);
// Flying
this.addAbility(FlyingAbility.getInstance());

View file

@ -0,0 +1,107 @@
package mage.cards.f;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetControlledCreaturePermanent;
/**
*
* @author TheElk801
*/
public final class FrayingOmnipotence extends CardImpl {
public FrayingOmnipotence(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}{B}");
// Each player loses half their life, then discards half the cards in their hand, then sacrifices half the creatures they control. Round up each time.
this.getSpellAbility().addEffect(new FrayingOmnipotenceEffect());
}
public FrayingOmnipotence(final FrayingOmnipotence card) {
super(card);
}
@Override
public FrayingOmnipotence copy() {
return new FrayingOmnipotence(this);
}
}
class FrayingOmnipotenceEffect extends OneShotEffect {
FrayingOmnipotenceEffect() {
super(Outcome.Detriment);
this.staticText = "Each player loses half their life, "
+ "then discards half the cards in their hand, "
+ "then sacrifices half the creatures they control. "
+ "Round up each time.";
}
FrayingOmnipotenceEffect(final FrayingOmnipotenceEffect effect) {
super(effect);
}
@Override
public FrayingOmnipotenceEffect copy() {
return new FrayingOmnipotenceEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return false;
}
// Each player loses half of their life,
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
Player player = game.getPlayer(playerId);
if (player == null) {
continue;
}
int lifeToLose = (int) Math.ceil(player.getLife() / 2.0);
player.loseLife(lifeToLose, game, false);
}
// then discards half of the cards in their hand,
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
Player player = game.getPlayer(playerId);
if (player == null) {
continue;
}
int cardsToDiscard = (int) Math.ceil(player.getHand().size() / 2.0);
if (cardsToDiscard > 0) {
player.discard(cardsToDiscard, false, source, game);
}
}
// then sacrifices half of the creatures they controls,
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
Player player = game.getPlayer(playerId);
if (player == null) {
continue;
}
FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent();
int creaturesToSacrifice = (int) Math.ceil(game.getBattlefield().count(filter, source.getSourceId(), player.getId(), game) / 2.0);
if (creaturesToSacrifice == 0) {
continue;
}
Target target = new TargetControlledCreaturePermanent(creaturesToSacrifice, creaturesToSacrifice, filter, true);
target.chooseTarget(Outcome.Sacrifice, playerId, source, game);
for (UUID permanentId : target.getTargets()) {
Permanent permanent = game.getPermanent(permanentId);
if (permanent != null) {
permanent.sacrifice(source.getSourceId(), game);
}
}
}
return true;
}
}

View file

@ -0,0 +1,43 @@
package mage.cards.g;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.constants.SubType;
import mage.abilities.keyword.VigilanceAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.game.permanent.token.KnightToken;
/**
*
* @author TheElk801
*/
public final class GallantCavalry extends CardImpl {
public GallantCavalry(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.KNIGHT);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// Vigilance
this.addAbility(VigilanceAbility.getInstance());
// When Gallant Cavalry enters the battlefield, create a 2/2 white Knight creature token with vigilance.
this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new KnightToken())));
}
public GallantCavalry(final GallantCavalry card) {
super(card);
}
@Override
public GallantCavalry copy() {
return new GallantCavalry(this);
}
}

View file

@ -0,0 +1,101 @@
package mage.cards.g;
import mage.MageInt;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.common.DiesTriggeredAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.common.PayLifeCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DoIfCostPaid;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import java.util.List;
import java.util.UUID;
/**
*
* @author noahg
*/
public final class GiantAlbatross extends CardImpl {
public GiantAlbatross(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}");
this.subtype.add(SubType.BIRD);
this.power = new MageInt(1);
this.toughness = new MageInt(1);
// Flying
this.addAbility(FlyingAbility.getInstance());
// When Giant Albatross dies, you may pay {1}{U}. If you do, for each creature that dealt damage to Giant Albatross this turn, destroy that creature unless its controller pays 2 life. A creature destroyed this way can't be regenerated.
Ability ability = new DiesTriggeredAbility(new DoIfCostPaid(new GiantAlbatrossEffect(), new ManaCostsImpl("{1}{U}")));
this.addAbility(ability);
}
public GiantAlbatross(final GiantAlbatross card) {
super(card);
}
@Override
public GiantAlbatross copy() {
return new GiantAlbatross(this);
}
}
class GiantAlbatrossEffect extends OneShotEffect {
public GiantAlbatrossEffect() {
super(Outcome.Detriment);
this.staticText = "for each creature that dealt damage to {this} this turn, destroy that creature unless its controller pays 2 life. A creature destroyed this way cant be regenerated";
}
public GiantAlbatrossEffect(final GiantAlbatrossEffect effect) {
super(effect);
}
@Override
public GiantAlbatrossEffect copy() {
return new GiantAlbatrossEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId());
if (sourcePermanent != null) {
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
Player player = game.getPlayer(playerId);
if (player != null) {
List<Permanent> creatures = game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, playerId, game);
Cost cost = new PayLifeCost(2);
for (Permanent creature : creatures) {
if (sourcePermanent.getDealtDamageByThisTurn().contains(new MageObjectReference(creature.getId(), game))) {
final StringBuilder sb = new StringBuilder("Pay 2 life? (Otherwise ").append(creature.getName()).append(" will be destroyed)");
if (cost.canPay(source, creature.getControllerId(), creature.getControllerId(), game) && player.chooseUse(Outcome.Benefit, sb.toString(), source, game)) {
cost.pay(source, game, creature.getControllerId(), creature.getControllerId(), true, null);
}
if (!cost.isPaid()) {
creature.destroy(source.getSourceId(), game, true);
}
}
}
}
}
}
return false;
}
}

View file

@ -0,0 +1,38 @@
package mage.cards.g;
import java.util.UUID;
import mage.MageInt;
import mage.constants.SubType;
import mage.abilities.keyword.VigilanceAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
/**
*
* @author TheElk801
*/
public final class GreenwoodSentinel extends CardImpl {
public GreenwoodSentinel(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}");
this.subtype.add(SubType.ELF);
this.subtype.add(SubType.SCOUT);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// Vigilance
this.addAbility(VigilanceAbility.getInstance());
}
public GreenwoodSentinel(final GreenwoodSentinel card) {
super(card);
}
@Override
public GreenwoodSentinel copy() {
return new GreenwoodSentinel(this);
}
}

View file

@ -0,0 +1,106 @@
package mage.cards.h;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.condition.common.IsPhaseCondition;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.decorator.ConditionalActivatedAbility;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.ExileGraveyardAllTargetPlayerEffect;
import mage.abilities.effects.common.SacrificeSourceEffect;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.constants.*;
import mage.abilities.keyword.FirstStrikeAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.turn.Phase;
import mage.target.Target;
import mage.target.TargetPlayer;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author noahg
*/
public final class HeartWolf extends CardImpl {
private final static FilterCreaturePermanent filter = new FilterCreaturePermanent();
static {
filter.add(new SubtypePredicate(SubType.DWARF));
}
public HeartWolf(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}");
this.subtype.add(SubType.WOLF);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// First strike
this.addAbility(FirstStrikeAbility.getInstance());
// {tap}: Target Dwarf creature gets +2/+0 and gains first strike until end of turn. When that creature leaves the battlefield this turn, sacrifice Heart Wolf. Activate this ability only during combat.
Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(2, 0, Duration.EndOfTurn)
.setText("Target Dwarf creature gets +2/+0"), new TapSourceCost(), new IsPhaseCondition(TurnPhase.COMBAT));
ability.addEffect(new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(),
Duration.EndOfTurn).setText("and gains first strike until end of turn"));
ability.addTarget(new TargetCreaturePermanent(filter));
ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new HeartWolfDelayedTriggeredAbility(), true));
this.addAbility(ability);
}
public HeartWolf(final HeartWolf card) {
super(card);
}
@Override
public HeartWolf copy() {
return new HeartWolf(this);
}
}
class HeartWolfDelayedTriggeredAbility extends DelayedTriggeredAbility {
public HeartWolfDelayedTriggeredAbility() {
super(new SacrificeSourceEffect(), Duration.EndOfTurn, false);
}
public HeartWolfDelayedTriggeredAbility(HeartWolfDelayedTriggeredAbility ability) {
super(ability);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
if (zEvent.getFromZone() == Zone.BATTLEFIELD && zEvent.getTarget() != null && zEvent.getTargetId().equals(getTargets().getFirstTarget())) {
this.getTargets().clear(); // else spell fizzles because target creature died
return true;
}
return false;
}
@Override
public HeartWolfDelayedTriggeredAbility copy() {
return new HeartWolfDelayedTriggeredAbility(this);
}
@Override
public String getRule() {
return "When that creature leaves the battlefield this turn, sacrifice {this}.";
}
}

View file

@ -1,35 +1,46 @@
package mage.cards.h;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.common.EndOfCombatTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DestroyAllEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.permanent.BlockedPredicate;
import mage.filter.predicate.permanent.BlockingPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.watchers.common.BlockedThisTurnWatcher;
import mage.watchers.common.WasBlockedThisTurnWatcher;
/**
* @author dustinroepsch
*/
public final class HeatStroke extends CardImpl {
private static final FilterPermanent filter = new FilterCreaturePermanent();
static {
filter.add(Predicates.or(new BlockedPredicate(), new BlockingPredicate()));
}
public HeatStroke(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}");
// At end of combat, destroy each creature that blocked or was blocked this turn.
this.addAbility(new EndOfCombatTriggeredAbility(new DestroyAllEffect(filter)
.setText("destroy each creature that blocked or was blocked this turn"), false));
Ability ability = new EndOfCombatTriggeredAbility(new HeatStrokeEffect(), false);
ability.addWatcher(new BlockedThisTurnWatcher());
ability.addWatcher(new WasBlockedThisTurnWatcher());
this.addAbility(ability);
}
public HeatStroke(final HeatStroke card) {
@ -41,3 +52,45 @@ public final class HeatStroke extends CardImpl {
return new HeatStroke(this);
}
}
class HeatStrokeEffect extends OneShotEffect {
public HeatStrokeEffect() {
super(Outcome.DestroyPermanent);
this.staticText = "destroy each creature that blocked or was blocked this turn";
}
public HeatStrokeEffect(HeatStrokeEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
BlockedThisTurnWatcher blockedWatcher = (BlockedThisTurnWatcher) game.getState().getWatchers().get(BlockedThisTurnWatcher.class.getSimpleName());
WasBlockedThisTurnWatcher wasBlockedThisTurnWatcher = (WasBlockedThisTurnWatcher) game.getState().getWatchers().get(WasBlockedThisTurnWatcher.class.getSimpleName());
Set<Permanent> inROI = new HashSet<>(game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source.getSourceId(), game));
boolean toRet = false;
Set<MageObjectReference> toDestroy = new HashSet<>();
if (blockedWatcher != null){
toDestroy.addAll(blockedWatcher.getBlockedThisTurnCreatures());
}
if (wasBlockedThisTurnWatcher != null){
toDestroy.addAll(wasBlockedThisTurnWatcher.getWasBlockedThisTurnCreatures());
}
for (MageObjectReference mor : toDestroy) {
Permanent permanent = mor.getPermanent(game);
if (permanent != null && permanent.isCreature() && inROI.contains(permanent)){
permanent.destroy(source.getSourceId(), game, false);
toRet = true;
}
}
return toRet;
}
@Override
public HeatStrokeEffect copy() {
return new HeatStrokeEffect(this);
}
}

View file

@ -4,11 +4,11 @@ import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.effects.common.GainLifeEffect;
import mage.constants.SubType;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
/**
*
@ -27,7 +27,7 @@ public final class HeraldOfFaith extends CardImpl {
this.addAbility(FlyingAbility.getInstance());
// When Herald of Faith attacks, you gain 2 life.
this.addAbility(new AttacksTriggeredAbility(new GainLifeEffect(1), false));
this.addAbility(new AttacksTriggeredAbility(new GainLifeEffect(2), false));
}
public HeraldOfFaith(final HeraldOfFaith card) {

View file

@ -0,0 +1,38 @@
package mage.cards.h;
import java.util.UUID;
import mage.MageInt;
import mage.constants.SubType;
import mage.abilities.keyword.FlashAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
/**
*
* @author TheElk801
*/
public final class HiredBlade extends CardImpl {
public HiredBlade(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.ASSASSIN);
this.power = new MageInt(3);
this.toughness = new MageInt(2);
// Flash
this.addAbility(FlashAbility.getInstance());
}
public HiredBlade(final HiredBlade card) {
super(card);
}
@Override
public HiredBlade copy() {
return new HiredBlade(this);
}
}

View file

@ -0,0 +1,78 @@
package mage.cards.i;
import java.util.UUID;
import mage.MageInt;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
import mage.abilities.condition.Condition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect;
import mage.constants.SubType;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.TargetController;
import mage.game.Game;
import mage.watchers.common.AttackedThisTurnWatcher;
import mage.watchers.common.BlockedThisTurnWatcher;
/**
*
* @author TheElk801
*/
public final class InfernoHellion extends CardImpl {
public InfernoHellion(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}");
this.subtype.add(SubType.HELLION);
this.power = new MageInt(7);
this.toughness = new MageInt(3);
// Trample
this.addAbility(TrampleAbility.getInstance());
// At the beginning of each end step, if Inferno Hellion attacked or blocked this turn, its owner shuffles it into their library.
Ability ability = new ConditionalTriggeredAbility(
new BeginningOfEndStepTriggeredAbility(
new ShuffleIntoLibrarySourceEffect(),
TargetController.ANY, false
),
InfernoHellionCondition.instance,
"At the beginning of each end step, "
+ "if {this} attacked or blocked this turn, "
+ "its owner shuffles it into their library."
);
ability.addWatcher(new AttackedThisTurnWatcher());
ability.addWatcher(new BlockedThisTurnWatcher());
this.addAbility(ability);
}
public InfernoHellion(final InfernoHellion card) {
super(card);
}
@Override
public InfernoHellion copy() {
return new InfernoHellion(this);
}
}
enum InfernoHellionCondition implements Condition {
instance;
@Override
public boolean apply(Game game, Ability source) {
AttackedThisTurnWatcher watcherAttacked = (AttackedThisTurnWatcher) game.getState().getWatchers().get(AttackedThisTurnWatcher.class.getSimpleName());
BlockedThisTurnWatcher watcherBlocked = (BlockedThisTurnWatcher) game.getState().getWatchers().get(BlockedThisTurnWatcher.class.getSimpleName());
MageObjectReference mor = new MageObjectReference(source.getSourceId(), game);
if (watcherAttacked == null || watcherBlocked == null) {
return false;
}
return watcherAttacked.getAttackedThisTurnCreatures().contains(mor)
|| watcherBlocked.getBlockedThisTurnCreatures().contains(mor);
}
}

View file

@ -0,0 +1,111 @@
package mage.cards.j;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.MageInt;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.common.EndOfCombatTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect;
import mage.abilities.effects.common.TapAllEffect;
import mage.abilities.effects.common.TapTargetEffect;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.watchers.common.BlockedAttackerWatcher;
/**
*
* @author noahg
*/
public final class JovensFerrets extends CardImpl {
public JovensFerrets(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}");
this.subtype.add(SubType.FERRET);
this.power = new MageInt(1);
this.toughness = new MageInt(1);
// Whenever Joven's Ferrets attacks, it gets +0/+2 until end of turn.
Effect boostSourceEffect = new BoostSourceEffect(0, 2, Duration.EndOfTurn);
boostSourceEffect.setText("it gets +0/+2 until end of turn");
this.addAbility(new AttacksTriggeredAbility(boostSourceEffect, false));
// At end of combat, tap all creatures that blocked Joven's Ferrets this turn. They don't untap during their controller's next untap step.
Ability eocAbility = new EndOfCombatTriggeredAbility(new JovensFerretsEffect(), false);
eocAbility.addWatcher(new BlockedAttackerWatcher());
this.addAbility(eocAbility);
}
public JovensFerrets(final JovensFerrets card) {
super(card);
}
@Override
public JovensFerrets copy() {
return new JovensFerrets(this);
}
}
class JovensFerretsEffect extends OneShotEffect {
public JovensFerretsEffect() {
super(Outcome.Benefit);
}
public JovensFerretsEffect(final JovensFerretsEffect effect) {
super(effect);
}
@Override
public JovensFerretsEffect copy() {
return new JovensFerretsEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId());
if (controller != null && sourcePermanent != null) {
BlockedAttackerWatcher watcher = (BlockedAttackerWatcher) game.getState().getWatchers().get(BlockedAttackerWatcher.class.getSimpleName());
if (watcher != null) {
List<Permanent> toTap = new ArrayList<>();
for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source.getSourceId(), game)) {
if (!creature.getId().equals(source.getSourceId())) {
if (watcher.creatureHasBlockedAttacker(sourcePermanent, creature, game)) {
toTap.add(creature);
}
}
}
for (Permanent creature : toTap) {
creature.tap(game);
DontUntapInControllersNextUntapStepTargetEffect effect = new DontUntapInControllersNextUntapStepTargetEffect();
effect.setTargetPointer(new FixedTarget(creature.getId()));
game.addEffect(effect, source);
}
return true;
}
}
return false;
}
@Override
public String getText(Mode mode) {
return "tap all creatures that blocked {this} this turn. They don't untap during their controller's next untap step.";
}
}

View file

@ -0,0 +1,138 @@
package mage.cards.m;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.ProtectionAbility;
import mage.cards.Card;
import mage.constants.*;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetCardInGraveyard;
import mage.util.CardUtil;
/**
*
* @author noahg
*/
public final class MirrorGolem extends CardImpl {
public MirrorGolem(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{6}");
this.subtype.add(SubType.GOLEM);
this.power = new MageInt(3);
this.toughness = new MageInt(4);
// Imprint - When Mirror Golem enters the battlefield, you may exile target card from a graveyard.
Ability ability = new EntersBattlefieldTriggeredAbility(new MirrorGolemImprintEffect(), true, "Imprint &mdash; ");
ability.addTarget(new TargetCardInGraveyard());
this.addAbility(ability);
// Mirror Golem has protection from each of the exiled card's card types.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MirrorGolemEffect()));
}
public MirrorGolem(final MirrorGolem card) {
super(card);
}
@Override
public MirrorGolem copy() {
return new MirrorGolem(this);
}
}
class MirrorGolemImprintEffect extends OneShotEffect {
MirrorGolemImprintEffect() {
super(Outcome.Exile);
this.staticText = "you may exile target card from a graveyard";
}
MirrorGolemImprintEffect(final MirrorGolemImprintEffect effect) {
super(effect);
}
@Override
public MirrorGolemImprintEffect copy() {
return new MirrorGolemImprintEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
Card card = game.getCard(this.getTargetPointer().getFirst(game, source));
if (card != null) {
controller.moveCardsToExile(card, source, game, true, CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), source.getSourceObject(game).getIdName());
if (sourcePermanent != null) {
sourcePermanent.imprint(this.getTargetPointer().getFirst(game, source), game);
}
}
return true;
}
return false;
}
}
class MirrorGolemEffect extends ContinuousEffectImpl {
public MirrorGolemEffect() {
super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
this.addDependedToType(DependencyType.AddingAbility);
staticText = "{this} has protection from each of the exiled card's card types.";
}
public MirrorGolemEffect(final MirrorGolemEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent sourceObject = game.getPermanent(source.getSourceId());
ExileZone exileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source.getSourceId()));
if (sourceObject == null || sourceObject.getImprinted() == null) {
return false;
}
for (UUID imprinted : sourceObject.getImprinted()){
if (imprinted != null && exileZone.contains(imprinted)){
Card card = game.getCard(imprinted);
if (card != null) {
for (CardType cardType : card.getCardType()){
FilterCard filterCard;
if (cardType.equals(CardType.SORCERY)){
filterCard = new FilterCard("sorceries");
} else if (cardType.equals(CardType.TRIBAL)){
filterCard = new FilterCard("tribal");
} else {
filterCard = new FilterCard(cardType.toString()+"s");
}
filterCard.add(new CardTypePredicate(cardType));
sourceObject.addAbility(new ProtectionAbility(filterCard));
}
}
}
}
return true;
}
@Override
public MirrorGolemEffect copy() {
return new MirrorGolemEffect(this);
}
}

View file

@ -0,0 +1,170 @@
package mage.cards.m;
import java.util.UUID;
import mage.MageObject;
import mage.MageObjectReference;
import mage.ObjectColor;
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.GenericManaCost;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.PreventionEffectImpl;
import mage.abilities.effects.common.PreventDamageBySourceEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.FilterObject;
import mage.filter.predicate.Predicate;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.ColorPredicate;
import mage.filter.predicate.mageobject.ColorlessPredicate;
import mage.filter.predicate.mageobject.SharesColorPredicate;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetSource;
import mage.target.common.TargetCardInGraveyard;
import mage.util.CardUtil;
/**
*
* @author noahg
*/
public final class MournersShield extends CardImpl {
public MournersShield(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}");
// Imprint - When Mourner's Shield enters the battlefield, you may exile target card from a graveyard.
Ability ability = new EntersBattlefieldTriggeredAbility(new MournersShieldImprintEffect(), true, "Imprint &mdash; ");
ability.addTarget(new TargetCardInGraveyard());
this.addAbility(ability);
// {2}, {tap}: Prevent all damage that would be dealt this turn by a source of your choice that shares a color with the exiled card.
Ability preventAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MournersShieldEffect(), new GenericManaCost(2));
preventAbility.addCost(new TapSourceCost());
this.addAbility(preventAbility);
}
public MournersShield(final MournersShield card) {
super(card);
}
@Override
public MournersShield copy() {
return new MournersShield(this);
}
}
class MournersShieldImprintEffect extends OneShotEffect {
MournersShieldImprintEffect() {
super(Outcome.Exile);
this.staticText = "you may exile target card from a graveyard";
}
MournersShieldImprintEffect(final MournersShieldImprintEffect effect) {
super(effect);
}
@Override
public MournersShieldImprintEffect copy() {
return new MournersShieldImprintEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Card card = game.getCard(this.getTargetPointer().getFirst(game, source));
if (card != null) {
controller.moveCardsToExile(card, source, game, true, CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), source.getSourceObject(game).getIdName());
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
if (sourcePermanent != null) {
sourcePermanent.imprint(this.getTargetPointer().getFirst(game, source), game);
}
}
return true;
}
return false;
}
}
class MournersShieldEffect extends PreventionEffectImpl {
private TargetSource target;
private MageObjectReference mageObjectReference;
private boolean noneExiled;
public MournersShieldEffect() {
super(Duration.EndOfTurn);
noneExiled = false;
this.staticText = "Prevent all damage that would be dealt this turn by a source of your choice that shares a color with the exiled card.";
}
public MournersShieldEffect(final MournersShieldEffect effect) {
super(effect);
if (effect.target != null) {
this.target = effect.target.copy();
} else {
this.target = null;
}
this.mageObjectReference = effect.mageObjectReference;
this.noneExiled = effect.noneExiled;
}
@Override
public MournersShieldEffect copy() {
return new MournersShieldEffect(this);
}
@Override
public void init(Ability source, Game game) {
ObjectColor colorsAmongImprinted = new ObjectColor();
Permanent sourceObject = game.getPermanent(source.getSourceId());
ExileZone exileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source.getSourceId()));
if (sourceObject == null || sourceObject.getImprinted() == null) {
noneExiled = true;
return;
}
for (UUID imprinted : sourceObject.getImprinted()){
if (imprinted != null && exileZone.contains(imprinted)){
Card card = game.getCard(imprinted);
if (card != null) {
colorsAmongImprinted = colorsAmongImprinted.union(card.getColor(game));
}
}
}
FilterObject filterObject = new FilterObject("a source of your choice that shares a color with the exiled card");
filterObject.add(new SharesColorPredicate(colorsAmongImprinted));
this.target = new TargetSource(filterObject);
this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), game);
if (target.getFirstTarget() != null) {
mageObjectReference = new MageObjectReference(target.getFirstTarget(), game);
} else {
mageObjectReference = null;
}
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (noneExiled || mageObjectReference == null){
return false;
}
if (super.applies(event, source, game)) {
MageObject mageObject = game.getObject(event.getSourceId());
return mageObjectReference.refersTo(mageObject, game);
}
return false;
}
}

View file

@ -0,0 +1,71 @@
package mage.cards.n;
import java.util.UUID;
import mage.MageInt;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ColoredManaCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.continuous.AddCardTypeTargetEffect;
import mage.abilities.effects.common.continuous.BecomesColorTargetEffect;
import mage.abilities.effects.common.continuous.LoseArtifactTypeTargetEffect;
import mage.abilities.effects.common.continuous.LoseCreatureTypeSourceEffect;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.target.TargetPermanent;
import mage.target.common.TargetArtifactPermanent;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author noahg
*/
public final class NeurokTransmuter extends CardImpl {
final static FilterCreaturePermanent filter = new FilterCreaturePermanent("artifact creature");
static {
filter.add(new CardTypePredicate(CardType.ARTIFACT));
}
public NeurokTransmuter(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.WIZARD);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// {U}: Target creature becomes an artifact in addition to its other types until end of turn.
Ability becomeArtifactAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCardTypeTargetEffect(Duration.EndOfTurn, CardType.ARTIFACT), new ManaCostsImpl("{U}"));
becomeArtifactAbility.addTarget(new TargetCreaturePermanent());
this.addAbility(becomeArtifactAbility);
// {U}: Until end of turn, target artifact creature becomes blue and isn't an artifact.
Effect blueEffect = new BecomesColorTargetEffect(ObjectColor.BLUE, Duration.EndOfTurn);
blueEffect.setText("Until end of turn, target artifact creature becomes blue and ");
Ability becomeBlueAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, blueEffect, new ManaCostsImpl("{U}"));
becomeBlueAbility.addTarget(new TargetCreaturePermanent(filter));
Effect loseArtifactEffect = new LoseArtifactTypeTargetEffect(Duration.EndOfTurn);
loseArtifactEffect.setText("isn't an artifact");
becomeBlueAbility.addEffect(loseArtifactEffect);
this.addAbility(becomeBlueAbility);
}
public NeurokTransmuter(final NeurokTransmuter card) {
super(card);
}
@Override
public NeurokTransmuter copy() {
return new NeurokTransmuter(this);
}
}

View file

@ -2,19 +2,20 @@ package mage.cards.n;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.OrCondition;
import mage.abilities.condition.common.EnchantedSourceCondition;
import mage.abilities.condition.common.EquippedSourceCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.constants.SubType;
import mage.abilities.keyword.DefenderAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.AsThoughEffectType;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
@ -35,18 +36,7 @@ public final class NoviceKnight extends CardImpl {
// As long as Novice Knight is enchanted or equipped, it can attack as though it didn't have defender.
this.addAbility(new SimpleStaticAbility(
Zone.BATTLEFIELD,
new ConditionalContinuousEffect(
new CanAttackAsThoughItDidntHaveDefenderSourceEffect(
Duration.WhileOnBattlefield
),
new OrCondition(
EquippedSourceCondition.instance,
new EnchantedSourceCondition()
),
"As long as {this} is enchanted or equipped, "
+ "it can attack as though it didn't have defender."
)
Zone.BATTLEFIELD, new NoviceKnightEffect()
));
}
@ -59,3 +49,45 @@ public final class NoviceKnight extends CardImpl {
return new NoviceKnight(this);
}
}
class NoviceKnightEffect extends AsThoughEffectImpl {
public NoviceKnightEffect() {
super(AsThoughEffectType.ATTACK, Duration.WhileOnBattlefield, Outcome.Benefit);
staticText = "As long as {this} is enchanted or equipped, "
+ "it can attack as though it didn't have defender.";
}
public NoviceKnightEffect(final NoviceKnightEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public NoviceKnightEffect copy() {
return new NoviceKnightEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
if (!objectId.equals(source.getSourceId())) {
return false;
}
Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId());
if (permanent != null) {
for (UUID uuid : permanent.getAttachments()) {
Permanent attached = game.getBattlefield().getPermanent(uuid);
if (attached != null
&& (attached.hasSubtype(SubType.EQUIPMENT, game)
|| attached.hasSubtype(SubType.AURA, game))) {
return true;
}
}
}
return false;
}
}

View file

@ -66,11 +66,11 @@ class NykthosShrineToNyxManaAbility extends ActivatedManaAbilityImpl {
@Override
public List<Mana> getNetMana(Game game) {
netMana.clear();
ArrayList<Mana> netManaCopy = new ArrayList<>();
if (game != null) {
netMana.addAll(((ManaEffect) this.getEffects().get(0)).getNetMana(game, this));
netManaCopy.addAll(((ManaEffect) this.getEffects().get(0)).getNetMana(game, this));
}
return netMana;
return netManaCopy;
}
}

View file

@ -6,6 +6,7 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.StaticFilters;
/**
*
@ -18,7 +19,7 @@ public final class OneWithTheMachine extends CardImpl {
// Draw cards equal to the highest converted mana cost among artifacts you control.
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(
new HighestConvertedManaCostValue()
new HighestConvertedManaCostValue(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT)
).setText("Draw cards equal to the highest converted mana cost among artifacts you control"));
}

View file

@ -10,15 +10,15 @@ import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.Condition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.HexproofAbility;
import mage.abilities.keyword.VigilanceAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.abilities.keyword.VigilanceAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.game.Game;
@ -58,7 +58,7 @@ public final class PalladiaMorsTheRuiner extends CardImpl {
PalladiaMorsTheRuinerCondition.instance,
"{this} has hexproof if it hasn't dealt damage yet"
)
));
), new PalladiaMorsTheRuinerWatcher());
}
public PalladiaMorsTheRuiner(final PalladiaMorsTheRuiner card) {
@ -79,7 +79,7 @@ enum PalladiaMorsTheRuinerCondition implements Condition {
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId());
PalladiaMorsTheRuinerWatcher watcher = (PalladiaMorsTheRuinerWatcher) game.getState().getWatchers().get(PalladiaMorsTheRuinerWatcher.class.getSimpleName());
return !watcher.getDamagers().contains(new MageObjectReference(permanent, game));
return permanent != null && !watcher.getDamagers().contains(new MageObjectReference(permanent, game));
}
@Override
@ -91,7 +91,7 @@ enum PalladiaMorsTheRuinerCondition implements Condition {
class PalladiaMorsTheRuinerWatcher extends Watcher {
private Set<MageObjectReference> damagers = new HashSet();
private final Set<MageObjectReference> damagers = new HashSet();
public PalladiaMorsTheRuinerWatcher() {
super(PalladiaMorsTheRuinerWatcher.class.getSimpleName(), WatcherScope.GAME);

View file

@ -1,4 +1,3 @@
package mage.cards.p;
import java.util.UUID;
@ -24,8 +23,7 @@ import mage.target.common.TargetControlledPermanent;
public final class Pox extends CardImpl {
public Pox(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{B}{B}{B}");
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}{B}{B}");
// Each player loses a third of their life, then discards a third of the cards in their hand, then sacrifices a third of the creatures he or she controls, then sacrifices a third of the lands he or she controls. Round up each time.
this.getSpellAbility().addEffect(new PoxEffect());
@ -42,21 +40,25 @@ public final class Pox extends CardImpl {
}
class PoxEffect extends OneShotEffect {
PoxEffect() {
super(Outcome.Detriment);
this.staticText = "Each player loses a third of their life, then discards a third of the cards in their hand, then sacrifices a third of the creatures he or she controls, then sacrifices a third of the lands he or she controls. Round up each time.";
this.staticText = "Each player loses a third of their life, "
+ "then discards a third of the cards in their hand, "
+ "then sacrifices a third of the creatures they control, "
+ "then sacrifices a third of the lands they control. "
+ "Round up each time.";
}
PoxEffect(final PoxEffect effect) {
super(effect);
}
@Override
public PoxEffect copy() {
return new PoxEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());

View file

@ -1,4 +1,3 @@
package mage.cards.p;
import java.util.UUID;
@ -72,12 +71,13 @@ class PreeminentCaptainEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
TargetCardInHand target = new TargetCardInHand(filter);
if (target.canChoose(controller.getId(), game) && target.choose(getOutcome(), controller.getId(), source.getSourceId(), game)) {
if (target.canChoose(controller.getId(), game)
&& target.choose(getOutcome(), controller.getId(), source.getSourceId(), game)) {
if (!target.getTargets().isEmpty()) {
UUID cardId = target.getFirstTarget();
Card card = controller.getHand().get(cardId, game);
if (card != null) {
if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) {
if (controller.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, true, null)) {
Permanent permanent = game.getPermanent(card.getId());
if (permanent != null) {
game.getCombat().addAttackingCreature(permanent.getId(), game);

View file

@ -0,0 +1,89 @@
package mage.cards.p;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.delayed.AtTheBeginOfNextUpkeepDelayedTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetOpponent;
/**
*
* @author noahg
*/
public final class Prophecy extends CardImpl {
public Prophecy(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{W}");
// Reveal the top card of target opponent's library. If it's a land, you gain 1 life. Then that player shuffles their library.
this.getSpellAbility().addEffect(new ProphecyEffect());
this.getSpellAbility().addTarget(new TargetOpponent());
// Draw a card at the beginning of the next turn's upkeep.
this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextUpkeepDelayedTriggeredAbility(new DrawCardSourceControllerEffect(1)), false));
}
public Prophecy(final Prophecy card) {
super(card);
}
@Override
public Prophecy copy() {
return new Prophecy(this);
}
}
class ProphecyEffect extends OneShotEffect {
public ProphecyEffect() {
super(Outcome.GainLife);
this.staticText = "Reveal the top card of target opponent's library. If it's a land, you gain 1 life. Then that player shuffles their library";
}
public ProphecyEffect(final ProphecyEffect effect) {
super(effect);
}
@Override
public ProphecyEffect copy() {
return new ProphecyEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player targetPlayer = game.getPlayer(source.getFirstTarget());
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = game.getObject(source.getSourceId());
if (sourceObject == null || targetPlayer == null || controller == null) {
return false;
}
if (targetPlayer.getLibrary().hasCards()) {
CardsImpl cards = new CardsImpl();
Card card = targetPlayer.getLibrary().getFromTop(game);
if (card == null) {
return false;
}
cards.add(card);
targetPlayer.revealCards(sourceObject.getIdName(), cards, game);
if (card.isLand()) {
controller.gainLife(1, game, source.getSourceId());
}
targetPlayer.shuffleLibrary(source, game);
}
return true;
}
}

View file

@ -1,4 +1,3 @@
package mage.cards.r;
import java.util.UUID;
@ -18,7 +17,7 @@ public final class Revitalize extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}");
// You gain 3 life.
this.getSpellAbility().addEffect(new GainLifeEffect(4));
this.getSpellAbility().addEffect(new GainLifeEffect(3));
// Draw a card.
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1));

View file

@ -66,7 +66,9 @@ class RunicArmasaurTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId());
if (stackAbility != null && stackAbility.getAbilityType() == AbilityType.ACTIVATED) {
if (stackAbility != null
&& stackAbility.getAbilityType() == AbilityType.ACTIVATED
&& game.getOpponents(this.getControllerId()).contains(stackAbility.getControllerId())) {
MageObject abilitySourceObject = stackAbility.getSourceObject(game);
return abilitySourceObject != null && (abilitySourceObject.isLand() || abilitySourceObject.isCreature());
}

View file

@ -0,0 +1,105 @@
package mage.cards.r;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect;
import mage.cards.Card;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.permanent.DefendingPlayerOwnsCardPredicate;
import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetCardInGraveyard;
/**
*
* @author TheElk801
*/
public final class RysorianBadger extends CardImpl {
private static final FilterCreatureCard filter = new FilterCreatureCard("creature cards from defending player's graveyard");
static {
filter.add(new DefendingPlayerOwnsCardPredicate());
}
public RysorianBadger(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
this.subtype.add(SubType.BADGER);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// Whenever Rysorian Badger attacks and isn't blocked, you may exile up to two target creature cards from defending player's graveyard. If you do, you gain 1 life for each card exiled this way and Rysorian Badger assigns no combat damage this turn.
Ability ability = new AttacksAndIsNotBlockedTriggeredAbility(
new RysorianBadgerEffect(), true
);
ability.addTarget(new TargetCardInGraveyard(0, 2, filter));
this.addAbility(ability);
}
public RysorianBadger(final RysorianBadger card) {
super(card);
}
@Override
public RysorianBadger copy() {
return new RysorianBadger(this);
}
}
class RysorianBadgerEffect extends OneShotEffect {
public RysorianBadgerEffect() {
super(Outcome.Benefit);
this.staticText = "you may exile up to two target creature cards "
+ "from defending player's graveyard. If you do, "
+ "you gain 1 life for each card exiled this way "
+ "and {this} assigns no combat damage this turn.";
}
public RysorianBadgerEffect(final RysorianBadgerEffect effect) {
super(effect);
}
@Override
public RysorianBadgerEffect copy() {
return new RysorianBadgerEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
Cards cardsToExile = new CardsImpl();
for (UUID cardId : this.getTargetPointer().getTargets(game, source)) {
Card card = game.getCard(cardId);
if (card != null) {
cardsToExile.add(card);
}
}
int cardsExiled = 0;
player.moveCardsToExile(cardsToExile.getCards(game), source, game, false, null, null);
for (Card card : cardsToExile.getCards(game)) {
if (game.getState().getZone(card.getId()) == Zone.EXILED) {
cardsExiled++;
}
}
player.gainLife(cardsExiled, game, source);
game.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn), source);
return true;
}
}

View file

@ -50,11 +50,11 @@ public final class SarkhanFireblood extends CardImpl {
new StaticValue(2),
new ConditionalSpellManaBuilder(filter),
false
)
), 1
));
// -7: Create four 5/5 red Dragon creature tokens with flying.
this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new DragonToken2(), 4)));
this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new DragonToken2(), 4), -7));
}
public SarkhanFireblood(final SarkhanFireblood card) {

View file

@ -2,7 +2,6 @@ package mage.cards.s;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
import mage.abilities.effects.common.DamageAllEffect;
import mage.abilities.effects.common.DamagePlayersEffect;
@ -34,7 +33,7 @@ public final class SarkhansUnsealing extends CardImpl {
static {
filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3));
filter.add(new PowerPredicate(ComparisonType.FEWER_THAN, 7));
filter2.add(new PowerPredicate(ComparisonType.MORE_THAN, 7));
filter2.add(new PowerPredicate(ComparisonType.MORE_THAN, 6));
filter3.add(Predicates.or(
new CardTypePredicate(CardType.CREATURE),
new CardTypePredicate(CardType.PLANESWALKER)
@ -46,14 +45,18 @@ public final class SarkhansUnsealing extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}");
// Whenever you cast a creature spell with power 4, 5, or 6, Sarkhan's Unsealing deals 4 damage to any target.
Ability ability = new SpellCastControllerTriggeredAbility(new DamageTargetEffect(4), filter, false);
Ability ability = new SpellCastControllerTriggeredAbility(
new DamageTargetEffect(4),
filter, false
);
ability.addTarget(new TargetAnyTarget());
this.addAbility(ability);
// Whenever you cast a creature spell with power 7 or greater, Sarkhan's Unsealing deals 4 damage to each opponent and each creature and planeswalker they control.
ability = new EntersBattlefieldTriggeredAbility(
ability = new SpellCastControllerTriggeredAbility(
new DamagePlayersEffect(4, TargetController.OPPONENT)
.setText("{this} deals 4 damage to each opponent")
.setText("{this} deals 4 damage to each opponent"),
filter2, false
);
ability.addEffect(
new DamageAllEffect(4, filter3)

View file

@ -111,6 +111,6 @@ class ShieldMareTriggeredAbility extends TriggeredAbilityImpl {
@Override
public String getRule() {
return "When {this} enters the battlefield or becomes the target"
+ " of a spell or ability and opponent controls, you gain 3 life";
+ " of a spell or ability an opponent controls, you gain 3 life";
}
}

View file

@ -47,7 +47,9 @@ public final class SkyriderPatrol extends CardImpl {
this.addAbility(new BeginningOfCombatTriggeredAbility(
new DoIfCostPaid(
new SkyriderPatrolCreateReflexiveTriggerEffect(),
new ManaCostsImpl("{G}{U}")
new ManaCostsImpl("{G}{U}"),
"Pay {G}{U} to put a +1/+1 counter on another"
+ " creature you control and give it flying?"
).setText("you may pay {G}{U}. When you do, "
+ "put a +1/+1 counter on another target creature you control, "
+ "and that creature gains flying until end of turn."),

View file

@ -1,4 +1,3 @@
package mage.cards.s;
import java.util.UUID;
@ -136,14 +135,7 @@ class SunbirdsInvocationEffect extends OneShotEffect {
}
}
}
}
while (!cards.isEmpty()) {
Card card = cards.getRandom(game);
if (card != null) {
cards.remove(card);
card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false);
}
controller.putCardsOnBottomOfLibrary(cards, game, source, false);
}
return true;
}

View file

@ -0,0 +1,143 @@
package mage.cards.s;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
import mage.abilities.effects.OneShotEffect;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.counters.Counter;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetCreaturePermanent;
import mage.target.common.TargetOpponent;
/**
*
* @author TheElk801
*/
public final class Suncleanser extends CardImpl {
public Suncleanser(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.CLERIC);
this.power = new MageInt(1);
this.toughness = new MageInt(4);
// When Suncleanser enters the battlefield, choose one
// Remove all counters from target creature. It can't have counters put on it for as long as Suncleanser remains on the battlefield.
Ability ability = new EntersBattlefieldTriggeredAbility(
new SuncleanserRemoveCountersEffect(false), false
);
ability.addEffect(new SuncleanserPreventCountersEffect(false));
ability.addTarget(new TargetCreaturePermanent());
// Target opponent loses all counters. That player can't get counters for as long as Suncleanser remains on the battlefield.
Mode mode = new Mode();
mode.getEffects().add(new SuncleanserRemoveCountersEffect(true));
mode.getEffects().add(new SuncleanserPreventCountersEffect(true));
mode.getTargets().add(new TargetOpponent());
ability.addMode(mode);
this.addAbility(ability);
}
public Suncleanser(final Suncleanser card) {
super(card);
}
@Override
public Suncleanser copy() {
return new Suncleanser(this);
}
}
class SuncleanserRemoveCountersEffect extends OneShotEffect {
public SuncleanserRemoveCountersEffect(boolean player) {
super(Outcome.Benefit);
if (player) {
staticText = "Target opponent loses all counters.";
} else {
staticText = "Remove all counters from target creature.";
}
}
public SuncleanserRemoveCountersEffect(SuncleanserRemoveCountersEffect effect) {
super(effect);
}
@Override
public SuncleanserRemoveCountersEffect copy() {
return new SuncleanserRemoveCountersEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
if (permanent != null) {
for (Counter counter : permanent.getCounters(game).copy().values()) { // copy to prevent ConcurrentModificationException
permanent.removeCounters(counter, game);
}
return true;
}
Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
if (player != null) {
for (Counter counter : player.getCounters().copy().values()) { // copy to prevent ConcurrentModificationException
player.removeCounters(counter.getName(), counter.getCount(), source, game);
}
return true;
}
return false;
}
}
class SuncleanserPreventCountersEffect extends ContinuousRuleModifyingEffectImpl {
public SuncleanserPreventCountersEffect(boolean player) {
super(Duration.WhileOnBattlefield, Outcome.Detriment);
if (player) {
staticText = "That player can't get counters for as long as {this} remains on the battlefield.";
} else {
staticText = "It can't have counters put on it for as long as {this} remains on the battlefield";
}
}
public SuncleanserPreventCountersEffect(final SuncleanserPreventCountersEffect effect) {
super(effect);
}
@Override
public SuncleanserPreventCountersEffect copy() {
return new SuncleanserPreventCountersEffect(this);
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ADD_COUNTERS;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (!source.getFirstTarget().equals(event.getTargetId())) {
return false;
}
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent == null) {
discard();
return false;
}
return true;
}
}

View file

@ -0,0 +1,69 @@
package mage.cards.t;
import java.util.UUID;
import mage.constants.SubType;
import mage.target.common.TargetCreaturePermanent;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.AttachEffect;
import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect;
import mage.abilities.effects.common.continuous.BoostEnchantedEffect;
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
import mage.constants.Outcome;
import mage.target.TargetPermanent;
import mage.abilities.keyword.EnchantAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.AttachmentType;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Zone;
/**
*
* @author TheElk801
*/
public final class TalonsOfWildwood extends CardImpl {
public TalonsOfWildwood(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}");
this.subtype.add(SubType.AURA);
// Enchant creature
TargetPermanent auraTarget = new TargetCreaturePermanent();
this.getSpellAbility().addTarget(auraTarget);
this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
Ability ability = new EnchantAbility(auraTarget.getTargetName());
this.addAbility(ability);
// Enchanted creature gets +1/+1 and has trample.
ability = new SimpleStaticAbility(
Zone.BATTLEFIELD, new BoostEnchantedEffect(1, 1, Duration.WhileOnBattlefield)
);
ability.addEffect(new GainAbilityAttachedEffect(
TrampleAbility.getInstance(),
AttachmentType.AURA
).setText("and has trample"));
this.addAbility(ability);
// {2}{G}: Return Talons of Wildwood from your graveyard to your hand.
this.addAbility(new SimpleActivatedAbility(
Zone.GRAVEYARD,
new ReturnSourceFromGraveyardToHandEffect(),
new ManaCostsImpl("{2}{G}")
));
}
public TalonsOfWildwood(final TalonsOfWildwood card) {
super(card);
}
@Override
public TalonsOfWildwood copy() {
return new TalonsOfWildwood(this);
}
}

View file

@ -1,16 +1,12 @@
package mage.cards.t;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.MageInt;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.PutCardFromHandOntoBattlefieldEffect;
import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
import mage.abilities.keyword.FlyingAbility;
@ -22,8 +18,6 @@ import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.watchers.Watcher;
/**
* @author TheElk801
@ -60,7 +54,7 @@ public final class TheUrDragon extends CardImpl {
this.addAbility(FlyingAbility.getInstance());
// Whenever one or more Dragons you control attack, draw that many cards, then you may put a permanent card from your hand onto the battlefield
this.addAbility(new TheUrDragonTriggeredAbility(), new DragonsAttackedWatcher());
this.addAbility(new TheUrDragonTriggeredAbility());
}
public TheUrDragon(final TheUrDragon card) {
@ -73,46 +67,10 @@ public final class TheUrDragon extends CardImpl {
}
}
class DragonsAttackedWatcher extends Watcher {
public final Set<MageObjectReference> attackedThisTurnCreatures = new HashSet<>();
public DragonsAttackedWatcher() {
super(DragonsAttackedWatcher.class.getSimpleName(), WatcherScope.GAME);
}
public DragonsAttackedWatcher(final DragonsAttackedWatcher watcher) {
super(watcher);
this.attackedThisTurnCreatures.addAll(watcher.attackedThisTurnCreatures);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.BEGIN_COMBAT_STEP_PRE) {
this.attackedThisTurnCreatures.clear();
}
if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) {
if (game.getPermanent(event.getSourceId()).hasSubtype(SubType.DRAGON, game)) {
this.attackedThisTurnCreatures.add(new MageObjectReference(event.getSourceId(), game));
}
}
}
public Set<MageObjectReference> getAttackedThisTurnCreatures() {
return this.attackedThisTurnCreatures;
}
@Override
public DragonsAttackedWatcher copy() {
return new DragonsAttackedWatcher(this);
}
}
class TheUrDragonTriggeredAbility extends TriggeredAbilityImpl {
public TheUrDragonTriggeredAbility() {
super(Zone.BATTLEFIELD, new TheUrDragonEffect(), false);
super(Zone.BATTLEFIELD, null, false);
}
public TheUrDragonTriggeredAbility(final TheUrDragonTriggeredAbility ability) {
@ -131,58 +89,27 @@ class TheUrDragonTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
int attackingDragons = 0;
for (UUID attacker : game.getCombat().getAttackers()) {
Permanent creature = game.getPermanent(attacker);
if (creature != null
&& creature.getControllerId() != null
&& creature.getControllerId().equals(this.getControllerId())
&& creature.hasSubtype(SubType.DRAGON, game)) {
return true;
attackingDragons++;
}
}
if (attackingDragons > 0) {
this.getEffects().clear();
addEffect(new DrawCardSourceControllerEffect(attackingDragons));
addEffect(new PutCardFromHandOntoBattlefieldEffect());
return true;
}
return false;
}
@Override
public String getRule() {
return "Whenever one or more Dragons you control attack, " + super.getRule();
}
}
class TheUrDragonEffect extends OneShotEffect {
public TheUrDragonEffect() {
super(Outcome.Benefit);
this.staticText = "draw that many cards, then you may put a permanent card from your hand onto the battlefield";
}
public TheUrDragonEffect(final TheUrDragonEffect effect) {
super(effect);
}
@Override
public TheUrDragonEffect copy() {
return new TheUrDragonEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
DragonsAttackedWatcher watcher = (DragonsAttackedWatcher) game.getState().getWatchers().get(DragonsAttackedWatcher.class.getSimpleName());
if (watcher != null) {
int attackingDragons = 0;
for (MageObjectReference attacker : watcher.getAttackedThisTurnCreatures()) {
if (attacker.getPermanentOrLKIBattlefield(game).getControllerId().equals(controller.getId())) {
attackingDragons++;
}
}
if (attackingDragons > 0) {
controller.drawCards(attackingDragons, game);
}
return new PutCardFromHandOntoBattlefieldEffect().apply(game, source);
}
}
return false;
return "Whenever one or more Dragons you control attack, draw that many cards, then you may put a permanent card from your hand onto the battlefield.";
}
}

View file

@ -0,0 +1,106 @@
package mage.cards.t;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.VariableManaCost;
import mage.abilities.dynamicvalue.common.ManacostVariableValue;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetOpponent;
import java.util.UUID;
/**
*
* @author noahg
*/
public final class ThoughtDissector extends CardImpl {
public ThoughtDissector(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}");
// {X}, {tap}: Target opponent reveals cards from the top of his or her library until an artifact card or X cards are revealed, whichever comes first. If an artifact card is revealed this way, put it onto the battlefield under your control and sacrifice Thought Dissector. Put the rest of the revealed cards into that player's graveyard.
SimpleActivatedAbility abilitiy = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ThoughtDissectorEffect(), new VariableManaCost());
abilitiy.addCost(new TapSourceCost());
abilitiy.addTarget(new TargetOpponent());
this.addAbility(abilitiy);
}
public ThoughtDissector(final ThoughtDissector card) {
super(card);
}
@Override
public ThoughtDissector copy() {
return new ThoughtDissector(this);
}
}
class ThoughtDissectorEffect extends OneShotEffect {
private static final ManacostVariableValue amount = new ManacostVariableValue();
public ThoughtDissectorEffect() {
super(Outcome.Detriment);
staticText = "Target opponent reveals cards from the top of his or her library until an artifact card or X cards are revealed, whichever comes first. If an artifact card is revealed this way, put it onto the battlefield under your control and sacrifice {this}. Put the rest of the revealed cards into that player's graveyard.";
}
public ThoughtDissectorEffect(final ThoughtDissectorEffect effect) {
super(effect);
}
@Override
public ThoughtDissectorEffect copy() {
return new ThoughtDissectorEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Player targetOpponent = game.getPlayer(targetPointer.getFirst(game, source));
int max = amount.calculate(game, source, this);
if (targetOpponent != null && controller != null && max > 0) {
int numberOfCard = 0;
Card artifact = null;
CardsImpl nonArtifacts = new CardsImpl();
CardsImpl reveal = new CardsImpl();
for (Card card : targetOpponent.getLibrary().getCards(game)) {
reveal.add(card);
if (card.isArtifact()) {
artifact = card;
break;
} else {
numberOfCard++;
if (numberOfCard > max){
break;
}
nonArtifacts.add(card);
}
}
targetOpponent.revealCards(source, reveal, game);
if (artifact != null) {
game.applyEffects();
controller.moveCards(artifact, Zone.BATTLEFIELD, source, game);
Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game);
if (sourcePermanent != null) {
sourcePermanent.sacrifice(source.getSourceId(), game);
}
}
targetOpponent.moveCards(nonArtifacts, Zone.GRAVEYARD, source, game);
return true;
}
return false;
}
}

View file

@ -0,0 +1,91 @@
package mage.cards.t;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.ActivateAsSorceryActivatedAbility;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.costs.common.RemoveCountersSourceCost;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenTargetEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.OxToken;
import mage.target.common.TargetCreaturePermanent;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author TheElk801
*/
public final class TransmogrifyingWand extends CardImpl {
public TransmogrifyingWand(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
// Transmogrifying Wand enters the battlefield with three charge counters on it.
this.addAbility(new EntersBattlefieldAbility(
new AddCountersSourceEffect(CounterType.CHARGE.createInstance(3)),
"{this} enters the battlefield with three charge counters on it"
));
// {1}, {T}, Remove a charge counter from Transmogrifying Wand: Destroy target creature. Its controller creates a 2/4 white Ox creature token. Activate this ability only any time you could cast a sorcery.
Ability ability = new ActivateAsSorceryActivatedAbility(
Zone.BATTLEFIELD,
new TransmogrifyingWandEffect(),
new GenericManaCost(1)
);
ability.addCost(new TapSourceCost());
ability.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance()));
ability.addTarget(new TargetCreaturePermanent());
this.addAbility(ability);
}
public TransmogrifyingWand(final TransmogrifyingWand card) {
super(card);
}
@Override
public TransmogrifyingWand copy() {
return new TransmogrifyingWand(this);
}
}
class TransmogrifyingWandEffect extends OneShotEffect {
public TransmogrifyingWandEffect() {
super(Outcome.Benefit);
this.staticText = "Destroy target creature. Its controller creates a 2/4 white Ox creature token.";
}
public TransmogrifyingWandEffect(final TransmogrifyingWandEffect effect) {
super(effect);
}
@Override
public TransmogrifyingWandEffect copy() {
return new TransmogrifyingWandEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent creature = game.getPermanent(source.getFirstTarget());
if (creature == null) {
return false;
}
Effect effect = new CreateTokenTargetEffect(new OxToken());
effect.setTargetPointer(new FixedTarget(creature.getControllerId(), game));
new DestroyTargetEffect().apply(game, source);
return effect.apply(game, source);
}
}

View file

@ -1,5 +1,3 @@
package mage.cards.t;
import java.util.UUID;
@ -15,9 +13,7 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.filter.StaticFilters;
import mage.target.TargetPermanent;
/**
@ -25,24 +21,26 @@ import mage.target.TargetPermanent;
* @author Loki
*/
public final class TumbleMagnet extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent("artifact or creature");
static {
filter.add(Predicates.or(
new CardTypePredicate(CardType.ARTIFACT),
new CardTypePredicate(CardType.CREATURE)));
}
public TumbleMagnet(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
public TumbleMagnet (UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}");
this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.CHARGE.createInstance(3)), "Tumble Magnet enters the battlefield with three charge counters on it"));
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TapTargetEffect(), new TapSourceCost());
this.addAbility(new EntersBattlefieldAbility(
new AddCountersSourceEffect(CounterType.CHARGE.createInstance(3)),
"{this} enters the battlefield with three charge counters on it"
));
Ability ability = new SimpleActivatedAbility(
Zone.BATTLEFIELD,
new TapTargetEffect(),
new TapSourceCost()
);
ability.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance()));
ability.addTarget(new TargetPermanent(filter));
ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_CREATURE));
this.addAbility(ability);
}
public TumbleMagnet (final TumbleMagnet card) {
public TumbleMagnet(final TumbleMagnet card) {
super(card);
}

View file

@ -23,6 +23,7 @@ import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.target.TargetPermanent;
/**
@ -79,6 +80,9 @@ class VaevictisAsmadiTheDireTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (!game.getCombat().getAttackers().contains(this.getSourceId())) {
return false;
}
this.getTargets().clear();
for (UUID playerId : game.getState().getPlayerList(this.getControllerId())) {
Player player = game.getPlayer(playerId);
@ -125,15 +129,17 @@ class VaevictisAsmadiTheDireEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
List<Player> playersToFlip = new ArrayList();
for (UUID permId : this.getTargetPointer().getTargets(game, source)) {
Permanent permanent = game.getPermanent(permId);
if (permanent == null
|| !permanent.sacrifice(source.getSourceId(), game)) {
continue;
}
Player player = game.getPlayer(permanent.getControllerId());
if (player != null) {
playersToFlip.add(player);
for (Target target : source.getTargets()) {
for (UUID permId : target.getTargets()) {
Permanent permanent = game.getPermanent(permId);
if (permanent == null
|| !permanent.sacrifice(source.getSourceId(), game)) {
continue;
}
Player player = game.getPlayer(permanent.getControllerId());
if (player != null) {
playersToFlip.add(player);
}
}
}
for (Player player : playersToFlip) {

View file

@ -0,0 +1,47 @@
package mage.cards.v;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.LoseLifeOpponentsEffect;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
/**
*
* @author TheElk801
*/
public final class VampireNeonate extends CardImpl {
public VampireNeonate(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}");
this.subtype.add(SubType.VAMPIRE);
this.power = new MageInt(0);
this.toughness = new MageInt(3);
// {2}, {T}: Each opponent loses 1 life and you gain 1 life.
Ability ability = new SimpleActivatedAbility(
new LoseLifeOpponentsEffect(1),
new GenericManaCost(2)
);
ability.addCost(new TapSourceCost());
ability.addEffect(new GainLifeEffect(1).setText("and you gain 1 life"));
this.addAbility(ability);
}
public VampireNeonate(final VampireNeonate card) {
super(card);
}
@Override
public VampireNeonate copy() {
return new VampireNeonate(this);
}
}

View file

@ -51,6 +51,7 @@ public final class VivienOfTheArkbow extends CardImpl {
ability = new LoyaltyAbility(new DamageWithPowerTargetEffect(), -3);
ability.addTarget(new TargetControlledCreaturePermanent());
ability.addTarget(new TargetCreaturePermanent(filter));
this.addAbility(ability);
// 9: Creatures you control get +4/+4 and gain trample until end of turn.
ability = new LoyaltyAbility(

View file

@ -30,7 +30,10 @@ public final class CoreSet2019 extends ExpansionSet {
cards.add(new SetCardInfo("Abnormal Endurance", 85, Rarity.COMMON, mage.cards.a.AbnormalEndurance.class));
cards.add(new SetCardInfo("Act of Treason", 127, Rarity.COMMON, mage.cards.a.ActOfTreason.class));
cards.add(new SetCardInfo("Aegis of the Heavens", 1, Rarity.UNCOMMON, mage.cards.a.AegisOfTheHeavens.class));
cards.add(new SetCardInfo("Aerial Engineer", 211, Rarity.UNCOMMON, mage.cards.a.AerialEngineer.class));
cards.add(new SetCardInfo("Aether Tunnel", 43, Rarity.UNCOMMON, mage.cards.a.AetherTunnel.class));
cards.add(new SetCardInfo("Aethershield Artificer", 2, Rarity.UNCOMMON, mage.cards.a.AethershieldArtificer.class));
cards.add(new SetCardInfo("Aggressive Mammoth", 302, Rarity.RARE, mage.cards.a.AggressiveMammoth.class));
cards.add(new SetCardInfo("Air Elemental", 308, Rarity.UNCOMMON, mage.cards.a.AirElemental.class));
cards.add(new SetCardInfo("Ajani's Influence", 282, Rarity.RARE, mage.cards.a.AjanisInfluence.class));
@ -53,8 +56,10 @@ public final class CoreSet2019 extends ExpansionSet {
cards.add(new SetCardInfo("Befuddle", 309, Rarity.COMMON, mage.cards.b.Befuddle.class));
cards.add(new SetCardInfo("Blanchwood Armor", 169, Rarity.UNCOMMON, mage.cards.b.BlanchwoodArmor.class));
cards.add(new SetCardInfo("Blood Divination", 86, Rarity.UNCOMMON, mage.cards.b.BloodDivination.class));
cards.add(new SetCardInfo("Boggart Brute", 131, Rarity.COMMON, mage.cards.b.BoggartBrute.class));
cards.add(new SetCardInfo("Bogstomper", 87, Rarity.COMMON, mage.cards.b.Bogstomper.class));
cards.add(new SetCardInfo("Bone Dragon", 88, Rarity.MYTHIC, mage.cards.b.BoneDragon.class));
cards.add(new SetCardInfo("Bone to Ash", 47, Rarity.UNCOMMON, mage.cards.b.BoneToAsh.class));
cards.add(new SetCardInfo("Brawl-Bash Ogre", 213, Rarity.UNCOMMON, mage.cards.b.BrawlBashOgre.class));
cards.add(new SetCardInfo("Bristling Boar", 170, Rarity.COMMON, mage.cards.b.BristlingBoar.class));
cards.add(new SetCardInfo("Cancel", 48, Rarity.COMMON, mage.cards.c.Cancel.class));
@ -64,11 +69,14 @@ public final class CoreSet2019 extends ExpansionSet {
cards.add(new SetCardInfo("Chaos Wand", 228, Rarity.RARE, mage.cards.c.ChaosWand.class));
cards.add(new SetCardInfo("Child of Night", 89, Rarity.COMMON, mage.cards.c.ChildOfNight.class));
cards.add(new SetCardInfo("Chromium, the Mutable", 214, Rarity.MYTHIC, mage.cards.c.ChromiumTheMutable.class));
cards.add(new SetCardInfo("Cinder Barrens", 248, Rarity.COMMON, mage.cards.c.CinderBarrens.class));
cards.add(new SetCardInfo("Cleansing Nova", 9, Rarity.RARE, mage.cards.c.CleansingNova.class));
cards.add(new SetCardInfo("Colossal Dreadmaw", 172, Rarity.COMMON, mage.cards.c.ColossalDreadmaw.class));
cards.add(new SetCardInfo("Colossal Majesty", 173, Rarity.UNCOMMON, mage.cards.c.ColossalMajesty.class));
cards.add(new SetCardInfo("Court Cleric", 283, Rarity.UNCOMMON, mage.cards.c.CourtCleric.class));
cards.add(new SetCardInfo("Crash Through", 133, Rarity.COMMON, mage.cards.c.CrashThrough.class));
cards.add(new SetCardInfo("Crucible of Worlds", 229, Rarity.MYTHIC, mage.cards.c.CrucibleOfWorlds.class));
cards.add(new SetCardInfo("Daggerback Basilisk", 174, Rarity.COMMON, mage.cards.d.DaggerbackBasilisk.class));
cards.add(new SetCardInfo("Dark-Dweller Oracle", 134, Rarity.RARE, mage.cards.d.DarkDwellerOracle.class));
cards.add(new SetCardInfo("Daybreak Chaplain", 10, Rarity.COMMON, mage.cards.d.DaybreakChaplain.class));
cards.add(new SetCardInfo("Death Baron", 90, Rarity.RARE, mage.cards.d.DeathBaron.class));
@ -77,17 +85,22 @@ public final class CoreSet2019 extends ExpansionSet {
cards.add(new SetCardInfo("Demon of Catastrophes", 91, Rarity.RARE, mage.cards.d.DemonOfCatastrophes.class));
cards.add(new SetCardInfo("Departed Deckhand", 49, Rarity.UNCOMMON, mage.cards.d.DepartedDeckhand.class));
cards.add(new SetCardInfo("Desecrated Tomb", 230, Rarity.RARE, mage.cards.d.DesecratedTomb.class));
cards.add(new SetCardInfo("Detection Tower", 249, Rarity.RARE, mage.cards.d.DetectionTower.class));
cards.add(new SetCardInfo("Diamond Mare", 231, Rarity.UNCOMMON, mage.cards.d.DiamondMare.class));
cards.add(new SetCardInfo("Diregraf Ghoul", 92, Rarity.UNCOMMON, mage.cards.d.DiregrafGhoul.class));
cards.add(new SetCardInfo("Dismissive Pyromancer", 136, Rarity.RARE, mage.cards.d.DismissivePyromancer.class));
cards.add(new SetCardInfo("Disperse", 50, Rarity.COMMON, mage.cards.d.Disperse.class));
cards.add(new SetCardInfo("Divination", 51, Rarity.COMMON, mage.cards.d.Divination.class));
cards.add(new SetCardInfo("Djinn of Wishes", 52, Rarity.RARE, mage.cards.d.DjinnOfWishes.class));
cards.add(new SetCardInfo("Doomed Dissenter", 93, Rarity.COMMON, mage.cards.d.DoomedDissenter.class));
cards.add(new SetCardInfo("Doublecast", 137, Rarity.UNCOMMON, mage.cards.d.Doublecast.class));
cards.add(new SetCardInfo("Draconic Disciple", 215, Rarity.UNCOMMON, mage.cards.d.DraconicDisciple.class));
cards.add(new SetCardInfo("Dragon Egg", 138, Rarity.UNCOMMON, mage.cards.d.DragonEgg.class));
cards.add(new SetCardInfo("Dragon's Hoard", 232, Rarity.RARE, mage.cards.d.DragonsHoard.class));
cards.add(new SetCardInfo("Druid of Horns", 176, Rarity.UNCOMMON, mage.cards.d.DruidOfHorns.class));
cards.add(new SetCardInfo("Druid of the Cowl", 177, Rarity.COMMON, mage.cards.d.DruidOfTheCowl.class));
cards.add(new SetCardInfo("Dryad Greenseeker", 178, Rarity.UNCOMMON, mage.cards.d.DryadGreenseeker.class));
cards.add(new SetCardInfo("Duress", 94, Rarity.COMMON, mage.cards.d.Duress.class));
cards.add(new SetCardInfo("Dwarven Priest", 11, Rarity.COMMON, mage.cards.d.DwarvenPriest.class));
cards.add(new SetCardInfo("Dwindle", 53, Rarity.COMMON, mage.cards.d.Dwindle.class));
cards.add(new SetCardInfo("Electrify", 139, Rarity.COMMON, mage.cards.e.Electrify.class));
@ -95,18 +108,31 @@ public final class CoreSet2019 extends ExpansionSet {
cards.add(new SetCardInfo("Elvish Rejuvenator", 180, Rarity.COMMON, mage.cards.e.ElvishRejuvenator.class));
cards.add(new SetCardInfo("Enigma Drake", 216, Rarity.UNCOMMON, mage.cards.e.EnigmaDrake.class));
cards.add(new SetCardInfo("Epicure of Blood", 95, Rarity.COMMON, mage.cards.e.EpicureOfBlood.class));
cards.add(new SetCardInfo("Essence Scatter", 54, Rarity.COMMON, mage.cards.e.EssenceScatter.class));
cards.add(new SetCardInfo("Exclusion Mage", 55, Rarity.UNCOMMON, mage.cards.e.ExclusionMage.class));
cards.add(new SetCardInfo("Explosive Apparatus", 233, Rarity.COMMON, mage.cards.e.ExplosiveApparatus.class));
cards.add(new SetCardInfo("Fell Specter", 96, Rarity.UNCOMMON, mage.cards.f.FellSpecter.class));
cards.add(new SetCardInfo("Field Creeper", 234, Rarity.COMMON, mage.cards.f.FieldCreeper.class));
cards.add(new SetCardInfo("Fiery Finish", 140, Rarity.UNCOMMON, mage.cards.f.FieryFinish.class));
cards.add(new SetCardInfo("Fire Elemental", 141, Rarity.COMMON, mage.cards.f.FireElemental.class));
cards.add(new SetCardInfo("Forest", 277, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Forest", 278, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Forest", 279, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Forest", 280, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Forsaken Sanctuary", 250, Rarity.COMMON, mage.cards.f.ForsakenSanctuary.class));
cards.add(new SetCardInfo("Foul Orchard", 251, Rarity.COMMON, mage.cards.f.FoulOrchard.class));
cards.add(new SetCardInfo("Fountain of Renewal", 235, Rarity.UNCOMMON, mage.cards.f.FountainOfRenewal.class));
cards.add(new SetCardInfo("Fraying Omnipotence", 97, Rarity.RARE, mage.cards.f.FrayingOmnipotence.class));
cards.add(new SetCardInfo("Frilled Sea Serpent", 56, Rarity.COMMON, mage.cards.f.FrilledSeaSerpent.class));
cards.add(new SetCardInfo("Gallant Cavalry", 12, Rarity.COMMON, mage.cards.g.GallantCavalry.class));
cards.add(new SetCardInfo("Gargoyle Sentinel", 236, Rarity.UNCOMMON, mage.cards.g.GargoyleSentinel.class));
cards.add(new SetCardInfo("Gearsmith Guardian", 237, Rarity.COMMON, mage.cards.g.GearsmithGuardian.class));
cards.add(new SetCardInfo("Gearsmith Prodigy", 57, Rarity.COMMON, mage.cards.g.GearsmithProdigy.class));
cards.add(new SetCardInfo("Ghastbark Twins", 181, Rarity.UNCOMMON, mage.cards.g.GhastbarkTwins.class));
cards.add(new SetCardInfo("Ghirapur Guide", 182, Rarity.UNCOMMON, mage.cards.g.GhirapurGuide.class));
cards.add(new SetCardInfo("Ghostform", 58, Rarity.COMMON, mage.cards.g.Ghostform.class));
cards.add(new SetCardInfo("Giant Spider", 183, Rarity.COMMON, mage.cards.g.GiantSpider.class));
cards.add(new SetCardInfo("Gift of Paradise", 184, Rarity.UNCOMMON, mage.cards.g.GiftOfParadise.class));
cards.add(new SetCardInfo("Gigantosaurus", 185, Rarity.RARE, mage.cards.g.Gigantosaurus.class));
cards.add(new SetCardInfo("Goblin Instigator", 142, Rarity.COMMON, mage.cards.g.GoblinInstigator.class));
cards.add(new SetCardInfo("Goblin Motivator", 143, Rarity.COMMON, mage.cards.g.GoblinMotivator.class));
@ -116,25 +142,34 @@ public final class CoreSet2019 extends ExpansionSet {
cards.add(new SetCardInfo("Gravedigger", 98, Rarity.UNCOMMON, mage.cards.g.Gravedigger.class));
cards.add(new SetCardInfo("Gravewaker", 293, Rarity.RARE, mage.cards.g.Gravewaker.class));
cards.add(new SetCardInfo("Graveyard Marshal", 99, Rarity.RARE, mage.cards.g.GraveyardMarshal.class));
cards.add(new SetCardInfo("Greenwood Sentinel", 187, Rarity.COMMON, mage.cards.g.GreenwoodSentinel.class));
cards.add(new SetCardInfo("Guttersnipe", 145, Rarity.UNCOMMON, mage.cards.g.Guttersnipe.class));
cards.add(new SetCardInfo("Havoc Devils", 146, Rarity.COMMON, mage.cards.h.HavocDevils.class));
cards.add(new SetCardInfo("Herald of Faith", 13, Rarity.UNCOMMON, mage.cards.h.HeraldOfFaith.class));
cards.add(new SetCardInfo("Heroic Reinforcements", 217, Rarity.UNCOMMON, mage.cards.h.HeroicReinforcements.class));
cards.add(new SetCardInfo("Hieromancer's Cage", 14, Rarity.UNCOMMON, mage.cards.h.HieromancersCage.class));
cards.add(new SetCardInfo("Highland Game", 188, Rarity.COMMON, mage.cards.h.HighlandGame.class));
cards.add(new SetCardInfo("Highland Lake", 252, Rarity.COMMON, mage.cards.h.HighlandLake.class));
cards.add(new SetCardInfo("Hired Blade", 100, Rarity.COMMON, mage.cards.h.HiredBlade.class));
cards.add(new SetCardInfo("Horizon Scholar", 59, Rarity.UNCOMMON, mage.cards.h.HorizonScholar.class));
cards.add(new SetCardInfo("Hostile Minotaur", 147, Rarity.COMMON, mage.cards.h.HostileMinotaur.class));
cards.add(new SetCardInfo("Hungering Hydra", 189, Rarity.RARE, mage.cards.h.HungeringHydra.class));
cards.add(new SetCardInfo("Infectious Horror", 101, Rarity.COMMON, mage.cards.i.InfectiousHorror.class));
cards.add(new SetCardInfo("Infernal Reckoning", 102, Rarity.RARE, mage.cards.i.InfernalReckoning.class));
cards.add(new SetCardInfo("Infernal Scarring", 103, Rarity.COMMON, mage.cards.i.InfernalScarring.class));
cards.add(new SetCardInfo("Inferno Hellion", 148, Rarity.UNCOMMON, mage.cards.i.InfernoHellion.class));
cards.add(new SetCardInfo("Inspired Charge", 15, Rarity.COMMON, mage.cards.i.InspiredCharge.class));
cards.add(new SetCardInfo("Invoke the Divine", 16, Rarity.COMMON, mage.cards.i.InvokeTheDivine.class));
cards.add(new SetCardInfo("Isareth the Awakener", 104, Rarity.RARE, mage.cards.i.IsarethTheAwakener.class));
cards.add(new SetCardInfo("Island", 265, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Island", 266, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Island", 267, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Island", 268, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Isolate", 17, Rarity.RARE, mage.cards.i.Isolate.class));
cards.add(new SetCardInfo("Isolated Tower", 249, Rarity.RARE, mage.cards.i.IsolatedTower.class));
cards.add(new SetCardInfo("Kargan Dragonrider", 297, Rarity.COMMON, mage.cards.k.KarganDragonrider.class));
cards.add(new SetCardInfo("Knight of the Tusk", 18, Rarity.COMMON, mage.cards.k.KnightOfTheTusk.class));
cards.add(new SetCardInfo("Knight's Pledge", 19, Rarity.COMMON, mage.cards.k.KnightsPledge.class));
cards.add(new SetCardInfo("Knightly Valor", 20, Rarity.UNCOMMON, mage.cards.k.KnightlyValor.class));
cards.add(new SetCardInfo("Lathliss, Dragon Queen", 149, Rarity.RARE, mage.cards.l.LathlissDragonQueen.class));
cards.add(new SetCardInfo("Lava Axe", 150, Rarity.COMMON, mage.cards.l.LavaAxe.class));
cards.add(new SetCardInfo("Lena, Selfless Champion", 21, Rarity.RARE, mage.cards.l.LenaSelflessChampion.class));
@ -150,18 +185,28 @@ public final class CoreSet2019 extends ExpansionSet {
cards.add(new SetCardInfo("Llanowar Elves", 314, Rarity.COMMON, mage.cards.l.LlanowarElves.class));
cards.add(new SetCardInfo("Loxodon Line Breaker", 24, Rarity.COMMON, mage.cards.l.LoxodonLineBreaker.class));
cards.add(new SetCardInfo("Luminous Bonds", 25, Rarity.COMMON, mage.cards.l.LuminousBonds.class));
cards.add(new SetCardInfo("Macabre Waltz", 108, Rarity.COMMON, mage.cards.m.MacabreWaltz.class));
cards.add(new SetCardInfo("Magistrate's Scepter", 238, Rarity.RARE, mage.cards.m.MagistratesScepter.class));
cards.add(new SetCardInfo("Make a Stand", 26, Rarity.UNCOMMON, mage.cards.m.MakeAStand.class));
cards.add(new SetCardInfo("Manalith", 239, Rarity.COMMON, mage.cards.m.Manalith.class));
cards.add(new SetCardInfo("Marauder's Axe", 240, Rarity.COMMON, mage.cards.m.MaraudersAxe.class));
cards.add(new SetCardInfo("Meandering River", 253, Rarity.COMMON, mage.cards.m.MeanderingRiver.class));
cards.add(new SetCardInfo("Mentor of the Meek", 27, Rarity.RARE, mage.cards.m.MentorOfTheMeek.class));
cards.add(new SetCardInfo("Meteor Golem", 241, Rarity.UNCOMMON, mage.cards.m.MeteorGolem.class));
cards.add(new SetCardInfo("Mighty Leap", 28, Rarity.COMMON, mage.cards.m.MightyLeap.class));
cards.add(new SetCardInfo("Militia Bugler", 29, Rarity.UNCOMMON, mage.cards.m.MilitiaBugler.class));
cards.add(new SetCardInfo("Millstone", 242, Rarity.UNCOMMON, mage.cards.m.Millstone.class));
cards.add(new SetCardInfo("Mind Rot", 109, Rarity.COMMON, mage.cards.m.MindRot.class));
cards.add(new SetCardInfo("Mirror Image", 61, Rarity.UNCOMMON, mage.cards.m.MirrorImage.class));
cards.add(new SetCardInfo("Mist-Cloaked Herald", 310, Rarity.COMMON, mage.cards.m.MistCloakedHerald.class));
cards.add(new SetCardInfo("Mistcaller", 62, Rarity.RARE, mage.cards.m.Mistcaller.class));
cards.add(new SetCardInfo("Mountain", 273, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Mountain", 274, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Mountain", 275, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Mountain", 276, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Murder", 110, Rarity.UNCOMMON, mage.cards.m.Murder.class));
cards.add(new SetCardInfo("Mystic Archaeologist", 63, Rarity.RARE, mage.cards.m.MysticArchaeologist.class));
cards.add(new SetCardInfo("Naturalize", 190, Rarity.COMMON, mage.cards.n.Naturalize.class));
cards.add(new SetCardInfo("Nexus of Fate", 306, Rarity.MYTHIC, mage.cards.n.NexusOfFate.class));
cards.add(new SetCardInfo("Nicol Bolas, the Arisen", 218, Rarity.MYTHIC, mage.cards.n.NicolBolasTheArisen.class));
cards.add(new SetCardInfo("Nicol Bolas, the Ravager", 218, Rarity.MYTHIC, mage.cards.n.NicolBolasTheRavager.class));
@ -177,9 +222,14 @@ public final class CoreSet2019 extends ExpansionSet {
cards.add(new SetCardInfo("Palladia-Mors, the Ruiner", 219, Rarity.MYTHIC, mage.cards.p.PalladiaMorsTheRuiner.class));
cards.add(new SetCardInfo("Patient Rebuilding", 67, Rarity.RARE, mage.cards.p.PatientRebuilding.class));
cards.add(new SetCardInfo("Pegasus Courser", 32, Rarity.COMMON, mage.cards.p.PegasusCourser.class));
cards.add(new SetCardInfo("Pelakka Wurm", 192, Rarity.UNCOMMON, mage.cards.p.PelakkaWurm.class));
cards.add(new SetCardInfo("Pelakka Wurm", 192, Rarity.RARE, mage.cards.p.PelakkaWurm.class));
cards.add(new SetCardInfo("Pendulum of Patterns", 288, Rarity.COMMON, mage.cards.p.PendulumOfPatterns.class));
cards.add(new SetCardInfo("Phylactery Lich", 113, Rarity.RARE, mage.cards.p.PhylacteryLich.class));
cards.add(new SetCardInfo("Plague Mare", 114, Rarity.UNCOMMON, mage.cards.p.PlagueMare.class));
cards.add(new SetCardInfo("Plains", 261, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Plains", 262, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Plains", 263, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Plains", 264, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Plummet", 193, Rarity.COMMON, mage.cards.p.Plummet.class));
cards.add(new SetCardInfo("Poison-Tip Archer", 220, Rarity.UNCOMMON, mage.cards.p.PoisonTipArcher.class));
cards.add(new SetCardInfo("Prodigious Growth", 194, Rarity.RARE, mage.cards.p.ProdigiousGrowth.class));
@ -236,15 +286,24 @@ public final class CoreSet2019 extends ExpansionSet {
cards.add(new SetCardInfo("Spit Flame", 160, Rarity.RARE, mage.cards.s.SpitFlame.class));
cards.add(new SetCardInfo("Star-Crowned Stag", 38, Rarity.COMMON, mage.cards.s.StarCrownedStag.class));
cards.add(new SetCardInfo("Stitcher's Supplier", 121, Rarity.UNCOMMON, mage.cards.s.StitchersSupplier.class));
cards.add(new SetCardInfo("Stone Quarry", 256, Rarity.COMMON, mage.cards.s.StoneQuarry.class));
cards.add(new SetCardInfo("Strangling Spores", 122, Rarity.COMMON, mage.cards.s.StranglingSpores.class));
cards.add(new SetCardInfo("Submerged Boneyard", 257, Rarity.COMMON, mage.cards.s.SubmergedBoneyard.class));
cards.add(new SetCardInfo("Sun Sentinel", 307, Rarity.COMMON, mage.cards.s.SunSentinel.class));
cards.add(new SetCardInfo("Suncleanser", 39, Rarity.RARE, mage.cards.s.Suncleanser.class));
cards.add(new SetCardInfo("Supreme Phantom", 76, Rarity.RARE, mage.cards.s.SupremePhantom.class));
cards.add(new SetCardInfo("Sure Strike", 161, Rarity.COMMON, mage.cards.s.SureStrike.class));
cards.add(new SetCardInfo("Surge Mare", 77, Rarity.UNCOMMON, mage.cards.s.SurgeMare.class));
cards.add(new SetCardInfo("Suspicious Bookcase", 246, Rarity.UNCOMMON, mage.cards.s.SuspiciousBookcase.class));
cards.add(new SetCardInfo("Swamp", 269, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Swamp", 270, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Swamp", 271, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Swamp", 272, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Switcheroo", 78, Rarity.UNCOMMON, mage.cards.s.Switcheroo.class));
cards.add(new SetCardInfo("Take Vengeance", 40, Rarity.COMMON, mage.cards.t.TakeVengeance.class));
cards.add(new SetCardInfo("Talons of Wildwood", 202, Rarity.COMMON, mage.cards.t.TalonsOfWildwood.class));
cards.add(new SetCardInfo("Tattered Mummy", 295, Rarity.COMMON, mage.cards.t.TatteredMummy.class));
cards.add(new SetCardInfo("Tectonic Rift", 162, Rarity.UNCOMMON, mage.cards.t.TectonicRift.class));
cards.add(new SetCardInfo("Tezzeret's Gatebreaker", 289, Rarity.RARE, mage.cards.t.TezzeretsGatebreaker.class));
cards.add(new SetCardInfo("Tezzeret's Strider", 290, Rarity.UNCOMMON, mage.cards.t.TezzeretsStrider.class));
cards.add(new SetCardInfo("Tezzeret, Artifice Master", 79, Rarity.MYTHIC, mage.cards.t.TezzeretArtificeMaster.class));
@ -257,6 +316,8 @@ public final class CoreSet2019 extends ExpansionSet {
cards.add(new SetCardInfo("Tolarian Scholar", 80, Rarity.COMMON, mage.cards.t.TolarianScholar.class));
cards.add(new SetCardInfo("Tormenting Voice", 164, Rarity.COMMON, mage.cards.t.TormentingVoice.class));
cards.add(new SetCardInfo("Totally Lost", 81, Rarity.COMMON, mage.cards.t.TotallyLost.class));
cards.add(new SetCardInfo("Tranquil Expanse", 259, Rarity.COMMON, mage.cards.t.TranquilExpanse.class));
cards.add(new SetCardInfo("Transmogrifying Wand", 247, Rarity.RARE, mage.cards.t.TransmogrifyingWand.class));
cards.add(new SetCardInfo("Trumpet Blast", 165, Rarity.COMMON, mage.cards.t.TrumpetBlast.class));
cards.add(new SetCardInfo("Trusty Packbeast", 41, Rarity.COMMON, mage.cards.t.TrustyPackbeast.class));
cards.add(new SetCardInfo("Two-Headed Zombie", 123, Rarity.COMMON, mage.cards.t.TwoHeadedZombie.class));
@ -264,6 +325,7 @@ public final class CoreSet2019 extends ExpansionSet {
cards.add(new SetCardInfo("Ursine Champion", 304, Rarity.COMMON, mage.cards.u.UrsineChampion.class));
cards.add(new SetCardInfo("Vaevictis Asmadi, the Dire", 225, Rarity.MYTHIC, mage.cards.v.VaevictisAsmadiTheDire.class));
cards.add(new SetCardInfo("Valiant Knight", 42, Rarity.RARE, mage.cards.v.ValiantKnight.class));
cards.add(new SetCardInfo("Vampire Neonate", 124, Rarity.COMMON, mage.cards.v.VampireNeonate.class));
cards.add(new SetCardInfo("Vampire Sovereign", 125, Rarity.UNCOMMON, mage.cards.v.VampireSovereign.class));
cards.add(new SetCardInfo("Viashino Pyromancer", 166, Rarity.COMMON, mage.cards.v.ViashinoPyromancer.class));
cards.add(new SetCardInfo("Vigilant Baloth", 206, Rarity.UNCOMMON, mage.cards.v.VigilantBaloth.class));
@ -279,5 +341,6 @@ public final class CoreSet2019 extends ExpansionSet {
cards.add(new SetCardInfo("Wall of Vines", 210, Rarity.COMMON, mage.cards.w.WallOfVines.class));
cards.add(new SetCardInfo("Waterknot", 311, Rarity.COMMON, mage.cards.w.Waterknot.class));
cards.add(new SetCardInfo("Windreader Sphinx", 84, Rarity.RARE, mage.cards.w.WindreaderSphinx.class));
cards.add(new SetCardInfo("Woodland Stream", 260, Rarity.COMMON, mage.cards.w.WoodlandStream.class));
}
}

View file

@ -114,6 +114,7 @@ public final class Darksteel extends ExpansionSet {
cards.add(new SetCardInfo("Myr Moonvessel", 133, Rarity.COMMON, mage.cards.m.MyrMoonvessel.class));
cards.add(new SetCardInfo("Nemesis Mask", 134, Rarity.UNCOMMON, mage.cards.n.NemesisMask.class));
cards.add(new SetCardInfo("Neurok Prodigy", 26, Rarity.COMMON, mage.cards.n.NeurokProdigy.class));
cards.add(new SetCardInfo("Neurok Transmuter", 27, Rarity.UNCOMMON, mage.cards.n.NeurokTransmuter.class));
cards.add(new SetCardInfo("Nim Abomination", 49, Rarity.UNCOMMON, mage.cards.n.NimAbomination.class));
cards.add(new SetCardInfo("Nourish", 78, Rarity.COMMON, mage.cards.n.Nourish.class));
cards.add(new SetCardInfo("Oxidda Golem", 135, Rarity.COMMON, mage.cards.o.OxiddaGolem.class));
@ -169,6 +170,7 @@ public final class Darksteel extends ExpansionSet {
cards.add(new SetCardInfo("Tel-Jilad Outrider", 87, Rarity.COMMON, mage.cards.t.TelJiladOutrider.class));
cards.add(new SetCardInfo("Tel-Jilad Wolf", 88, Rarity.COMMON, mage.cards.t.TelJiladWolf.class));
cards.add(new SetCardInfo("Test of Faith", 17, Rarity.UNCOMMON, mage.cards.t.TestOfFaith.class));
cards.add(new SetCardInfo("Thought Dissector", 152, Rarity.RARE, mage.cards.t.ThoughtDissector.class));
cards.add(new SetCardInfo("Thunderstaff", 153, Rarity.UNCOMMON, mage.cards.t.Thunderstaff.class));
cards.add(new SetCardInfo("Trinisphere", 154, Rarity.RARE, mage.cards.t.Trinisphere.class));
cards.add(new SetCardInfo("Turn the Tables", 18, Rarity.RARE, mage.cards.t.TurnTheTables.class));

View file

@ -89,6 +89,7 @@ public final class Homelands extends ExpansionSet {
cards.add(new SetCardInfo("Dry Spell", "46a", Rarity.COMMON, DrySpell.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Dry Spell", "46b", Rarity.COMMON, DrySpell.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Dwarven Pony", 70, Rarity.RARE, mage.cards.d.DwarvenPony.class));
cards.add(new SetCardInfo("Dwarven Sea Clan", 71, Rarity.RARE, mage.cards.d.DwarvenSeaClan.class));
cards.add(new SetCardInfo("Dwarven Trader", "72a", Rarity.COMMON, DwarvenTrader.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Dwarven Trader", "72b", Rarity.COMMON, DwarvenTrader.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Ebony Rhino", 106, Rarity.COMMON, mage.cards.e.EbonyRhino.class));
@ -104,10 +105,13 @@ public final class Homelands extends ExpansionSet {
cards.add(new SetCardInfo("Funeral March", 48, Rarity.UNCOMMON, mage.cards.f.FuneralMarch.class));
cards.add(new SetCardInfo("Ghost Hounds", 49, Rarity.UNCOMMON, mage.cards.g.GhostHounds.class));
cards.add(new SetCardInfo("Giant Oyster", 35, Rarity.UNCOMMON, mage.cards.g.GiantOyster.class));
cards.add(new SetCardInfo("Giant Albatross", "27a", Rarity.COMMON, mage.cards.g.GiantAlbatross.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Giant Albatross", "27b", Rarity.COMMON, mage.cards.g.GiantAlbatross.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Grandmother Sengir", 50, Rarity.RARE, mage.cards.g.GrandmotherSengir.class));
cards.add(new SetCardInfo("Greater Werewolf", 51, Rarity.UNCOMMON, mage.cards.g.GreaterWerewolf.class));
cards.add(new SetCardInfo("Hazduhr the Abbot", 8, Rarity.RARE, mage.cards.h.HazduhrTheAbbot.class));
cards.add(new SetCardInfo("Headstone", 52, Rarity.COMMON, mage.cards.h.Headstone.class));
cards.add(new SetCardInfo("Heart Wolf", 75, Rarity.RARE, mage.cards.h.HeartWolf.class));
cards.add(new SetCardInfo("Hungry Mist", "88a", Rarity.COMMON, mage.cards.h.HungryMist.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Hungry Mist", "88b", Rarity.COMMON, mage.cards.h.HungryMist.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Ihsan's Shade", 53, Rarity.UNCOMMON, mage.cards.i.IhsansShade.class));
@ -115,6 +119,7 @@ public final class Homelands extends ExpansionSet {
cards.add(new SetCardInfo("Ironclaw Curse", 76, Rarity.RARE, mage.cards.i.IronclawCurse.class));
cards.add(new SetCardInfo("Jinx", 29, Rarity.COMMON, mage.cards.j.Jinx.class));
cards.add(new SetCardInfo("Joven", 77, Rarity.COMMON, mage.cards.j.Joven.class));
cards.add(new SetCardInfo("Joven's Ferrets", 89, Rarity.COMMON, mage.cards.j.JovensFerrets.class));
cards.add(new SetCardInfo("Joven's Tools", 108, Rarity.UNCOMMON, mage.cards.j.JovensTools.class));
cards.add(new SetCardInfo("Koskun Falls", 55, Rarity.RARE, mage.cards.k.KoskunFalls.class));
cards.add(new SetCardInfo("Koskun Keep", 114, Rarity.UNCOMMON, mage.cards.k.KoskunKeep.class));
@ -131,6 +136,7 @@ public final class Homelands extends ExpansionSet {
cards.add(new SetCardInfo("Mystic Decree", 34, Rarity.RARE, mage.cards.m.MysticDecree.class));
cards.add(new SetCardInfo("Narwhal", 35, Rarity.RARE, mage.cards.n.Narwhal.class));
cards.add(new SetCardInfo("Primal Order", 92, Rarity.RARE, mage.cards.p.PrimalOrder.class));
cards.add(new SetCardInfo("Prophecy", 11, Rarity.COMMON, mage.cards.p.Prophecy.class));
cards.add(new SetCardInfo("Rashka the Slayer", 12, Rarity.RARE, mage.cards.r.RashkaTheSlayer.class));
cards.add(new SetCardInfo("Reef Pirates", "36a", Rarity.COMMON, ReefPirates.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Reef Pirates", "36b", Rarity.COMMON, ReefPirates.class, NON_FULL_USE_VARIOUS));
@ -140,6 +146,7 @@ public final class Homelands extends ExpansionSet {
cards.add(new SetCardInfo("Root Spider", 94, Rarity.UNCOMMON, mage.cards.r.RootSpider.class));
cards.add(new SetCardInfo("Roots", 95, Rarity.UNCOMMON, mage.cards.r.Roots.class));
cards.add(new SetCardInfo("Roterothopter", 109, Rarity.COMMON, mage.cards.r.Roterothopter.class));
cards.add(new SetCardInfo("Rysorian Badger", 96, Rarity.RARE, mage.cards.r.RysorianBadger.class));
cards.add(new SetCardInfo("Samite Alchemist", 117, Rarity.COMMON, mage.cards.s.SamiteAlchemist.class));
cards.add(new SetCardInfo("Sea Sprite", 38, Rarity.UNCOMMON, mage.cards.s.SeaSprite.class));
cards.add(new SetCardInfo("Sengir Autocrat", 56, Rarity.UNCOMMON, mage.cards.s.SengirAutocrat.class));

View file

@ -144,6 +144,7 @@ public final class MastersEditionII extends ExpansionSet {
cards.add(new SetCardInfo("Jester's Mask", 211, Rarity.RARE, mage.cards.j.JestersMask.class));
cards.add(new SetCardInfo("Jeweled Amulet", 212, Rarity.UNCOMMON, mage.cards.j.JeweledAmulet.class));
cards.add(new SetCardInfo("Johtull Wurm", 168, Rarity.UNCOMMON, mage.cards.j.JohtullWurm.class));
cards.add(new SetCardInfo("Joven's Ferrets", 169, Rarity.UNCOMMON, mage.cards.j.JovensFerrets.class));
cards.add(new SetCardInfo("Juniper Order Advocate", 20, Rarity.UNCOMMON, mage.cards.j.JuniperOrderAdvocate.class));
cards.add(new SetCardInfo("Karplusan Giant", 133, Rarity.UNCOMMON, mage.cards.k.KarplusanGiant.class));
cards.add(new SetCardInfo("Kaysa", 170, Rarity.RARE, mage.cards.k.Kaysa.class));

View file

@ -1,6 +1,7 @@
package mage.sets;
import mage.cards.ExpansionSet;
import mage.cards.d.Delirium;
import mage.constants.Rarity;
import mage.constants.SetType;
@ -83,6 +84,7 @@ public final class Mirage extends ExpansionSet {
cards.add(new SetCardInfo("Dark Banishing", 115, Rarity.COMMON, mage.cards.d.DarkBanishing.class));
cards.add(new SetCardInfo("Dark Ritual", 116, Rarity.COMMON, mage.cards.d.DarkRitual.class));
cards.add(new SetCardInfo("Dazzling Beauty", 8, Rarity.COMMON, mage.cards.d.DazzlingBeauty.class));
cards.add(new SetCardInfo("Delirium", 260, Rarity.UNCOMMON, Delirium.class));
cards.add(new SetCardInfo("Dirtwater Wraith", 117, Rarity.COMMON, mage.cards.d.DirtwaterWraith.class));
cards.add(new SetCardInfo("Disempower", 9, Rarity.COMMON, mage.cards.d.Disempower.class));
cards.add(new SetCardInfo("Disenchant", 10, Rarity.COMMON, mage.cards.d.Disenchant.class));

View file

@ -169,6 +169,7 @@ public final class Mirrodin extends ExpansionSet {
cards.add(new SetCardInfo("Mind's Eye", 205, Rarity.RARE, mage.cards.m.MindsEye.class));
cards.add(new SetCardInfo("Mindslaver", 206, Rarity.RARE, mage.cards.m.Mindslaver.class));
cards.add(new SetCardInfo("Mindstorm Crown", 207, Rarity.UNCOMMON, mage.cards.m.MindstormCrown.class));
cards.add(new SetCardInfo("Mirror Golem", 208, Rarity.UNCOMMON, mage.cards.m.MirrorGolem.class));
cards.add(new SetCardInfo("Molder Slug", 125, Rarity.RARE, mage.cards.m.MolderSlug.class));
cards.add(new SetCardInfo("Molten Rain", 101, Rarity.COMMON, mage.cards.m.MoltenRain.class));
cards.add(new SetCardInfo("Moriok Scavenger", 68, Rarity.COMMON, mage.cards.m.MoriokScavenger.class));
@ -176,6 +177,7 @@ public final class Mirrodin extends ExpansionSet {
cards.add(new SetCardInfo("Mountain", 300, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Mountain", 301, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Mountain", 302, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Mourner's Shield", 209, Rarity.UNCOMMON, mage.cards.m.MournersShield.class));
cards.add(new SetCardInfo("Myr Adapter", 210, Rarity.COMMON, mage.cards.m.MyrAdapter.class));
cards.add(new SetCardInfo("Myr Enforcer", 211, Rarity.COMMON, mage.cards.m.MyrEnforcer.class));
cards.add(new SetCardInfo("Myr Incubator", 212, Rarity.RARE, mage.cards.m.MyrIncubator.class));

View file

@ -454,4 +454,103 @@ public class ManaOptionsTest extends CardTestPlayerBase {
Assert.assertEquals("mana variations don't fit", 1, manaOptions.size());
assertManaOptions("{C}{C}", manaOptions);
}
@Test
public void testManaSourcesWithCosts() {
// {T}: Add {C} to your mana pool.
// {5}, {T}: Add {W}{U}{B}{R}{G} to your mana pool.
addCard(Zone.BATTLEFIELD, playerA, "Crystal Quarry", 1);
// {T}: Add {C} to your mana pool.
// {W/B}, {T}: Add {W}{W}, {W}{B}, or {B}{B} to your mana pool.
addCard(Zone.BATTLEFIELD, playerA, "Fetid Heath", 3);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
setStopAt(1, PhaseStep.UPKEEP);
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 16, manaOptions.size());
assertManaOptions("{C}{C}{C}{C}{W}{W}{W}", manaOptions);
assertManaOptions("{C}{C}{C}{W}{W}{W}{W}", manaOptions);
assertManaOptions("{C}{C}{C}{W}{W}{W}{B}", manaOptions);
assertManaOptions("{C}{C}{C}{W}{W}{B}{B}", manaOptions);
assertManaOptions("{C}{C}{W}{W}{W}{W}{W}", manaOptions);
assertManaOptions("{C}{C}{W}{W}{W}{W}{B}", manaOptions);
assertManaOptions("{C}{C}{W}{W}{W}{B}{B}", manaOptions);
assertManaOptions("{C}{C}{W}{W}{B}{B}{B}", manaOptions);
assertManaOptions("{C}{C}{W}{B}{B}{B}{B}", manaOptions);
assertManaOptions("{C}{W}{W}{W}{W}{W}{W}", manaOptions);
assertManaOptions("{C}{W}{W}{W}{W}{W}{B}", manaOptions);
assertManaOptions("{C}{W}{W}{W}{W}{B}{B}", manaOptions);
assertManaOptions("{C}{W}{W}{W}{B}{B}{B}", manaOptions);
assertManaOptions("{C}{W}{W}{B}{B}{B}{B}", manaOptions);
assertManaOptions("{C}{W}{B}{B}{B}{B}{B}", manaOptions);
assertManaOptions("{C}{B}{B}{B}{B}{B}{B}", manaOptions);
}
@Test
public void testSungrassPrairie() {
// {1}, {T}: Add {G}{W}.
addCard(Zone.BATTLEFIELD, playerA, "Sungrass Prairie", 1);
// {T}: Add one mana of any color to your mana pool.
addCard(Zone.BATTLEFIELD, playerA, "Alloy Myr", 2);
setStopAt(3, PhaseStep.PRECOMBAT_MAIN);
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 2, manaOptions.size());
assertManaOptions("{W}{G}{Any}", manaOptions);
assertManaOptions("{Any}{Any}", manaOptions);
}
@Test
public void testSungrassPrairie2() {
// {1}, {T}: Add {G}{W}.
addCard(Zone.BATTLEFIELD, playerA, "Sungrass Prairie", 5);
// ({T}: Add {U} or {W} to your mana pool.)
addCard(Zone.BATTLEFIELD, playerA, "Tundra", 9);
// ({T}: Add {G} or {U} to your mana pool.)
addCard(Zone.BATTLEFIELD, playerA, "Tropical Island", 3);
setStopAt(3, PhaseStep.PRECOMBAT_MAIN);
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 88, manaOptions.size());
assertManaOptions("{W}{W}{W}{W}{W}{W}{W}{W}{W}{G}{G}{G}{G}{G}{G}{G}{G}", manaOptions);
assertManaOptions("{W}{W}{W}{W}{W}{W}{W}{W}{U}{G}{G}{G}{G}{G}{G}{G}{G}", manaOptions);
}
@Test
public void testSungrassPrairie3() {
// {1}, {T}: Add {G}{W}.
addCard(Zone.BATTLEFIELD, playerA, "Sungrass Prairie", 1);
// ({T}: Add {U} or {W} to your mana pool.)
addCard(Zone.BATTLEFIELD, playerA, "Tundra", 1);
// ({T}: Add {G} or {U} to your mana pool.)
addCard(Zone.BATTLEFIELD, playerA, "Tropical Island", 1);
setStopAt(3, PhaseStep.PRECOMBAT_MAIN);
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 4, manaOptions.size());
assertManaOptions("{U}{U}", manaOptions);
assertManaOptions("{W}{G}{G}", manaOptions);
assertManaOptions("{W}{U}{G}", manaOptions);
assertManaOptions("{W}{W}{G}", manaOptions);
}
}

View file

@ -72,6 +72,7 @@ public class VerifyCardDataTest {
// subtype
skipListCreate("SUBTYPE");
skipListAddName("SUBTYPE", "Dragon Egg"); // uncomment when MTGJSON is updated with M19
// number
skipListCreate("NUMBER");

View file

@ -1,4 +1,3 @@
package mage;
import java.io.Serializable;
@ -24,6 +23,11 @@ public class MageObjectReference implements Comparable<MageObjectReference>, Ser
private final int zoneChangeCounter;
public MageObjectReference(MageObject mageObject, Game game) {
if (mageObject == null) {
this.sourceId = null;
this.zoneChangeCounter = -1;
return;
}
this.sourceId = mageObject.getId();
this.zoneChangeCounter = mageObject.getZoneChangeCounter(game);
}

View file

@ -1051,7 +1051,8 @@ public class Mana implements Comparable<Mana>, Serializable, Copyable<Mana> {
/**
* Returns if this {@link Mana} object has more than or equal values of mana
* as the passed in {@link Mana} object.
* as the passed in {@link Mana} object. Ignores {Any} mana to prevent
* endless iterations.
*
* @param mana the mana to compare with
* @return if this object has more than or equal mana to the passed in
@ -1090,13 +1091,44 @@ public class Mana implements Comparable<Mana>, Serializable, Copyable<Mana> {
moreMana = mana1;
lessMana = mana2;
}
if (lessMana.getWhite() > moreMana.getWhite()
|| lessMana.getRed() > moreMana.getRed()
|| lessMana.getGreen() > moreMana.getGreen()
|| lessMana.getBlue() > moreMana.getBlue()
|| lessMana.getBlack() > moreMana.getBlack()
|| lessMana.getColorless() > moreMana.getColorless()
|| lessMana.getAny() > moreMana.getAny()) {
int anyDiff = mana2.getAny() - mana1.getAny();
if (lessMana.getWhite() > moreMana.getWhite()) {
anyDiff -= lessMana.getWhite() - moreMana.getWhite();
if (anyDiff < 0) {
return null;
}
}
if (lessMana.getRed() > moreMana.getRed()) {
anyDiff -= lessMana.getRed() - moreMana.getRed();
if (anyDiff < 0) {
return null;
}
}
if (lessMana.getGreen() > moreMana.getGreen()) {
anyDiff -= lessMana.getGreen() - moreMana.getGreen();
if (anyDiff < 0) {
return null;
}
}
if (lessMana.getBlue() > moreMana.getBlue()) {
anyDiff -= lessMana.getBlue() - moreMana.getBlue();
if (anyDiff < 0) {
return null;
}
}
if (lessMana.getBlack() > moreMana.getBlack()) {
anyDiff -= lessMana.getBlack() - moreMana.getBlack();
if (anyDiff < 0) {
return null;
}
}
if (lessMana.getColorless() > moreMana.getColorless()) {
anyDiff -= lessMana.getColorless() - moreMana.getColorless();
if (anyDiff < 0) {
return null;
}
}
if (lessMana.getAny() > moreMana.getAny()) {
return null;
}
return moreMana;

View file

@ -5,10 +5,10 @@ import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.*;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.TokenImpl;
import mage.game.permanent.token.Token;
public class CopyTokenEffect extends ContinuousEffectImpl {
protected Token token;
public CopyTokenEffect(Token token) {
@ -28,19 +28,19 @@ public class CopyTokenEffect extends ContinuousEffectImpl {
permanent.setName(token.getName());
permanent.getColor(game).setColor(token.getColor(game));
permanent.getCardType().clear();
for (CardType type: token.getCardType()) {
for (CardType type : token.getCardType()) {
permanent.addCardType(type);
}
permanent.getSubtype(game).clear();
for (SubType type: token.getSubtype(game)) {
for (SubType type : token.getSubtype(game)) {
permanent.getSubtype(game).add(type);
}
permanent.getSuperType().clear();
for (SuperType type: token.getSuperType()) {
for (SuperType type : token.getSuperType()) {
permanent.addSuperType(type);
}
permanent.getAbilities().clear();
for (Ability ability: token.getAbilities()) {
for (Ability ability : token.getAbilities()) {
permanent.addAbility(ability, game);
}
permanent.getPower().setValue(token.getPower().getValue());

View file

@ -0,0 +1,77 @@
package mage.abilities.effects.common.continuous;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.*;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.util.CardUtil;
import java.util.UUID;
/**
*
* @author noahg
*/
public class LoseArtifactTypeTargetEffect extends ContinuousEffectImpl{
public LoseArtifactTypeTargetEffect(Duration duration) {
super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral);
dependencyTypes.add(DependencyType.ArtifactAddingRemoving);
setText("isn't an artifact");
}
public LoseArtifactTypeTargetEffect(final LoseArtifactTypeTargetEffect effect) {
super(effect);
}
@Override
public LoseArtifactTypeTargetEffect copy() {
return new LoseArtifactTypeTargetEffect(this);
}
@Override
public void init(Ability source, Game game) {
super.init(source, game); //To change body of generated methods, choose Tools | Templates.
if (duration.isOnlyValidIfNoZoneChange()) {
// If source permanent is no longer onto battlefield discard the effect
if (source.getSourcePermanentIfItStillExists(game) == null) {
discard();
}
}
}
@Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
for (UUID targetId : targetPointer.getTargets(game, source)) {
if (targetId != null) {
Permanent permanent = game.getPermanent(targetId);
if (permanent != null) {
switch (layer) {
case TypeChangingEffects_4:
if (sublayer == SubLayer.NA) {
permanent.getCardType().remove(CardType.ARTIFACT);
permanent.getSubtype(game).removeAll(SubType.getArtifactTypes(false));
}
break;
}
return true;
}
}
}
return false;
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public boolean hasLayer(Layer layer) {
return layer == Layer.TypeChangingEffects_4;
}
}

View file

@ -35,14 +35,12 @@ public class EquipAbility extends ActivatedAbilityImpl {
@Override
public ActivationStatus canActivate(UUID playerId, Game game) {
ActivationStatus activationStatus = super.canActivate(playerId, game);
if (activationStatus.canActivate()) {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null && permanent.hasSubtype(SubType.EQUIPMENT, game)) {
return activationStatus;
}
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null && permanent.hasSubtype(SubType.EQUIPMENT, game) && !permanent.isCreature()) {
return super.canActivate(playerId, game);
} else {
return ActivationStatus.getFalse();
}
return activationStatus;
}
public EquipAbility(final EquipAbility ability) {

View file

@ -2,13 +2,22 @@
package mage.abilities.keyword;
import mage.abilities.costs.mana.GenericManaCost;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.TimingRule;
import mage.constants.Zone;
import mage.abilities.ActivatedAbilityImpl;
import mage.abilities.costs.Cost;
import mage.abilities.effects.common.AttachEffect;
import mage.filter.common.FilterControlledLandPermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.Target;
import mage.target.TargetPermanent;
import mage.target.common.TargetControlledCreaturePermanent;
import java.util.UUID;
/**
*
@ -17,10 +26,29 @@ import mage.target.TargetPermanent;
//20091005 - 702.64
public class FortifyAbility extends ActivatedAbilityImpl {
public FortifyAbility(Zone zone, AttachEffect effect, Cost cost) {
super(zone, effect, cost);
this.addTarget(new TargetPermanent(new FilterControlledLandPermanent()));
timing = TimingRule.SORCERY;
public FortifyAbility(int cost) {
this(Outcome.AddAbility, new GenericManaCost(cost));
}
public FortifyAbility(Outcome outcome, Cost cost) {
this(outcome, cost, new TargetPermanent(new FilterControlledLandPermanent()));
}
public FortifyAbility(Outcome outcome, Cost cost, Target target) {
super(Zone.BATTLEFIELD, new AttachEffect(outcome, "Fortify"), cost);
this.addTarget(target);
this.timing = TimingRule.SORCERY;
}
@Override
public ActivationStatus canActivate(UUID playerId, Game game) {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null && permanent.hasSubtype(SubType.FORTIFICATION, game) && !permanent.isCreature() && !permanent.isLand()) {
return super.canActivate(playerId, game);
} else {
return ActivationStatus.getFalse();
}
}
public FortifyAbility(final FortifyAbility ability) {
@ -31,4 +59,10 @@ public class FortifyAbility extends ActivatedAbilityImpl {
public FortifyAbility copy() {
return new FortifyAbility(this);
}
@Override
public String getRule() {
return "Fortify " + costs.getText() + manaCosts.getText() + " (" + manaCosts.getText() + ": <i>Attach to target land you control. Fortify only as a sorcery.)</i>";
}
}

View file

@ -1,6 +1,6 @@
package mage.abilities.mana;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.Mana;
@ -42,6 +42,7 @@ public class CommanderColorIdentityManaAbility extends ActivatedManaAbilityImpl
@Override
public List<Mana> getNetMana(Game game) {
List<Mana> netManas = new ArrayList<>();
if (netMana.isEmpty() && game != null) {
Player controller = game.getPlayer(getControllerId());
if (controller != null) {
@ -68,7 +69,8 @@ public class CommanderColorIdentityManaAbility extends ActivatedManaAbilityImpl
}
}
}
return netMana;
netManas.addAll(netMana);
return netManas;
}
@Override

View file

@ -33,8 +33,6 @@ public class ConditionalManaAbility extends ActivatedManaAbilityImpl {
@Override
public List<Mana> getNetMana(Game game) {
List<Mana> newNetMana = new ArrayList<>();
newNetMana.addAll(conditionalManaEffect.getNetMana(game, this));
return newNetMana;
return new ArrayList<>(conditionalManaEffect.getNetMana(game, this));
}
}

View file

@ -53,7 +53,7 @@ public class ManaOptions extends ArrayList<Mana> {
boolean hasTapCost = hasTapCost(abilities.get(0));
for (Mana netMana : netManas) {
for (Mana mana : copy) {
if (!hasTapCost || checkTappedForManaReplacement(abilities.get(0), game, netMana)) {
if (!hasTapCost /* || checkTappedForManaReplacement(abilities.get(0), game, netMana) */) { // Seems to produce endless iterations so deactivated for now: https://github.com/magefree/mage/issues/5023
Mana newMana = new Mana();
newMana.add(mana);
newMana.add(netMana);
@ -114,6 +114,7 @@ public class ManaOptions extends ArrayList<Mana> {
}
public void addManaWithCost(List<ActivatedManaAbilityImpl> abilities, Game game) {
int replaces = 0;
if (isEmpty()) {
this.add(new Mana());
}
@ -187,7 +188,7 @@ public class ManaOptions extends ArrayList<Mana> {
Mana moreValuable = Mana.getMoreValuableMana(newMana, existingMana);
if (moreValuable != null) {
existingMana.setToMana(moreValuable);
logger.trace("mana replaced " + newMana.toString() + " <=> " + existingMana.toString() + " from " + ability.getRule());
replaces++;
continue CombineWithExisting;
}
}
@ -203,6 +204,10 @@ public class ManaOptions extends ArrayList<Mana> {
}
}
}
if (this.size() > 30 || replaces > 30) {
logger.trace("ManaOptionsCosts " + this.size() + " Ign:" + replaces + " => " + this.toString());
logger.trace("Abilities: " + abilities.toString());
}
}
public void addMana(Mana addMana) {
@ -255,20 +260,28 @@ public class ManaOptions extends ArrayList<Mana> {
this.clear();
for (Mana mana : copy) {
Mana oldMan = mana.copy();
if (mana.includesMana(cost)) {
// colorless costs can be paid with different colored mana, can lead to different color combinations
if (mana.includesMana(cost)) { // it can be paid
// generic mana costs can be paid with different colored mana, can lead to different color combinations
if (cost.getGeneric() > 0 && cost.getGeneric() > (mana.getGeneric() + mana.getColorless())) {
Mana coloredCost = cost.copy();
coloredCost.setGeneric(0);
mana.subtract(coloredCost);
boolean oldManaWasReplaced = false;
for (Mana payCombination : getPossiblePayCombinations(cost.getGeneric(), mana)) {
Mana newMana = mana.copy();
newMana.subtract(payCombination);
newMana.add(addMana);
if (oldMan.contains(newMana) && oldMan.count() > newMana.count()) {
newMana.setToMana(oldMan);
Mana moreValuable = Mana.getMoreValuableMana(oldMan, newMana);
if (!oldMan.equals(moreValuable)) {
this.add(newMana);
if (moreValuable != null) {
oldManaWasReplaced = true; // the new mana includes all possibilities of the old one
}
}
this.add(newMana);
}
if (!oldManaWasReplaced) {
this.add(oldMan);
}
} else {
while (mana.includesMana(cost)) {
@ -303,28 +316,33 @@ public class ManaOptions extends ArrayList<Mana> {
existingManas.add(new Mana());
}
for (Mana existingMana : existingManas) {
Mana manaToPay = manaAvailable.copy();
manaToPay.subtract(existingMana);
if (manaToPay.getBlack() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.BlackMana(1).toString())) {
manaToPay.subtract(Mana.BlackMana(1));
Mana manaToPayFrom = manaAvailable.copy();
manaToPayFrom.subtract(existingMana);
if (manaToPayFrom.getBlack() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.BlackMana(1).toString())) {
manaToPayFrom.subtract(Mana.BlackMana(1));
addManaCombination(Mana.BlackMana(1), existingMana, payCombinations, payCombinationsStrings);
}
if (manaToPay.getBlue() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.BlueMana(1).toString())) {
manaToPay.subtract(Mana.BlueMana(1));
if (manaToPayFrom.getBlue() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.BlueMana(1).toString())) {
manaToPayFrom.subtract(Mana.BlueMana(1));
addManaCombination(Mana.BlueMana(1), existingMana, payCombinations, payCombinationsStrings);
}
if (manaToPay.getGreen() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.GreenMana(1).toString())) {
manaToPay.subtract(Mana.GreenMana(1));
if (manaToPayFrom.getGreen() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.GreenMana(1).toString())) {
manaToPayFrom.subtract(Mana.GreenMana(1));
addManaCombination(Mana.GreenMana(1), existingMana, payCombinations, payCombinationsStrings);
}
if (manaToPay.getRed() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.RedMana(1).toString())) {
manaToPay.subtract(Mana.RedMana(1));
if (manaToPayFrom.getRed() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.RedMana(1).toString())) {
manaToPayFrom.subtract(Mana.RedMana(1));
addManaCombination(Mana.RedMana(1), existingMana, payCombinations, payCombinationsStrings);
}
if (manaToPay.getWhite() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.WhiteMana(1).toString())) {
manaToPay.subtract(Mana.WhiteMana(1));
if (manaToPayFrom.getWhite() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.WhiteMana(1).toString())) {
manaToPayFrom.subtract(Mana.WhiteMana(1));
addManaCombination(Mana.WhiteMana(1), existingMana, payCombinations, payCombinationsStrings);
}
// Pay with any only needed if colored payment was not possible
if (payCombinations.isEmpty() && manaToPayFrom.getAny() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.AnyMana(1).toString())) {
manaToPayFrom.subtract(Mana.AnyMana(1));
addManaCombination(Mana.AnyMana(1), existingMana, payCombinations, payCombinationsStrings);
}
}
}
} else {
@ -352,5 +370,16 @@ public class ManaOptions extends ArrayList<Mana> {
list.add(s);
}
}
// Remove fully included variations
for (int i = this.size() - 1; i >= 0; i--) {
for (int ii = 0; ii < i; ii++) {
Mana moreValuable = Mana.getMoreValuableMana(this.get(i), this.get(ii));
if (moreValuable != null) {
this.get(ii).setToMana(moreValuable);
this.remove(i);
break;
}
}
}
}
}

View file

@ -1,6 +1,6 @@
package mage.abilities.mana;
import java.util.ArrayList;
import java.util.List;
import mage.Mana;
import mage.abilities.costs.Cost;
@ -55,7 +55,7 @@ public class SimpleManaAbility extends ActivatedManaAbilityImpl {
if (predictable) {
return super.getNetMana(game);
}
return netMana;
return new ArrayList<Mana>(netMana);
}
}

View file

@ -1,4 +1,3 @@
package mage.abilities.mana;
import java.util.ArrayList;
@ -54,7 +53,7 @@ public abstract class TriggeredManaAbility extends TriggeredAbilityImpl implemen
}
return newNetMana;
}
return netMana;
return new ArrayList<Mana>(netMana);
}
/**

View file

@ -32,7 +32,7 @@ public enum CardRepository {
// raise this if db structure was changed
private static final long CARD_DB_VERSION = 51;
// raise this if new cards were added to the server
private static final long CARD_CONTENT_VERSION = 115;
private static final long CARD_CONTENT_VERSION = 116;
private Dao<CardInfo, Object> cardDao;
private Set<String> classNames;
@ -159,7 +159,10 @@ public enum CardRepository {
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
qb.distinct().selectColumns("name");
Where where = qb.where();
where.and(where.not().not().like("supertypes", '%' + SuperType.BASIC.name() + '%'), where.like("types", '%' + CardType.LAND.name() + '%'));
where.and(
where.not().like("supertypes", '%' + SuperType.BASIC.name() + '%'),
where.like("types", '%' + CardType.LAND.name() + '%')
);
List<CardInfo> results = cardDao.query(qb.prepare());
for (CardInfo card : results) {
int result = card.getName().indexOf(" // ");
@ -252,7 +255,10 @@ public enum CardRepository {
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
qb.distinct().selectColumns("name");
Where where = qb.where();
where.and(where.not().like("types", '%' + CardType.CREATURE.name() + '%'), where.not().like("types", '%' + CardType.LAND.name() + '%'));
where.and(
where.not().like("types", '%' + CardType.CREATURE.name() + '%'),
where.not().like("types", '%' + CardType.LAND.name() + '%')
);
List<CardInfo> results = cardDao.query(qb.prepare());
for (CardInfo card : results) {
int result = card.getName().indexOf(" // ");
@ -275,7 +281,10 @@ public enum CardRepository {
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
qb.distinct().selectColumns("name");
Where where = qb.where();
where.and(where.not().like("types", '%' + CardType.ARTIFACT.name() + '%'), where.not().like("types", '%' + CardType.LAND.name() + '%'));
where.and(
where.not().like("types", '%' + CardType.ARTIFACT.name() + '%'),
where.not().like("types", '%' + CardType.LAND.name() + '%')
);
List<CardInfo> results = cardDao.query(qb.prepare());
for (CardInfo card : results) {
int result = card.getName().indexOf(" // ");

View file

@ -462,6 +462,16 @@ public enum SubType {
return subTypeSet;
}
public static Set<SubType> getArtifactTypes(boolean withCustomSets) {
Set<SubType> subTypes = EnumSet.noneOf(SubType.class);
for (SubType subType : values()) {
if (subType.getSubTypeSet() == SubTypeSet.ArtifactType && (withCustomSets || !subType.customSet)) {
subTypes.add(subType);
}
}
return subTypes;
}
public static Set<SubType> getPlaneswalkerTypes(boolean withCustomSets) {
Set<SubType> subTypes = EnumSet.noneOf(SubType.class);
for (SubType subType : values()) {

View file

@ -0,0 +1,39 @@
/*
* 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.filter.predicate.mageobject;
import mage.MageObject;
import mage.ObjectColor;
import mage.filter.predicate.ObjectSourcePlayer;
import mage.filter.predicate.ObjectSourcePlayerPredicate;
import mage.filter.predicate.Predicate;
import mage.game.Game;
/**
*
* @author noahg
*/
public class SharesColorPredicate implements Predicate<MageObject> {
private final ObjectColor color;
public SharesColorPredicate(ObjectColor color) {
this.color = color;
}
@Override
public boolean apply(MageObject input, Game game) {
return color != null && input.getColor(game).shares(color);
}
@Override
public String toString() {
return "shares a color";
}
}

View file

@ -0,0 +1,27 @@
package mage.filter.predicate.permanent;
import mage.filter.predicate.Predicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
*
* @author North
*/
public class ControllerIsActivePlayerPredicate implements Predicate<Permanent> {
@Override
public boolean apply(Permanent input, Game game) {
if(input.getControllerId() == null){
return false;
}
return game.getActivePlayerId().equals(input.getControllerId());
}
@Override
public String toString() {
return "controlled by the active player";
}
}

View file

@ -0,0 +1,32 @@
package mage.game.permanent.token;
import mage.MageInt;
import mage.constants.CardType;
import mage.constants.SubType;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public final class OxToken extends TokenImpl {
public OxToken() {
super("Ox", "2/4 white Ox creature token");
cardType.add(CardType.CREATURE);
color.setWhite(true);
subtype.add(SubType.OX);
power = new MageInt(2);
toughness = new MageInt(4);
}
public OxToken(final OxToken token) {
super(token);
}
@Override
public OxToken copy() {
return new OxToken(this);
}
}

View file

@ -2647,6 +2647,7 @@ public abstract class PlayerImpl implements Player, Serializable {
available.addMana(manaAbilities, game);
}
for (Abilities<ActivatedManaAbilityImpl> manaAbilities : sourceWithCosts) {
available.removeDuplicated();
available.addManaWithCost(manaAbilities, game);
}

View file

@ -0,0 +1,53 @@
package mage.watchers.common;
import mage.MageObjectReference;
import mage.constants.WatcherScope;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.watchers.Watcher;
import java.util.HashSet;
import java.util.Set;
/**
*
* @author noahg
*/
public class WasBlockedThisTurnWatcher extends Watcher {
private final Set<MageObjectReference> wasBlockedThisTurnCreatures;
public WasBlockedThisTurnWatcher() {
super(WasBlockedThisTurnWatcher.class.getSimpleName(), WatcherScope.GAME);
wasBlockedThisTurnCreatures = new HashSet<>();
}
public WasBlockedThisTurnWatcher(final WasBlockedThisTurnWatcher watcher) {
super(watcher);
wasBlockedThisTurnCreatures = new HashSet<>(watcher.wasBlockedThisTurnCreatures);
}
@Override
public Watcher copy() {
return new WasBlockedThisTurnWatcher(this);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.BLOCKER_DECLARED) {
this.wasBlockedThisTurnCreatures.add(new MageObjectReference(event.getTargetId(), game));
}
}
public Set<MageObjectReference> getWasBlockedThisTurnCreatures() {
return this.wasBlockedThisTurnCreatures;
}
@Override
public void reset() {
super.reset();
wasBlockedThisTurnCreatures.clear();
}
}

View file

@ -33585,7 +33585,8 @@ Island|Battlebond|251|C||Basic Land - Island|||({T}: Add {U}.)|
Swamp|Battlebond|252|C||Basic Land - Swamp|||({T}: Add {B}.)|
Mountain|Battlebond|253|C||Basic Land - Mountain|||({T}: Add {R}.)|
Forest|Battlebond|254|C||Basic Land - Forest|||({T}: Add {G}.)|
Aether Shield Artificer|Core Set 2019|2|U|{3}{W}|Creature - Dwarf Artificer|3|3|At the beginning of combat on your turn, target artifact creature you control gets +2/+2 and gains indestructible until end of turn.|
Aegis of the Heavens|Core Set 2019|1|U|{1}{W}|Instant|||Target creature gets +1/+7 until end of turn.|
Aethershield Artificer|Core Set 2019|2|U|{3}{W}|Creature - Dwarf Artificer|3|3|At the beginning of combat on your turn, target artifact creature you control gets +2/+2 and gains indestructible until end of turn.|
Ajani, Adversary of Tyrants|Core Set 2019|3|M|{2}{W}{W}|Legendary Planeswalker - Ajani|4|+1: Put a +1/+1 counter on each of up to two target creatures.$2: Return target creature card with converted mana cost 2 or less from your graveyard to the battlefield.$7: You get an emblem with "At the beginning of your end step, create three 1/1 white Cat creature tokens with lifelink."|
Ajani's Last Stand|Core Set 2019|4|R|{2}{W}{W}|Enchantment|||Whenever a creature or planeswalker you control dies, you may sacrifice Ajani's Last Stand. If you do, create a 4/4 white Avatar creature token with flying.$When a spell or ability an opponent controls causes you to discard this card, if you control a Plains, create a 4/4 white Avatar creature token with flying.|
Ajani's Pridemate|Core Set 2019|5|U|{1}{W}|Creature - Cat Soldier|2|2|Whenever you gain life, you may put a +1/+1 counter on Ajani's Pridemate.|
@ -33595,6 +33596,7 @@ Cavalry Drillmaster|Core Set 2019|8|C|{1}{W}|Creature - Human Knight|2|1|When Ca
Cleansing Nova|Core Set 2019|9|R|{3}{W}{W}|Sorcery|||Choose one —$• Destroy all creatures.$• Destroy all artifacts and enchantments.|
Daybreak Chaplain|Core Set 2019|10|C|{1}{W}|Creature - Human Cleric|1|3|Lifelink|
Dwarven Priest|Core Set 2019|11|C|{3}{W}|Creature - Dwarf Cleric|2|4|When Dwarven Priest enters the battlefield, you gain 1 life for each creature you control.|
Gallant Cavalry|Core Set 2019|12|C|{3}{W}|Creature - Human Knight|2|2|Vigilance$When Gallant Cavalry enters the battlefield, create a 2/2 white Knight creature token with vigilance.|
Herald of Faith|Core Set 2019|13|U|{3}{W}{W}|Creature - Angel|4|3|Flying$Whenever Herald of Faith attacks, you gain 2 life.|
Hieromancer's Cage|Core Set 2019|14|U|{3}{W}|Enchantment|||When Hieromancer's Cage enters the battlefield, exile target nonland permanent an opponent controls until Hieromancer's Cage leaves the battlefield.|
Inspired Charge|Core Set 2019|15|C|{2}{W}{W}|Instant|||Creatures you control get +2/+1 until end of turn.|
@ -33602,11 +33604,13 @@ Invoke the Divine|Core Set 2019|16|C|{2}{W}|Instant|||Destroy target artifact or
Isolate|Core Set 2019|17|R|{W}|Instant|||Exile target permanent with converted mana cost 1.|
Knight of the Tusk|Core Set 2019|18|C|{4}{W}{W}|Creature - Human Knight|3|7|Vigilance|
Knight's Pledge|Core Set 2019|19|C|{1}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2.|
Knightly Valor|Core Set 2019|20|U|{4}{W}|Enchantment - Aura|||Enchant creature$When Knightly Valor enters the battlefield, create a 2/2 white Knight creature token with vigilance.$Enchanted creature gets +2/+2 and has vigilance.|
Lena, Selfless Champion|Core Set 2019|21|R|{4}{W}{W}|Legendary Creature - Human Knight|3|3|When Lena, Selfless Champion enters the battlefield, create a 1/1 white Soldier creature token for each nontoken creature you control.$Sacrifice Lena: Creatures you control with power less than Lena's power gain indestructible until end of turn.|
Leonin Vanguard|Core Set 2019|22|U|{W}|Creature - Cat Soldier|1|1|At the beginning of combat on your turn, if you control three or more creatures, Leonin Vanguard gets +1/+1 until end of turn and you gain 1 life.|
Leonin Warleader|Core Set 2019|23|R|{2}{W}{W}|Creature - Cat Soldier|4|4|Whenever Leonin Warleader attacks, create two 1/1 white Cat creature tokens with lifelink that are tapped and attacking.|
Loxodon Line Breaker|Core Set 2019|24|C|{2}{W}|Creature - Elephant Soldier|3|2||
Luminous Bonds|Core Set 2019|25|C|{2}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature can't attack or block.|
Make a Stand|Core Set 2019|26|U|{2}{W}|Instant|||Creatures you control get +1/+0 and gain indestructible until end of turn.|
Mentor of the Meek|Core Set 2019|27|R|{2}{W}|Creature - Human Soldier|2|2|Whenever another creature with power 2 or less enters the battlefield under your control, you may pay {1}. If you do, draw a card.|
Mighty Leap|Core Set 2019|28|C|{1}{W}|Instant|||Target creature gets +2/+2 and gains flying until end of turn.|
Militia Bugler|Core Set 2019|29|U|{2}{W}|Creature - Human Soldier|2|3|Vigilance$When Militia Bugler enters the battlefield, look at the top four cards of your library. You may reveal a creature card with power 2 or less from among them and put it into your hand. Put the rest on the bottom of your library in a random order.|
@ -33617,21 +33621,24 @@ Remorseful Cleric|Core Set 2019|33|R|{1}{W}|Creature - Spirit Cleric|2|1|Flying$
Resplendent Angel|Core Set 2019|34|M|{1}{W}{W}|Creature - Angel|3|3|Flying$At the beginning of each end step, if you gained 5 or more life this turn, create a 4/4 white Angel creature token with flying and vigilance.${3}{W}{W}{W}: Until end of turn, Resplendent Angel gets +2/+2 and gains lifelink.|
Revitalize|Core Set 2019|35|C|{1}{W}|Instant|||You gain 3 life.$Draw a card.|
Rustwing Falcon|Core Set 2019|36|C|{W}|Creature - Bird|1|2|Flying|
Shield Mare|Core Set 2019|37|U|{1}{W}{W}|Creature - Horse|2|3|Shield Mare can't be blocked by red creatures.$When Shield Mare enters the battlefield or becomes the target of a spell or ability and opponent controls, you gain 3 life.|
Shield Mare|Core Set 2019|37|U|{1}{W}{W}|Creature - Horse|2|3|Shield Mare can't be blocked by red creatures.$When Shield Mare enters the battlefield or becomes the target of a spell or ability an opponent controls, you gain 3 life.|
Star-Crowned Stag|Core Set 2019|38|C|{3}{W}|Creature - Elk|3|3|Whenever Star-Crowned Stag attacks, tap target creature defending player controls.|
Suncleanser|Core Set 2019|39|R|{1}{W}|Creature - Human Cleric|1|4|When Suncleanser enters the battlefield, choose one —$• Remove all counters from target creature. It can't have counters put on it for as long as Suncleanser remains on the battlefield.$• Target opponent loses all counters. That player can't get counters for as long as Suncleanser remains on the battlefield.|
Take Vengeance|Core Set 2019|40|C|{1}{W}|Sorcery|||Destroy target tapped creature.|
Trusty Packbeast|Core Set 2019|41|C|{2}{W}|Creature - Beast|2|3|When Trusty Packbeast enters the battlefield, return target artifact card from your graveyard to your hand.|
Valiant Knight|Core Set 2019|42|R|{3}{W}|Creature - Human Knight|3|4|Other Knights you control get +1/+1.${3}{W}{W}: Knights you control gain double strike until end of turn.|
Aether Tunnel|Core Set 2019|43|U|{1}{U}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +1/+0 and can't be blocked.|
Anticipate|Core Set 2019|44|C|{1}{U}|Instant|||Look at the top three cards of your library. Put one of them into your hand and the rest on the bottom of your library in any order.|
Aven Wind Mage|Core Set 2019|45|C|{2}{U}|Creature - Bird Wizard|2|2|Flying$Whenever you cast an instant or sorcery spell, Aven Wind Mage gets +1/+1 until end of turn.|
Aviation Pioneer|Core Set 2019|46|C|{2}{U}|Creature - Human Artificer|1|2|When Aviation Pioneer enters the battlefield, create a 1/1 colorless Thopter artifact creature token with flying.|
Bone to Ash|Core Set 2019|47|U|{2}{U}{U}|Instant|||Counter target creature spell.$Draw a card.|
Cancel|Core Set 2019|48|C|{1}{U}{U}|Instant|||Counter target spell.|
Departed Deckhand|Core Set 2019|49|U|{1}{U}|Creature - Spirit Pirate|||When Departed Deckhand becomes the target of a spell, sacrifice it.$Departed Deckhand can't be blocked except by Spirits.${3}{U}: Another target creature you control can't be blocked this turn except by Spirits.|
Departed Deckhand|Core Set 2019|49|U|{1}{U}|Creature - Spirit Pirate|2|2|When Departed Deckhand becomes the target of a spell, sacrifice it.$Departed Deckhand can't be blocked except by Spirits.${3}{U}: Another target creature you control can't be blocked this turn except by Spirits.|
Disperse|Core Set 2019|50|C|{1}{U}|Instant|||Return target nonland permanent to its owner's hand.|
Divination|Core Set 2019|51|C|{2}{U}|Sorcery|||Draw two cards.|
Djinn of Wishes|Core Set 2019|52|R|{3}{U}{U}|Creature - Djinn|4|4|Flying$Djinn of Wishes enters the battlefield with three wish counters on it.${2}{U}{U}, Remove a wish counter from Djinn of Wishes: Reveal the top card of your library. You may play that card without paying its mana cost. If you don't, exile it.|
Dwindle|Core Set 2019|53|C|{2}{U}|Enchantment - Aura|||Enchant creature$Enchanted creature gets -6/-0.$When enchanted creature blocks, destroy it.|
Essence Scatter|Core Set 2019|54|C|{1}{U}|Instant|||Counter target creature spell.|
Exclusion Mage|Core Set 2019|55|U|{2}{U}|Creature - Human Wizard|2|2|When Exclusion Mage enters the battlefield, return target creature an opponent controls to its owner's hand.|
Frilled Sea Serpent|Core Set 2019|56|C|{4}{U}{U}|Creature - Serpent|4|6|{5}{U}{U}: Frilled Sea Serpent can't be blocked this turn.|
Gearsmith Prodigy|Core Set 2019|57|C|{U}|Creature - Human Artificer|1|2|Gearsmith Prodigy gets +1/+0 as long as you control an artifact.|
@ -33656,7 +33663,7 @@ Snapping Drake|Core Set 2019|75|C|{3}{U}|Creature - Drake|3|2|Flying|
Supreme Phantom|Core Set 2019|76|R|{1}{U}|Creature - Spirit|1|3|Flying$Other Spirits you control get +1/+1.|
Surge Mare|Core Set 2019|77|U|{U}{U}|Creature - Horse Fish|0|5|Surge Mare can't be blocked by green creatures.$Whenever Surge Mare deals damage to an opponent, you may draw a card. If you do, discard a card.${1}{U}: Surge Mare gets +2/-2 until end of turn.|
Switcheroo|Core Set 2019|78|U|{4}{U}|Sorcery|||Exchange control of two target creatures.|
Tezzeret, Artifice Master|Core Set 2019|79|M|{3}{U}{U}|Legendary Planeswalker - Tezzeret|5|+1: Create a colorless Thopter artifact creature token with flying.$0: Draw a card. If you control three or more artifacts, draw two cards instead.$9: You get an emblem with "At the beginning of your end step, search your library for a permanent card, put it into the battlefield, then shuffle your library."|
Tezzeret, Artifice Master|Core Set 2019|79|M|{3}{U}{U}|Legendary Planeswalker - Tezzeret|5|+1: Create a 1/1 colorless Thopter artifact creature token with flying.$0: Draw a card. If you control three or more artifacts, draw two cards instead.$9: You get an emblem with "At the beginning of your end step, search your library for a permanent card, put it onto the battlefield, then shuffle your library."|
Tolarian Scholar|Core Set 2019|80|C|{2}{U}|Creature - Human Wizard|2|3||
Totally Lost|Core Set 2019|81|C|{4}{U}|Instant|||Put target nonland permanent on top of its owner's library.|
Uncomfortable Chill|Core Set 2019|82|C|{2}{U}|Instant|||Creatures your opponents control get -2/-0 until end of turn.$Draw a card.|
@ -33670,17 +33677,23 @@ Child of Night|Core Set 2019|89|C|{1}{B}|Creature - Vampire|2|1|Lifelink|
Death Baron|Core Set 2019|90|R|{1}{B}{B}|Creature - Zombie Wizard|2|2|Skeleton creatures you control and other Zombie creatures you control get +1/+1 and have deathtouch.|
Demon of Catastrophes|Core Set 2019|91|R|{2}{B}{B}|Creature - Demon|6|6|As an additional cost to cast this spell, sacrifice a creature.$Flying, trample|
Diregraf Ghoul|Core Set 2019|92|U|{B}|Creature - Zombie|2|2|Diregraf Ghoul enters the battlefield tapped.|
Doomed Dissenter|Core Set 2019|93|C|{1}{B}|Creature - Human|1|1|When Doomed Dissenter dies, create a 2/2 black Zombie creature token.|
Duress|Core Set 2019|94|C|{B}|Sorcery|||Target opponent reveals their hand. You choose a noncreature, nonland card from it. That player discards that card.|
Epicure of Blood|Core Set 2019|95|C|{4}{B}|Creature - Vampire|4|4|Whenever you gain life, each opponent loses 1 life.|
Fell Specter|Core Set 2019|96|U|{3}{B}|Creature - Specter|||Flying$When Fell Specter enters the battlefield, target opponent discards a card.$Whenever an opponent discards a card, that player loses 2 life.|
Fell Specter|Core Set 2019|96|U|{3}{B}|Creature - Specter|1|3|Flying$When Fell Specter enters the battlefield, target opponent discards a card.$Whenever an opponent discards a card, that player loses 2 life.|
Fraying Omnipotence|Core Set 2019|97|R|{3}{B}{B}|Sorcery|||Each player loses half their life, then discards half the cards in their hand, then sacrifices half the creatures they control. Round up each time.|
Gravedigger|Core Set 2019|98|U|{3}{B}|Creature - Zombie|2|2|When Gravedigger enters the battlefield, you may return target creature card from your graveyard to your hand.|
Graveyard Marshal|Core Set 2019|99|R|{B}{B}|Creature - Zombie Soldier|3|2|{2}{B}, Exile a creature card from your graveyard: Create a tapped 2/2 black Zombie creature token.|
Hired Blade|Core Set 2019|100|C|{2}{B}|Creature - Human Assassin|3|2|Flash|
Infectious Horror|Core Set 2019|101|C|{3}{B}|Creature - Zombie Horror|2|2|Whenever Infectious Horror attacks, each opponent loses 2 life.|
Infernal Reckoning|Core Set 2019|102|R|{B}|Instant|||Exile target colorless creature. You gain life equal to its power.|
Infernal Scarring|Core Set 2019|103|C|{1}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+0 and has "When this creature dies, draw a card."|
Isareth the Awakener|Core Set 2019|104|R|{1}{B}{B}|Legendary Creature - Human Wizard|3|3|Deathtouch$Whenever Isareth the Awakener attacks, you may pay {X}. When you do, return target creature card with converted mana cost X from your graveyard to the battlefield with a corpse counter on it. If that creature would leave the battlefield, exile it instead of putting it anywhere else.|
Lich's Caress|Core Set 2019|105|C|{3}{B}{B}|Sorcery|||Destroy target creature. You gain 3 life.|
Liliana, Untouched by Death|Core Set 2019|106|M|{2}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Put the top three cards of your library into your graveyard. If at least one of them is a Zombie card, each opponent loses 2 life and you gain 2 life.$2: Target creature gets -X/-X until end of turn, where X is the number of Zombies you control.$3: You may cast Zombie cards from your graveyard this turn.|
Liliana's Contract|Core Set 2019|107|R|{3}{B}{B}|Enchantment|||When Liliana's Contract enters the battlefield, you draw four cards and you lose 4 life.$At the beginning of your upkeep, if you control four or more Demons with different names, you win the game.|
Macabre Waltz|Core Set 2019|108|C|{1}{B}|Sorcery|||Return up to two target creature cards from your graveyard to your hand, then discard a card.|
Mind Rot|Core Set 2019|109|C|{2}{B}|Sorcery|||Target player discards two cards.|
Murder|Core Set 2019|110|U|{1}{B}{B}|Instant|||Destroy target creature.|
Nightmare's Thirst|Core Set 2019|111|U|{B}|Instant|||You gain 1 life. Target creature gets -X/-X until end of turn, where X is the amount of life you gained this turn.|
Open the Graves|Core Set 2019|112|R|{3}{B}{B}|Enchantment|||Whenever a nontoken creature you control dies, create a 2/2 black Zombie creature token.|
@ -33695,17 +33708,21 @@ Sovereign's Bite|Core Set 2019|120|C|{1}{B}|Sorcery|||Target player loses 3 life
Stitcher's Supplier|Core Set 2019|121|U|{B}|Creature - Zombie|1|1|When Stitcher's Supplier enters the battlefield or dies, put the top three cards of your library into your graveyard.|
Strangling Spores|Core Set 2019|122|C|{3}{B}|Instant|||Target creature gets -3/-3 until end of turn.|
Two-Headed Zombie|Core Set 2019|123|C|{3}{B}|Creature - Zombie|4|2|Menace|
Vampire Neonate|Core Set 2019|124|C|{B}|Creature - Vampire|0|3|{2}, {T}: Each opponent loses 1 life and you gain 1 life.|
Vampire Sovereign|Core Set 2019|125|U|{3}{B}{B}|Creature - Vampire|3|4|Flying$When Vampire Sovereign enters the battlefield, target opponent loses 3 life and you gain 3 life.|
Walking Corpse|Core Set 2019|126|C|{1}{B}|Creature - Zombie|2|2||
Act of Treason|Core Set 2019|127|C|{2}{R}|Sorcery|||Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn.|
Alpine Moon|Core Set 2019|128|R|{R}|Enchantment|||As Alpine Moon enters the battlefield, choose a nonbasic land card name.$Lands your opponents control with the chosen name lose all land types and abilities, and they gain "{T}: Add one mana of any color."|
Apex of Power|Core Set 2019|129|M|{7}{R}{R}{R}|Sorcery|||Exile the top seven cards of your library. Until end of turn, you may cast nonland cards exiled this way.$If this spell was cast from your hand, add ten mana of any one color.|
Banefire|Core Set 2019|130|R|{X}{R}|Sorcery|||Banefire deals X damage to any target.$If X is 5 or more, this spell can't be countered and the damage can't be prevented.|
Boggart Brute|Core Set 2019|131|C|{2}{R}|Creature - Goblin Warrior|3|2|Menace|
Catalyst Elemental|Core Set 2019|132|C|{2}{R}|Creature - Elemental|2|2|Sacrifice Catalyst Elemental: Add {R}{R}.|
Dark-Dweller Oracle|Core Set 2019|134|R|{1}{R}|Creature - Goblin Shaman|||{1}, Sacrifice a creature: Exile the top card of your library. You may play that card this turn.|
Crash Through|Core Set 2019|133|C|{R}|Sorcery|||Creatures you control gain trample until end of turn.$Draw a card.|
Dark-Dweller Oracle|Core Set 2019|134|R|{1}{R}|Creature - Goblin Shaman|2|2|{1}, Sacrifice a creature: Exile the top card of your library. You may play that card this turn.|
Demanding Dragon|Core Set 2019|135|R|{3}{R}{R}|Creature - Dragon|5|5|Flying$When Demanding Dragon enters the battlefield, it deals 5 damage to target opponent unless that player sacrifices a creature.|
Dismissive Pyromancer|Core Set 2019|136|R|{1}{R}|Creature - Human Wizard|2|2|{R}, {T}, Discard a card: Draw a card.${2}{R}, {T}, Sacrifice Disdainful Pyromancer: It deals 4 damage to target creature.|
Dismissive Pyromancer|Core Set 2019|136|R|{1}{R}|Creature - Human Wizard|2|2|{R}, {T}, Discard a card: Draw a card.${2}{R}, {T}, Sacrifice Dismissive Pyromancer: It deals 4 damage to target creature.|
Doublecast|Core Set 2019|137|U|{R}{R}|Sorcery|||When you cast your next instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy.|
Dragon Egg|Core Set 2019|138|U|{2}{R}|Creature - Dragon|0|2|Defender$When Dragon Egg dies, create a 2/2 red Dragon creature token with flying. It has "{R}: This creature gets +1/+0 until end of turn."|
Electrify|Core Set 2019|139|C|{3}{R}|Instant|||Electrify deals 4 damage to target creature.|
Fiery Finish|Core Set 2019|140|U|{4}{R}{R}|Sorcery|||Fiery Finish deals 7 damage to target creature.|
Fire Elemental|Core Set 2019|141|C|{3}{R}{R}|Creature - Elemental|5|4||
@ -33715,6 +33732,7 @@ Goblin Trashmaster|Core Set 2019|144|R|{2}{R}{R}|Creature - Goblin Warrior|3|3|O
Guttersnipe|Core Set 2019|145|U|{2}{R}|Creature - Goblin Shaman|2|2|Whenever you cast an instant or sorcery spell, Guttersnipe deals 2 damage to each opponent.|
Havoc Devils|Core Set 2019|146|C|{2}{R}{R}|Creature - Devil|4|3|Trample|
Hostile Minotaur|Core Set 2019|147|C|{3}{R}|Creature - Minotaur|3|3|Haste|
Inferno Hellion|Core Set 2019|148|U|{3}{R}|Creature - Hellion|7|3|Trample$At the beginning of each end step, if Inferno Hellion attacked or blocked this turn, its owner shuffles it into their library.|
Lathliss, Dragon Queen|Core Set 2019|149|R|{4}{R}{R}|Legendary Creature - Dragon|6|6|Flying$Whenever another nontoken Dragon enters the battlefield under your control, create a 5/5 red Dragon creature token with flying.${1}{R}: Dragons you control get +1/+0 until end of turn.|
Lava Axe|Core Set 2019|150|C|{4}{R}|Sorcery|||Lava Axe deals 5 damage to target player or planeswalker.|
Lightning Mare|Core Set 2019|151|U|{R}{R}|Creature - Elemental Horse|3|1|This spell can't be countered.$Lightning Mare can't be blocked by blue creatures.${1}{R}: Lightning Mare gets +1/+0 until end of turn.|
@ -33728,6 +33746,7 @@ Smelt|Core Set 2019|158|C|{R}|Instant|||Destroy target artifact.|
Sparktongue Dragon|Core Set 2019|159|C|{3}{R}{R}|Creature - Dragon|3|3|Flying$When Sparktongue Dragon enters the battlefield, you may pay {2}{R}. When you do, it deals 3 damage to any target.|
Spit Flame|Core Set 2019|160|R|{2}{R}|Instant|||Spit Flame deals 4 damage to target creature.$Whenever a Dragon enters the battlefield under your control, you may pay {R}. If you do, return Spit Flame from your graveyard to your hand.|
Sure Strike|Core Set 2019|161|C|{1}{R}|Instant|||Target creature gets +3/+0 and gains first strike until end of turn.|
Tectonic Rift|Core Set 2019|162|U|{3}{R}|Sorcery|||Destroy target land. Creatures without flying can't block this turn.|
Thud|Core Set 2019|163|U|{R}|Sorcery|||As an additional cost to cast this spell, sacrifice a creature.$Thud deals damage equal to the sacrificed creature's power to any target.|
Tormenting Voice|Core Set 2019|164|C|{1}{R}|Sorcery|||As an additional cost to cast this spell, discard a card.$Draw two cards.|
Trumpet Blast|Core Set 2019|165|C|{2}{R}|Instant|||Attacking creatures get +2/+0 until end of turn.|
@ -33739,6 +33758,7 @@ Bristling Boar|Core Set 2019|170|C|{3}{G}|Creature - Boar|4|3|Bristling Boar can
Centaur Courser|Core Set 2019|171|C|{2}{G}|Creature - Centaur Warrior|3|3||
Colossal Dreadmaw|Core Set 2019|172|C|{4}{G}{G}|Creature - Dinosaur|6|6|Trample|
Colossal Majesty|Core Set 2019|173|U|{2}{G}|Enchantment|||At the beginning of your upkeep, if you control a creature with power 4 or greater, draw a card.|
Daggerback Basilisk|Core Set 2019|174|C|{2}{G}|Creature - Basilisk|2|2|Deathtouch|
Declare Dominance|Core Set 2019|175|U|{3}{G}{G}|Sorcery|||Target creature gets +3/+3 until end of turn. All creatures able to block it this turn do so.|
Druid of Horns|Core Set 2019|176|U|{3}{G}|Creature - Human Druid|2|3|Whenever you cast an Aura spell that targets Druid of Horns, create a 3/3 green Beast creature token.|
Druid of the Cowl|Core Set 2019|177|C|{1}{G}|Creature - Elf Druid|1|3|{T}: Add {G}.|
@ -33746,13 +33766,17 @@ Dryad Greenseeker|Core Set 2019|178|U|{1}{G}|Creature - Dryad|1|3|{T}: Look at t
Elvish Clancaller|Core Set 2019|179|R|{G}{G}|Creature - Elf Druid|1|1|Other Elves you control get +1/+1.${4}{G}{G}, {T}: Search your library for a card named Elvish Clancaller, put it onto the battlefield, then shuffle your library.|
Elvish Rejuvenator|Core Set 2019|180|C|{2}{G}|Creature - Elf Druid|1|1|Whenever Elvish Rejuvenator enters the battlefield, look at the top five cards of your library. You may put a land card from among them onto the battlefield tapped. Put the rest on the bottom of your library in a random order.|
Ghastbark Twins|Core Set 2019|181|U|{5}{G}{G}|Creature - Treefolk|7|7|Trample$Ghastbark Twins can block an additional creature each combat.|
Ghirapur Guide|Core Set 2019|182|U|{2}{G}|Creature - Elf Scout|3|2|{2}{G}: Target creature you control can't be blocked by creatures with power 2 or less this turn.|
Giant Spider|Core Set 2019|183|C|{3}{G}|Creature - Spider|2|4|Reach|
Gift of Paradise|Core Set 2019|184|U|{2}{G}|Enchantment - Aura|||Enchant land$When Gift of Paradise enters the battlefield, you gain 3 life.$Enchanted land has "{T}: Add two mana of any one color."|
Gigantosaurus|Core Set 2019|185|R|{G}{G}{G}{G}{G}|Creature - Dinosaur|10|10||
Goreclaw, Terror of Qal Sisma|Core Set 2019|186|R|{3}{G}|Legendary Creature - Bear|4|3|Creature spells you cast with power 4 or greater cost {2} less to cast.$Whenever Goreclaw, Terror of Qal Sisma attacks, each creature you control with power 4 or greater gets +1/+1 and gains trample until end of turn.|
Greenwood Sentinel|Core Set 2019|187|C|{1}{G}|Creature - Elf Scout|2|2|Vigilance|
Highland Game|Core Set 2019|188|C|{1}{G}|Creature - Elk|2|1|When Highland Game dies, you gain 2 life.|
Hungering Hydra|Core Set 2019|189|R|{X}{G}|Creature - Hydra|0|0|Hungering Hydra enters the battlefield with X +1/+1 counters on it.$Hungering Hydra can't be blocked by more than one creature.$Whenever Hungering Hydra is dealt damage, put that many +1/+1 counters on it.|
Naturalize|Core Set 2019|190|C|{1}{G}|Instant|||Destroy target artifact or enchantment.|
Oakenform|Core Set 2019|191|C|{2}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +3/+3.|
Pelakka Wurm|Core Set 2019|192|U|{4}{G}{G}{G}|Creature - Wurm|7|7|Trample$When Pelakka Wurm enters the battlefield, you gain 7 life.$When Pelakka Wurm dies, draw a card.|
Pelakka Wurm|Core Set 2019|192|R|{4}{G}{G}{G}|Creature - Wurm|7|7|Trample$When Pelakka Wurm enters the battlefield, you gain 7 life.$When Pelakka Wurm dies, draw a card.|
Plummet|Core Set 2019|193|C|{1}{G}|Instant|||Destroy target creature with flying.|
Prodigious Growth|Core Set 2019|194|R|{4}{G}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +7/+7 and has trample.|
Rabid Bite|Core Set 2019|195|C|{1}{G}|Sorcery|||Target creature you control deals damage equal to its power to target creature you don't control.|
@ -33762,6 +33786,7 @@ Rhox Oracle|Core Set 2019|198|C|{4}{G}|Creature - Rhino Monk|4|2|When Rhox Oracl
Root Snare|Core Set 2019|199|C|{1}{G}|Instant|||Prevent all combat damage that would be dealt this turn.|
Runic Armasaur|Core Set 2019|200|R|{1}{G}{G}|Creature - Dinosaur|2|5|Whenever an opponent activates an ability of a creature or land that isn't a mana ability, you may draw a card.|
Scapeshift|Core Set 2019|201|M|{2}{G}{G}|Sorcery|||Sacrifice any number of lands. Search your library for up to that many land cards, put them onto the battlefield tapped, then shuffle your library.|
Talons of Wildwood|Core Set 2019|202|C|{1}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +1/+1 and has trample.${2}{G}: Return Talons of Wildwood from your graveyard to your hand.|
Thorn Lieutenant|Core Set 2019|203|R|{1}{G}|Creature - Elf Warrior|2|3|Whenever Thorn Lieutenant becomes the target of a spell or ability an opponent controls, create a 1/1 green Elf Warrior creature token.${5}{G}: Thorn Lieutenant gets +4/+4 until end of turn.|
Thornhide Wolves|Core Set 2019|204|C|{4}{G}|Creature - Wolf|4|5||
Titanic Growth|Core Set 2019|205|C|{1}{G}|Instant|||Target creature gets +4/+4 until end of turn.|
@ -33793,21 +33818,39 @@ Crucible of Worlds|Core Set 2019|229|M|{3}|Artifact|||You may play land cards fr
Desecrated Tomb|Core Set 2019|230|R|{3}|Artifact|||Whenever one or more creature cards leave your graveyard, create a 1/1 black Bat creature token with flying.|
Diamond Mare|Core Set 2019|231|U|{2}|Artifact Creature - Horse|1|3|As Diamond Mare enters the battlefield, choose a color.$Whenever you cast a spell of the chosen color, you gain 1 life.|
Dragon's Hoard|Core Set 2019|232|R|{3}|Artifact|||Whenever a Dragon enters the battlefield under your control, put a gold counter on Dragon's Hoard.${T}, Remove a gold counter from Dragon's Hoard: Draw a card.${T}: Add one mana of any color.|
Explosive Apparatus|Core Set 2019|233|C|{1}|Artifact|||{3}, {T}, Sacrifice Explosive Apparatus: It deals 2 damage to any target.|
Field Creeper|Core Set 2019|234|C|{2}|Artifact Creature - Scarecrow|2|1||
Fountain of Renewal|Core Set 2019|235|U|{1}|Artifact|||At the beginning of your upkeep, you gain 1 life.${3}, Sacrifice Fountain of Renewal: Draw a card.|
Gargoyle Sentinel|Core Set 2019|236|U|{3}|Artifact Creature - Gargoyle|3|3|Defender${3}: Until end of turn, Gargoyle Sentinel loses defender and gains flying.|
Gearsmith Guardian|Core Set 2019|237|C|{5}|Artifact Creature - Construct|3|5|Gearsmith Guardian gets +2/+0 as long as you control a blue creature.|
Magistrate's Scepter|Core Set 2019|238|R|{3}|Artifact|||{4}, {T}: Put a charge counter on Magistrate's Scepter.${T}, Remove three charge counters from Magistrate's Scepter: Take an extra turn after this one.|
Manalith|Core Set 2019|239|C|{3}|Artifact|||{T}: Add one mana of any color.|
Marauder's Axe|Core Set 2019|240|C|{2}|Artifact - Equipment|||Equipped creature +2/+0.$Equip {2}|
Marauder's Axe|Core Set 2019|240|C|{2}|Artifact - Equipment|||Equipped creature gets +2/+0.$Equip {2}|
Meteor Golem|Core Set 2019|241|U|{7}|Artifact Creature - Golem|3|3|When Meteor Golem enters the battlefield, destroy target nonland permanent an opponent controls.|
Millstone|Core Set 2019|242|U|{2}|Artifact|||{2}, {T}: Target player puts the top two cards of their library into their graveyard.|
Rogue's Gloves|Core Set 2019|243|U|{2}|Artifact - Equipment|||Whenever equipped creature deals combat damage to a player, you may draw a card.$Equip {2}|
Sigiled Sword of Valeron|Core Set 2019|244|R|{3}|Artifact - Equipment|||Equipped creature gets +2/+0, has vigilance, and is a Knight in addition to its other types.$Whenever equipped creature attacks, create a 2/2 white Knight creature token with vigilance that's attacking.$Equip {3}|
Skyscanner|Core Set 2019|245|C|{3}|Artifact Creature - Thopter|1|1|Flying$When Skyscanner enters the battlefield, draw a card.|
Suspicious Bookcase|Core Set 2019|246|U|{2}|Artifact Creature - Wall|0|4|Defender${3}, {T}: Target creature can't be blocked this turn.|
Isolated Tower|Core Set 2019|249|R||Land|||{T}: Add {C}.${1}, {T}: Until end of turn, your opponents and creatures with hexproof they control can be the targets of spells and abilities as though they didn't have hexproof.|
Transmogrifying Wand|Core Set 2019|247|R|{3}|Artifact|||Transmogrifying Wand enters the battlefield with three charge counters on it.${1}, {T}, Remove a charge counter from Transmogrifying Wand: Destroy target creature. Its controller creates a 2/4 white Ox creature token. Activate this ability only any time you could cast a sorcery.|
Cinder Barrens|Core Set 2019|248|C||Land|||Cinder Barrens enters the battlefield tapped.${T}: Add {B} or {R}.|
Detection Tower|Core Set 2019|249|R||Land|||{T}: Add {C}.${1}, {T}: Until end of turn, your opponents and creatures your opponents control with hexproof can be the targets of spells and abilities you control as though they didn't have hexproof.|
Forsaken Sanctuary|Core Set 2019|250|C||Land|||Forsaken Sanctuary enters the battlefield tapped.${T}: Add {W} or {B}.|
Foul Orchard|Core Set 2019|251|C||Land|||Foul Orchard enters the battlefield tapped.${T}: Add {B} or {G}.|
Highland Lake|Core Set 2019|252|C||Land|||Highland Lake enters the battlefield tapped.${T}: Add {U} or {R}.|
Meandering River|Core Set 2019|253|C||Land|||Meandering River enters the battlefield tapped.${T}: Add {W} or {U}.|
Reliquary Tower|Core Set 2019|254|U||Land|||You have no maximum hand size.${T}: Add {C}.|
Rupture Spire|Core Set 2019|255|U||Land|||Rupture Spire enters the battlefield tapped.$When Rupture Spire enters the battlefield, sacrifice it unless you pay {1}.${T}: Add one mana of any color.|
Stone Quarry|Core Set 2019|256|C||Land|||Stone Quarry enters the battlefield tapped.${T}: Add {R} or {W}.|
Submerged Boneyard|Core Set 2019|257|C||Land|||Submerged Boneyard enters the battlefield tapped.${T}: Add {U} or {B}.|
Timber Gorge|Core Set 2019|258|C||Land|||Timber Gorge enters the battlefield tapped.${T}: Add {R} or {G}.|
Tranquil Expanse|Core Set 2019|259|C||Land|||Tranquil Expanse enters the battlefield tapped.${T}: Add {G} or {W}.|
Woodland Stream|Core Set 2019|260|C||Land|||Woodland Stream enters the battlefield tapped.${T}: Add {G} or {U}.|
Plains|Core Set 2019|261|C||Basic Land - Plains|||({T}: Add {W}.)|
Island|Core Set 2019|265|C||Basic Land - Island|||({T}: Add {U}.)|
Swamp|Core Set 2019|269|C||Basic Land - Swamp|||({T}: Add {B}.)|
Mountain|Core Set 2019|273|C||Basic Land - Mountain|||({T}: Add {R}.)|
Forest|Core Set 2019|277|C||Basic Land - Forest|||({T}: Add {G}.)|
Ajani, Wise Counselor|Core Set 2019|281|M|{3}{W}{W}|Legendary Planeswalker - Ajani|5|+2: You gain 1 life for each creature you control.$3: Creatures you control get +2/+2 until end of turn.$9: Put X +1/+1 counters on target creature, where X is your life total.|
Ajani's Influence|Core Set 2019|282|R|{2}{W}{W}|Sorcery|||Put two +1/+1 counters on target creature.$Look at the top five cards of your library. You may reveal a white card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.|
Court Cleric|Core Set 2019|283|U|{W}|Creature - Human Cleric|1|1|Lifelink$Court Cleric gets +1/+1 as long as you control an Ajani planeswalker.|