Merge pull request #1 from magefree/master

Merge https://github.com/magefree/mage/
This commit is contained in:
Zzooouhh 2017-09-21 00:58:58 +02:00 committed by GitHub
commit a4c6790f93
423 changed files with 15282 additions and 3325 deletions

View file

@ -44,8 +44,6 @@ import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener; import javax.swing.event.PopupMenuListener;
import mage.cards.action.ActionCallback; import mage.cards.action.ActionCallback;
import mage.cards.decks.Deck; import mage.cards.decks.Deck;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository; import mage.cards.repository.CardRepository;
import mage.client.cards.BigCard; import mage.client.cards.BigCard;
import mage.client.chat.ChatPanelBasic; import mage.client.chat.ChatPanelBasic;
@ -134,8 +132,6 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
private final BalloonTip balloonTip; private final BalloonTip balloonTip;
private java.util.List<CardInfo> missingCards;
/** /**
* @return the session * @return the session
*/ */
@ -513,18 +509,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
} }
private void checkForNewImages() { private void checkForNewImages() {
long beforeCall = System.currentTimeMillis(); // Removed TODO: Remove related pref code
missingCards = CardRepository.instance.findCards(new CardCriteria());
LOGGER.info("Card pool load time: " + ((System.currentTimeMillis() - beforeCall) / 1000 + " seconds"));
beforeCall = System.currentTimeMillis();
if (DownloadPictures.checkForMissingCardImages(missingCards)) {
LOGGER.info("Card images checking time: " + ((System.currentTimeMillis() - beforeCall) / 1000 + " seconds"));
UserRequestMessage message = new UserRequestMessage("New images available", "Card images are missing (" + missingCards.size() + "). Do you want to download the images?"
+ "<br><br><i>You can deactivate the image download check on application start in the preferences.</i>");
message.setButton1("No", null);
message.setButton2("Yes", PlayerAction.CLIENT_DOWNLOAD_CARD_IMAGES);
showUserRequestDialog(message);
}
} }
public static void setActive(MagePane frame) { public static void setActive(MagePane frame) {
@ -978,8 +963,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
}//GEN-LAST:event_btnImagesActionPerformed }//GEN-LAST:event_btnImagesActionPerformed
public void downloadImages() { public void downloadImages() {
java.util.List<CardInfo> cards = CardRepository.instance.findCards(new CardCriteria()); DownloadPictures.startDownload();
DownloadPictures.startDownload(null, cards);
} }
public void exitApp() { public void exitApp() {
@ -1317,7 +1301,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
Plugins.instance.downloadSymbols(); Plugins.instance.downloadSymbols();
break; break;
case CLIENT_DOWNLOAD_CARD_IMAGES: case CLIENT_DOWNLOAD_CARD_IMAGES:
DownloadPictures.startDownload(null, missingCards); DownloadPictures.startDownload();
break; break;
case CLIENT_DISCONNECT: case CLIENT_DISCONNECT:
if (SessionHandler.isConnected()) { if (SessionHandler.isConnected()) {

View file

@ -1,5 +1,6 @@
package org.mage.plugins.card.dl.sources; package org.mage.plugins.card.dl.sources;
import java.util.ArrayList;
import org.mage.plugins.card.images.CardDownloadData; import org.mage.plugins.card.images.CardDownloadData;
/** /**
@ -9,12 +10,38 @@ import org.mage.plugins.card.images.CardDownloadData;
public interface CardImageSource { public interface CardImageSource {
String generateURL(CardDownloadData card) throws Exception; String generateURL(CardDownloadData card) throws Exception;
String generateTokenUrl(CardDownloadData card) throws Exception; String generateTokenUrl(CardDownloadData card) throws Exception;
String getNextHttpImageUrl(); String getNextHttpImageUrl();
String getFileForHttpImage(String httpImageUrl); String getFileForHttpImage(String httpImageUrl);
String getSourceName(); String getSourceName();
float getAverageSize(); float getAverageSize();
int getTotalImages(); int getTotalImages();
boolean isTokenSource();
default int getTokenImages() {
return 0;
}
default boolean isTokenSource() {
return false;
}
void doPause(String httpImageUrl); void doPause(String httpImageUrl);
default ArrayList<String> getSupportedSets() {
return null;
}
default boolean isSetSupportedComplete(String setCode) {
return true;
}
default boolean isImageProvided(String setCode, String cardName) {
return false;
}
} }

View file

@ -1,7 +1,10 @@
package org.mage.plugins.card.dl.sources; package org.mage.plugins.card.dl.sources;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import mage.client.dialog.PreferencesDialog; import mage.client.dialog.PreferencesDialog;
import org.mage.plugins.card.images.CardDownloadData; import org.mage.plugins.card.images.CardDownloadData;
import org.mage.plugins.card.utils.CardImageUtils; import org.mage.plugins.card.utils.CardImageUtils;
@ -13,6 +16,202 @@ import org.mage.plugins.card.utils.CardImageUtils;
public enum MagicCardsImageSource implements CardImageSource { public enum MagicCardsImageSource implements CardImageSource {
instance; instance;
private static final Set<String> supportedSets = new LinkedHashSet<String>() {
{
// add("PTC"); // Prerelease Events
add("LEA");
add("LEB");
add("2ED");
add("ARN");
add("ATQ");
add("3ED");
add("LEG");
add("DRK");
add("FEM");
add("4ED");
add("ICE");
add("CHR");
add("HML");
add("ALL");
add("MIR");
add("VIS");
add("5ED");
add("POR");
add("WTH");
add("TMP");
add("STH");
add("EXO");
add("P02");
add("UGL");
add("USG");
add("DD3DVD");
add("DD3EVG");
add("DD3GVL");
add("DD3JVC");
add("ULG");
add("6ED");
add("UDS");
add("PTK");
add("S99");
add("MMQ");
// add("BRB");Battle Royale Box Set
add("NEM");
add("S00");
add("PCY");
add("INV");
// add("BTD"); // Beatdown Boxset
add("PLS");
add("7ED");
add("APC");
add("ODY");
// add("DKM"); // Deckmasters 2001
add("TOR");
add("JUD");
add("ONS");
add("LGN");
add("SCG");
add("8ED");
add("MRD");
add("DST");
add("5DN");
add("CHK");
add("UNH");
add("BOK");
add("SOK");
add("9ED");
add("RAV");
add("GPT");
add("DIS");
add("CSP");
add("TSP");
add("TSB");
add("PLC");
add("FUT");
add("10E");
add("MED");
add("LRW");
add("EVG");
add("MOR");
add("SHM");
add("EVE");
add("DRB");
add("ME2");
add("ALA");
add("DD2");
add("CON");
add("DDC");
add("ARB");
add("M10");
// add("TD0"); // Magic Online Deck Series
add("V09");
add("HOP");
add("ME3");
add("ZEN");
add("DDD");
add("H09");
add("WWK");
add("DDE");
add("ROE");
add("DPA");
add("ARC");
add("M11");
add("V10");
add("DDF");
add("SOM");
// add("TD0"); // Commander Theme Decks
add("PD2");
add("ME4");
add("MBS");
add("DDG");
add("NPH");
add("CMD");
add("M12");
add("V11");
add("DDH");
add("ISD");
add("PD3");
add("DKA");
add("DDI");
add("AVR");
add("PC2");
add("M13");
add("V12");
add("DDJ");
add("RTR");
add("CM1");
// add("TD2"); // Duel Decks: Mirrodin Pure vs. New Phyrexia
add("GTC");
add("DDK");
add("DGM");
add("MMA");
add("M14");
add("V13");
add("DDL");
add("THS");
add("C13");
add("BNG");
add("DDM");
add("JOU");
// add("MD1"); // Modern Event Deck
add("CNS");
add("VMA");
add("M15");
add("V14");
add("DDN");
add("KTK");
add("C14");
// add("DD3"); // Duel Decks Anthology
add("FRF");
add("DDO");
add("DTK");
add("TPR");
add("MM2");
add("ORI");
add("V15");
add("DDP");
add("BFZ");
add("EXP");
add("C15");
// add("PZ1"); // Legendary Cube
add("OGW");
add("DDQ");
add("W16");
add("SOI");
add("EMA");
add("EMN");
add("V16");
add("CN2");
add("DDR");
add("KLD");
add("MPS");
// add("PZ2"); // Treasure Chests
add("C16");
add("PCA");
add("AER");
add("MM3");
add("DDS");
add("W17");
add("AKH");
add("MPS");
add("CMA");
add("E01");
add("HOU");
add("C17");
// add("XLN");
// add("DDT");
// add("IMA");
// add("E02");
// add("V17");
// add("UST");
// add("RIX");
// add("A25");
// add("DOM");
// add("M19");
}
};
private static final Map<String, String> setNameTokenReplacement = new HashMap<String, String>() { private static final Map<String, String> setNameTokenReplacement = new HashMap<String, String>() {
{ {
put("10E", "tenth-edition"); put("10E", "tenth-edition");
@ -146,12 +345,11 @@ public enum MagicCardsImageSource implements CardImageSource {
return "magiccards.info"; return "magiccards.info";
} }
@Override @Override
public String getNextHttpImageUrl() { public String getNextHttpImageUrl() {
return null; return null;
} }
@Override @Override
public String getFileForHttpImage(String httpImageUrl) { public String getFileForHttpImage(String httpImageUrl) {
return null; return null;
@ -210,18 +408,26 @@ public enum MagicCardsImageSource implements CardImageSource {
public float getAverageSize() { public float getAverageSize() {
return 70.0f; return 70.0f;
} }
@Override @Override
public int getTotalImages() { public int getTotalImages() {
return -1; return -1;
} }
@Override @Override
public boolean isTokenSource() { public boolean isTokenSource() {
return true; return true;
} }
@Override
public ArrayList<String> getSupportedSets() {
ArrayList<String> supportedSetsCopy = new ArrayList<>();
supportedSetsCopy.addAll(supportedSets);
return supportedSetsCopy;
}
@Override @Override
public void doPause(String httpImageUrl) { public void doPause(String httpImageUrl) {
} }
} }

View file

@ -24,20 +24,221 @@
* The views and conclusions contained in the software and documentation are those of the * The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package org.mage.plugins.card.dl.sources; package org.mage.plugins.card.dl.sources;
import org.mage.plugins.card.images.CardDownloadData;
import java.net.URI; import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.mage.plugins.card.images.CardDownloadData;
/** /**
* *
* @author Pete Rossi * @author Pete Rossi
*/ */
public enum MagidexImageSource implements CardImageSource {
instance;
public enum MagidexImageSource implements CardImageSource { private final Set<String> supportedSets;
instance;
MagidexImageSource() {
supportedSets = new LinkedHashSet<>();
// supportedSets.add("PTC"); // Prerelease Events
// supportedSets.add("FNMP");
supportedSets.add("JR");
supportedSets.add("LEA");
supportedSets.add("LEB");
supportedSets.add("2ED");
supportedSets.add("ARN");
supportedSets.add("ATQ");
supportedSets.add("3ED");
supportedSets.add("LEG");
supportedSets.add("DRK");
supportedSets.add("FEM");
supportedSets.add("4ED");
supportedSets.add("ICE");
supportedSets.add("CHR");
supportedSets.add("HML");
supportedSets.add("ALL");
supportedSets.add("MIR");
supportedSets.add("VIS");
supportedSets.add("5ED");
supportedSets.add("POR");
supportedSets.add("WTH");
supportedSets.add("TMP");
supportedSets.add("STH");
supportedSets.add("EXO");
supportedSets.add("P02");
supportedSets.add("UGL");
supportedSets.add("USG");
supportedSets.add("DD3DVD");
supportedSets.add("DD3EVG");
supportedSets.add("DD3GVL");
supportedSets.add("DD3JVC");
supportedSets.add("ULG");
supportedSets.add("6ED");
supportedSets.add("UDS");
supportedSets.add("PTK");
supportedSets.add("S99");
supportedSets.add("MMQ");
// supportedSets.add("BRB");Battle Royale Box Set
supportedSets.add("NEM");
supportedSets.add("S00");
supportedSets.add("PCY");
supportedSets.add("INV");
// supportedSets.add("BTD"); // Beatdown Boxset
supportedSets.add("PLS");
supportedSets.add("7ED");
supportedSets.add("APC");
supportedSets.add("ODY");
// supportedSets.add("DKM"); // Deckmasters 2001
supportedSets.add("TOR");
supportedSets.add("JUD");
supportedSets.add("ONS");
supportedSets.add("LGN");
supportedSets.add("SCG");
supportedSets.add("8ED");
supportedSets.add("MRD");
supportedSets.add("DST");
supportedSets.add("5DN");
supportedSets.add("CHK");
supportedSets.add("UNH");
supportedSets.add("BOK");
supportedSets.add("SOK");
supportedSets.add("9ED");
supportedSets.add("RAV");
supportedSets.add("GPT");
supportedSets.add("DIS");
supportedSets.add("CSP");
supportedSets.add("TSP");
supportedSets.add("TSB");
supportedSets.add("PLC");
supportedSets.add("FUT");
supportedSets.add("10E");
supportedSets.add("MED");
supportedSets.add("LRW");
supportedSets.add("EVG");
supportedSets.add("MOR");
supportedSets.add("SHM");
supportedSets.add("EVE");
supportedSets.add("DRB");
supportedSets.add("ME2");
supportedSets.add("ALA");
supportedSets.add("DD2");
supportedSets.add("CON");
supportedSets.add("DDC");
supportedSets.add("ARB");
supportedSets.add("M10");
// supportedSets.add("TD0"); // Magic Online Deck Series
supportedSets.add("V09");
supportedSets.add("HOP");
supportedSets.add("ME3");
supportedSets.add("ZEN");
supportedSets.add("DDD");
supportedSets.add("H09");
supportedSets.add("WWK");
supportedSets.add("DDE");
supportedSets.add("ROE");
supportedSets.add("DPA");
supportedSets.add("ARC");
supportedSets.add("M11");
supportedSets.add("V10");
supportedSets.add("DDF");
supportedSets.add("SOM");
// supportedSets.add("TD0"); // Commander Theme Decks
supportedSets.add("PD2");
supportedSets.add("ME4");
supportedSets.add("MBS");
supportedSets.add("DDG");
supportedSets.add("NPH");
supportedSets.add("CMD");
supportedSets.add("M12");
supportedSets.add("V11");
supportedSets.add("DDH");
supportedSets.add("ISD");
supportedSets.add("PD3");
supportedSets.add("DKA");
supportedSets.add("DDI");
supportedSets.add("AVR");
supportedSets.add("PC2");
supportedSets.add("M13");
supportedSets.add("V12");
supportedSets.add("DDJ");
supportedSets.add("RTR");
supportedSets.add("CM1");
// supportedSets.add("TD2"); // Duel Decks: Mirrodin Pure vs. New Phyrexia
supportedSets.add("GTC");
supportedSets.add("DDK");
supportedSets.add("DGM");
supportedSets.add("MMA");
supportedSets.add("M14");
supportedSets.add("V13");
supportedSets.add("DDL");
supportedSets.add("THS");
supportedSets.add("C13");
supportedSets.add("BNG");
supportedSets.add("DDM");
supportedSets.add("JOU");
// supportedSets.add("MD1"); // Modern Event Deck
supportedSets.add("CNS");
supportedSets.add("VMA");
supportedSets.add("M15");
supportedSets.add("V14");
supportedSets.add("DDN");
supportedSets.add("KTK");
supportedSets.add("C14");
// supportedSets.add("DD3"); // Duel Decks Anthology
supportedSets.add("FRF");
supportedSets.add("DDO");
supportedSets.add("DTK");
supportedSets.add("TPR");
supportedSets.add("MM2");
supportedSets.add("ORI");
supportedSets.add("V15");
supportedSets.add("DDP");
supportedSets.add("BFZ");
supportedSets.add("EXP");
supportedSets.add("C15");
// supportedSets.add("PZ1"); // Legendary Cube
supportedSets.add("OGW");
supportedSets.add("DDQ");
supportedSets.add("W16");
supportedSets.add("SOI");
supportedSets.add("EMA");
supportedSets.add("EMN");
supportedSets.add("V16");
supportedSets.add("CN2");
supportedSets.add("DDR");
supportedSets.add("KLD");
supportedSets.add("MPS");
// supportedSets.add("PZ2"); // Treasure Chests
supportedSets.add("C16");
supportedSets.add("PCA");
supportedSets.add("AER");
supportedSets.add("MM3");
supportedSets.add("DDS");
supportedSets.add("W17");
supportedSets.add("AKH");
supportedSets.add("MPS");
supportedSets.add("CMA");
supportedSets.add("E01");
supportedSets.add("HOU");
supportedSets.add("C17");
// supportedSets.add("XLN");
// supportedSets.add("DDT");
// supportedSets.add("IMA");
// supportedSets.add("E02");
// supportedSets.add("V17");
// supportedSets.add("UST");
// supportedSets.add("RIX");
// supportedSets.add("A25");
// supportedSets.add("DOM");
// supportedSets.add("M19");
}
@Override @Override
public String getSourceName() { public String getSourceName() {
@ -68,10 +269,23 @@ public enum MagidexImageSource implements CardImageSource {
} }
// This will properly escape the url // This will properly escape the url
URI uri = new URI("http", "magidex.com", "/extstatic/card/" + cardSet + '/' + cardDownloadName + ".jpg", null, null); URI uri = new URI("http", "magidex.com", "/extstatic/card/" + formatSetName(cardSet) + '/' + cardDownloadName + ".jpg", null, null);
return uri.toASCIIString(); return uri.toASCIIString();
} }
private String formatSetName(String setName) {
if (setNameReplacement.containsKey(setName)) {
setName = setNameReplacement.get(setName);
}
return setName;
}
private static final Map<String, String> setNameReplacement = new HashMap<String, String>() {
{
put("JR", "pJGP");
}
};
@Override @Override
public String generateTokenUrl(CardDownloadData card) { public String generateTokenUrl(CardDownloadData card) {
return null; return null;
@ -95,4 +309,11 @@ public enum MagidexImageSource implements CardImageSource {
@Override @Override
public void doPause(String httpImageUrl) { public void doPause(String httpImageUrl) {
} }
@Override
public ArrayList<String> getSupportedSets() {
ArrayList<String> supportedSetsCopy = new ArrayList<>();
supportedSetsCopy.addAll(supportedSets);
return supportedSetsCopy;
}
} }

View file

@ -27,11 +27,10 @@
*/ */
package org.mage.plugins.card.dl.sources; package org.mage.plugins.card.dl.sources;
import org.apache.log4j.Logger;
import org.mage.plugins.card.images.CardDownloadData;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import org.apache.log4j.Logger;
import org.mage.plugins.card.images.CardDownloadData;
/** /**
* *
@ -368,13 +367,19 @@ public enum MtgOnlTokensImageSource implements CardImageSource {
} }
return -1; return -1;
} }
@Override @Override
public boolean isTokenSource() { public boolean isTokenSource() {
return true; return true;
} }
@Override @Override
public void doPause(String httpImageUrl) { public void doPause(String httpImageUrl) {
} }
@Override
public boolean isImageProvided(String setCode, String cardName) {
return true;
}
} }

View file

@ -35,9 +35,11 @@ import java.net.InetSocketAddress;
import java.net.Proxy; import java.net.Proxy;
import java.net.URL; import java.net.URL;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
@ -57,10 +59,12 @@ import org.mage.plugins.card.images.CardDownloadData;
public enum MythicspoilerComSource implements CardImageSource { public enum MythicspoilerComSource implements CardImageSource {
instance; instance;
private Map<String, String> setsAliases; private final Map<String, String> setsAliases;
private Map<String, String> cardNameAliases; private final Map<String, String> cardNameAliases;
private Map<String, Set<String>> cardNameAliasesStart; private final Map<String, Set<String>> cardNameAliasesStart;
private final Map<String, Map<String, String>> sets; private final Map<String, Map<String, String>> sets;
private final Set<String> supportedSets;
private final Map<String, Map<String, String>> manualLinks;
@Override @Override
public String getSourceName() { public String getSourceName() {
@ -68,6 +72,188 @@ public enum MythicspoilerComSource implements CardImageSource {
} }
MythicspoilerComSource() { MythicspoilerComSource() {
supportedSets = new LinkedHashSet<>();
// supportedSets.add("LEA");
supportedSets.add("LEB");
// supportedSets.add("2ED");
supportedSets.add("ARN");
supportedSets.add("ATQ");
supportedSets.add("3ED");
supportedSets.add("LEG");
supportedSets.add("DRK");
supportedSets.add("FEM");
// supportedSets.add("4ED");
supportedSets.add("ICE");
supportedSets.add("CHR");
supportedSets.add("HML");
supportedSets.add("ALL");
supportedSets.add("MIR");
supportedSets.add("VIS");
// supportedSets.add("5ED");
// supportedSets.add("POR");
supportedSets.add("WTH");
supportedSets.add("TMP");
supportedSets.add("STH");
supportedSets.add("EXO");
// supportedSets.add("P02");
// supportedSets.add("UGL");
supportedSets.add("USG");
// supportedSets.add("DD3DVD");
// supportedSets.add("DD3EVG");
// supportedSets.add("DD3GVL");
// supportedSets.add("DD3JVC");
// supportedSets.add("ULG");
// supportedSets.add("6ED");
supportedSets.add("UDS");
supportedSets.add("PTK");
// supportedSets.add("S99");
supportedSets.add("MMQ");
// supportedSets.add("BRB");Battle Royale Box Set
supportedSets.add("NEM");
// supportedSets.add("S00");
supportedSets.add("PCY");
supportedSets.add("INV");
// supportedSets.add("BTD"); // Beatdown Boxset
supportedSets.add("PLS");
supportedSets.add("7ED");
supportedSets.add("APC");
supportedSets.add("ODY");
// supportedSets.add("DKM"); // Deckmasters 2001
supportedSets.add("TOR");
supportedSets.add("JUD");
supportedSets.add("ONS");
supportedSets.add("LGN");
supportedSets.add("SCG");
supportedSets.add("8ED");
supportedSets.add("MRD");
supportedSets.add("DST");
supportedSets.add("5DN");
supportedSets.add("CHK");
supportedSets.add("UNH");
supportedSets.add("BOK");
supportedSets.add("SOK");
supportedSets.add("9ED");
supportedSets.add("RAV");
supportedSets.add("GPT");
supportedSets.add("DIS");
supportedSets.add("CSP");
supportedSets.add("TSP");
supportedSets.add("TSB");
supportedSets.add("PLC");
supportedSets.add("FUT");
supportedSets.add("10E");
supportedSets.add("MED");
supportedSets.add("LRW");
supportedSets.add("EVG");
supportedSets.add("MOR");
supportedSets.add("SHM");
supportedSets.add("EVE");
supportedSets.add("DRB");
// supportedSets.add("ME2");
supportedSets.add("ALA");
// supportedSets.add("DD2");
supportedSets.add("CON");
// supportedSets.add("DDC");
supportedSets.add("ARB");
supportedSets.add("M10");
// supportedSets.add("TD0"); // Magic Online Deck Series
// supportedSets.add("V09");
supportedSets.add("HOP");
// supportedSets.add("ME3");
supportedSets.add("ZEN");
// supportedSets.add("DDD");
supportedSets.add("H09");
supportedSets.add("WWK");
// supportedSets.add("DDE");
supportedSets.add("ROE");
supportedSets.add("DPA");
supportedSets.add("ARC");
supportedSets.add("M11");
// supportedSets.add("V10");
// supportedSets.add("DDF");
supportedSets.add("SOM");
// supportedSets.add("TD0"); // Commander Theme Decks
// supportedSets.add("PD2");
// supportedSets.add("ME4");
supportedSets.add("MBS");
// supportedSets.add("DDG");
supportedSets.add("NPH");
supportedSets.add("CMD");
supportedSets.add("M12");
supportedSets.add("V11");
// supportedSets.add("DDH");
supportedSets.add("ISD");
supportedSets.add("PD3");
supportedSets.add("DKA");
// supportedSets.add("DDI");
supportedSets.add("AVR");
// supportedSets.add("PC2");
supportedSets.add("M13");
// supportedSets.add("V12");
// supportedSets.add("DDJ");
supportedSets.add("RTR");
// supportedSets.add("CM1");
// supportedSets.add("TD2"); // Duel Decks: Mirrodin Pure vs. New Phyrexia
supportedSets.add("GTC");
// supportedSets.add("DDK");
supportedSets.add("DGM");
// supportedSets.add("MMA");
supportedSets.add("M14");
supportedSets.add("V13");
// supportedSets.add("DDL");
supportedSets.add("THS");
supportedSets.add("C13");
supportedSets.add("BNG");
// supportedSets.add("DDM");
supportedSets.add("JOU");
// supportedSets.add("MD1"); // Modern Event Deck
// supportedSets.add("CNS");
// supportedSets.add("VMA");
supportedSets.add("M15");
supportedSets.add("V14");
supportedSets.add("DDN");
supportedSets.add("KTK");
supportedSets.add("C14");
// supportedSets.add("DD3"); // Duel Decks Anthology
supportedSets.add("FRF");
// supportedSets.add("DDO");
// supportedSets.add("DTK");
// supportedSets.add("TPR");
supportedSets.add("MM2");
supportedSets.add("ORI");
// supportedSets.add("V15");
// supportedSets.add("DDP");
supportedSets.add("BFZ");
// supportedSets.add("EXP");
supportedSets.add("C15");
// supportedSets.add("PZ1"); // Legendary Cube
supportedSets.add("OGW");
// supportedSets.add("DDQ");
// supportedSets.add("W16");
supportedSets.add("SOI");
supportedSets.add("EMA");
supportedSets.add("EMN");
// supportedSets.add("V16");
supportedSets.add("CN2");
// supportedSets.add("DDR");
supportedSets.add("KLD");
// supportedSets.add("MPS");
// supportedSets.add("PZ2");
supportedSets.add("C16");
// supportedSets.add("PCA");
supportedSets.add("AER");
supportedSets.add("MM3");
// supportedSets.add("W17");
supportedSets.add("AKH");
supportedSets.add("MPS");
supportedSets.add("CMA");
supportedSets.add("E01");
supportedSets.add("HOU");
supportedSets.add("C17");
supportedSets.add("IMA");
supportedSets.add("XLN");
sets = new LinkedHashMap<>(); sets = new LinkedHashMap<>();
setsAliases = new HashMap<>(); setsAliases = new HashMap<>();
setsAliases.put("exp", "bfz"); setsAliases.put("exp", "bfz");
@ -88,6 +274,25 @@ public enum MythicspoilerComSource implements CardImageSource {
cardNameAliases.put("AKH-reducerumble", "reducerubble"); cardNameAliases.put("AKH-reducerumble", "reducerubble");
cardNameAliases.put("AKH-forsakethewordly", "forsaketheworldly"); cardNameAliases.put("AKH-forsakethewordly", "forsaketheworldly");
cardNameAliases.put("AKH-kefnatsmonument", "kefnetsmonument"); cardNameAliases.put("AKH-kefnatsmonument", "kefnetsmonument");
cardNameAliases.put("XLN-kinjaliscaller", "kinjalliscaller");
cardNameAliases.put("XLN-lookoutsdecision", "lookoutsdispersal");
cardNameAliases.put("XLN-infuriatedgladiodon", "ragingswordtooth");
cardNameAliases.put("XLN-redoubledvolley", "repeatingbarrage");
// <card name, card link>
manualLinks = new HashMap<>();
HashMap<String, String> links = new HashMap<>();
links.put("templeofaclazotz", "templeofaclazotz");
links.put("conquerorsfoothold", "conquerorsfoothold");
links.put("primalwellspring", "primalwellspring");
links.put("azcantathesunkenruin", "azcantathesunkenruin");
links.put("spiresoforazca", "spiresoforazca");
links.put("treasurecove", "treasurecove");
links.put("itlimoccradleofthesun", "itlimoccradleofthesun");
links.put("lostvale", "lostvale");
links.put("adantothefirstfort", "adantothefirstport");
links.put("spitfirebastion", "spitfirebastion");
manualLinks.put("XLN", links);
cardNameAliasesStart = new HashMap<>(); cardNameAliasesStart = new HashMap<>();
HashSet<String> names = new HashSet<>(); HashSet<String> names = new HashSet<>();
@ -184,7 +389,12 @@ public enum MythicspoilerComSource implements CardImageSource {
pageLinks.put(cardName, baseUrl + cardLink); pageLinks.put(cardName, baseUrl + cardLink);
} }
} }
Map<String, String> linksToAdd = manualLinks.get(cardSet);
if (linksToAdd != null) {
for (Map.Entry<String, String> link : linksToAdd.entrySet()) {
pageLinks.put(link.getKey(), baseUrl + "cards/" + link.getValue() + ".jpg");
}
}
return pageLinks; return pageLinks;
} }
@ -243,4 +453,12 @@ public enum MythicspoilerComSource implements CardImageSource {
@Override @Override
public void doPause(String httpImageUrl) { public void doPause(String httpImageUrl) {
} }
@Override
public ArrayList<String> getSupportedSets() {
ArrayList<String> supportedSetsCopy = new ArrayList<>();
supportedSetsCopy.addAll(supportedSets);
return supportedSetsCopy;
}
} }

View file

@ -1,21 +1,223 @@
package org.mage.plugins.card.dl.sources; package org.mage.plugins.card.dl.sources;
import org.mage.plugins.card.images.CardDownloadData; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.mage.plugins.card.images.CardDownloadData;
/** /**
* @author Quercitron * @author Quercitron
* *
*/ */
public enum ScryfallImageSource implements CardImageSource { public enum ScryfallImageSource implements CardImageSource {
instance; instance;
private final Set<String> supportedSets;
ScryfallImageSource() {
supportedSets = new LinkedHashSet<>();
// supportedSets.add("PTC"); //
supportedSets.add("LEA");
supportedSets.add("LEB");
supportedSets.add("2ED");
supportedSets.add("ARN");
supportedSets.add("ATQ");
supportedSets.add("3ED");
supportedSets.add("LEG");
supportedSets.add("DRK");
supportedSets.add("FEM");
supportedSets.add("4ED");
supportedSets.add("ICE");
supportedSets.add("CHR");
supportedSets.add("HML");
supportedSets.add("ALL");
supportedSets.add("MIR");
supportedSets.add("VIS");
supportedSets.add("5ED");
supportedSets.add("POR");
supportedSets.add("WTH");
supportedSets.add("TMP");
supportedSets.add("STH");
supportedSets.add("EXO");
supportedSets.add("P02");
supportedSets.add("UGL");
supportedSets.add("USG");
supportedSets.add("DD3DVD");
supportedSets.add("DD3EVG");
supportedSets.add("DD3GVL");
supportedSets.add("DD3JVC");
supportedSets.add("ULG");
supportedSets.add("6ED");
supportedSets.add("UDS");
supportedSets.add("PTK");
supportedSets.add("S99");
supportedSets.add("MMQ");
// supportedSets.add("BRB");Battle Royale Box Set
supportedSets.add("NEM");
supportedSets.add("S00");
supportedSets.add("PCY");
supportedSets.add("INV");
// supportedSets.add("BTD"); // Beatdown Boxset
supportedSets.add("PLS");
supportedSets.add("7ED");
supportedSets.add("APC");
supportedSets.add("ODY");
// supportedSets.add("DKM"); // Deckmasters 2001
supportedSets.add("TOR");
supportedSets.add("JUD");
supportedSets.add("ONS");
supportedSets.add("LGN");
supportedSets.add("SCG");
supportedSets.add("8ED");
supportedSets.add("MRD");
supportedSets.add("DST");
supportedSets.add("5DN");
supportedSets.add("CHK");
supportedSets.add("UNH");
supportedSets.add("BOK");
supportedSets.add("SOK");
supportedSets.add("9ED");
supportedSets.add("RAV");
supportedSets.add("GPT");
supportedSets.add("DIS");
supportedSets.add("CSP");
supportedSets.add("TSP");
supportedSets.add("TSB");
supportedSets.add("PLC");
supportedSets.add("FUT");
supportedSets.add("10E");
supportedSets.add("MED");
supportedSets.add("LRW");
supportedSets.add("EVG");
supportedSets.add("MOR");
supportedSets.add("SHM");
supportedSets.add("EVE");
supportedSets.add("DRB");
supportedSets.add("ME2");
supportedSets.add("ALA");
supportedSets.add("DD2");
supportedSets.add("CON");
supportedSets.add("DDC");
supportedSets.add("ARB");
supportedSets.add("M10");
// supportedSets.add("TD0"); // Magic Online Deck Series
supportedSets.add("V09");
supportedSets.add("HOP");
supportedSets.add("ME3");
supportedSets.add("ZEN");
supportedSets.add("DDD");
supportedSets.add("H09");
supportedSets.add("WWK");
supportedSets.add("DDE");
supportedSets.add("ROE");
supportedSets.add("DPA");
supportedSets.add("ARC");
supportedSets.add("M11");
supportedSets.add("V10");
supportedSets.add("DDF");
supportedSets.add("SOM");
// supportedSets.add("TD0"); // Commander Theme Decks
supportedSets.add("PD2");
supportedSets.add("ME4");
supportedSets.add("MBS");
supportedSets.add("DDG");
supportedSets.add("NPH");
supportedSets.add("CMD");
supportedSets.add("M12");
supportedSets.add("V11");
supportedSets.add("DDH");
supportedSets.add("ISD");
supportedSets.add("PD3");
supportedSets.add("DKA");
supportedSets.add("DDI");
supportedSets.add("AVR");
supportedSets.add("PC2");
supportedSets.add("M13");
supportedSets.add("V12");
supportedSets.add("DDJ");
supportedSets.add("RTR");
supportedSets.add("CM1");
// supportedSets.add("TD2"); // Duel Decks: Mirrodin Pure vs. New Phyrexia
supportedSets.add("GTC");
supportedSets.add("DDK");
supportedSets.add("DGM");
supportedSets.add("MMA");
supportedSets.add("M14");
supportedSets.add("V13");
supportedSets.add("DDL");
supportedSets.add("THS");
supportedSets.add("C13");
supportedSets.add("BNG");
supportedSets.add("DDM");
supportedSets.add("JOU");
// supportedSets.add("MD1"); // Modern Event Deck
supportedSets.add("CNS");
supportedSets.add("VMA");
supportedSets.add("M15");
supportedSets.add("V14");
supportedSets.add("DDN");
supportedSets.add("KTK");
supportedSets.add("C14");
// supportedSets.add("DD3"); // Duel Decks Anthology
supportedSets.add("FRF");
supportedSets.add("DDO");
supportedSets.add("DTK");
supportedSets.add("TPR");
supportedSets.add("MM2");
supportedSets.add("ORI");
supportedSets.add("V15");
supportedSets.add("DDP");
supportedSets.add("BFZ");
supportedSets.add("EXP");
supportedSets.add("C15");
// supportedSets.add("PZ1"); // Legendary Cube
supportedSets.add("OGW");
supportedSets.add("DDQ");
supportedSets.add("W16");
supportedSets.add("SOI");
supportedSets.add("EMA");
supportedSets.add("EMN");
supportedSets.add("V16");
supportedSets.add("CN2");
supportedSets.add("DDR");
supportedSets.add("KLD");
supportedSets.add("MPS");
// supportedSets.add("PZ2");
supportedSets.add("C16");
supportedSets.add("PCA");
supportedSets.add("AER");
supportedSets.add("MM3");
supportedSets.add("DDS");
supportedSets.add("W17");
supportedSets.add("AKH");
supportedSets.add("MPS");
supportedSets.add("CMA");
supportedSets.add("E01");
supportedSets.add("HOU");
supportedSets.add("C17");
supportedSets.add("XLN");
// supportedSets.add("DDT");
supportedSets.add("IMA");
// supportedSets.add("E02");
// supportedSets.add("V17");
// supportedSets.add("UST");
// supportedSets.add("RIX");
// supportedSets.add("A25");
// supportedSets.add("DOM");
// supportedSets.add("M19");
}
@Override @Override
public String generateURL(CardDownloadData card) throws Exception { public String generateURL(CardDownloadData card) throws Exception {
return "https://api.scryfall.com/cards/" + formatSetName(card.getSet()) + "/" + card.getCollectorId() + "?format=image"; return "https://api.scryfall.com/cards/" + formatSetName(card.getSet()) + "/"
+ card.getCollectorId()
+ (card.isSecondSide() ? "b" : "")
+ "?format=image";
} }
@Override @Override
@ -40,7 +242,7 @@ public enum ScryfallImageSource implements CardImageSource {
@Override @Override
public float getAverageSize() { public float getAverageSize() {
return 240; return 90;
} }
@Override @Override
@ -75,4 +277,11 @@ public enum ScryfallImageSource implements CardImageSource {
put("MBP", "pmei"); put("MBP", "pmei");
} }
}; };
@Override
public ArrayList<String> getSupportedSets() {
ArrayList<String> supportedSetsCopy = new ArrayList<>();
supportedSetsCopy.addAll(supportedSets);
return supportedSetsCopy;
}
} }

View file

@ -27,9 +27,6 @@
*/ */
package org.mage.plugins.card.dl.sources; package org.mage.plugins.card.dl.sources;
import org.apache.log4j.Logger;
import org.mage.plugins.card.images.CardDownloadData;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -37,8 +34,14 @@ import java.io.InputStreamReader;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import org.apache.log4j.Logger;
import org.mage.plugins.card.images.CardDownloadData;
import org.mage.plugins.card.images.DownloadPictures;
/** /**
* *
@ -46,11 +49,12 @@ import java.util.Map;
*/ */
public enum TokensMtgImageSource implements CardImageSource { public enum TokensMtgImageSource implements CardImageSource {
instance; instance;
private static final Logger logger = Logger.getLogger(TokensMtgImageSource.class); private static final Logger logger = Logger.getLogger(TokensMtgImageSource.class);
// [[EXP/Name, TokenData>
private List<TokenData> tokensData; private HashMap<String, ArrayList<TokenData>> tokensData;
private static final Set<String> supportedSets = new LinkedHashSet<String>();
private final Object tokensDataSync = new Object(); private final Object tokensDataSync = new Object();
@ -100,7 +104,12 @@ public enum TokensMtgImageSource implements CardImageSource {
"Sorin", "Sorin",
"Tamiyo", "Tamiyo",
"Teferi", "Teferi",
"Venser",}; "Venser",
// Custom Emblems
"Yoda",
"Obi-Wan Kenobi",
"Aurra Sing"
};
private static final Map<String, String> SET_NAMES_REPLACEMENT = new HashMap<String, String>() { private static final Map<String, String> SET_NAMES_REPLACEMENT = new HashMap<String, String>() {
{ {
@ -134,36 +143,31 @@ public enum TokensMtgImageSource implements CardImageSource {
// e.g. http://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg -- token number 010 // e.g. http://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg -- token number 010
// We don't know these numbers, but we can take them from a file // We don't know these numbers, but we can take them from a file
// with tokens information that can be downloaded from the site. // with tokens information that can be downloaded from the site.
List<TokenData> newTokensData = getTokensData(); if (tokensData.isEmpty()) {
logger.info("Source " + getSourceName() + " provides no token data.");
if (newTokensData.isEmpty()) {
return null; return null;
} }
List<TokenData> matchedTokens = new ArrayList<>(); String key = set + "/" + name;
for (TokenData token : newTokensData) { List<TokenData> list = tokensData.get(key);
if (name.equalsIgnoreCase(token.getName()) && set.equalsIgnoreCase(token.getExpansionSetCode())) { if (list == null) {
matchedTokens.add(token); logger.info("Could not find data for token " + name + ", set " + set + ".");
} return null;
} }
//
// if (matchedTokens.isEmpty()) {
// logger.info("Could not find data for token " + name + ", set " + set + ".");
// return null;
// }
TokenData tokenData; TokenData tokenData;
if (type == 0) { if (type == 0) {
if (matchedTokens.size() > 1) { if (list.size() > 1) {
logger.info("Multiple images were found for token " + name + ", set " + set + '.'); logger.info("Multiple images were found for token " + name + ", set " + set + '.');
} }
tokenData = matchedTokens.get(0); logger.info("Token found: " + name + ", set " + set + '.');
tokenData = list.get(0);
} else { } else {
if (type > matchedTokens.size()) { if (type > list.size()) {
logger.warn("Not enough images for token with type " + type + ", name " + name + ", set " + set + '.'); logger.warn("Not enough images for token with type " + type + ", name " + name + ", set " + set + '.');
return null; return null;
} }
tokenData = matchedTokens.get(card.getType() - 1); tokenData = list.get(card.getType() - 1);
} }
String url = "http://tokens.mtg.onl/tokens/" + tokenData.getExpansionSetCode().trim() + '_' String url = "http://tokens.mtg.onl/tokens/" + tokenData.getExpansionSetCode().trim() + '_'
@ -172,15 +176,26 @@ public enum TokensMtgImageSource implements CardImageSource {
return url; return url;
} }
private List<TokenData> getTokensData() throws IOException { private HashMap<String, ArrayList<TokenData>> getTokensData() throws IOException {
synchronized (tokensDataSync) { synchronized (tokensDataSync) {
if (tokensData == null) { if (tokensData == null) {
tokensData = new ArrayList<>(); DownloadPictures.getInstance().updateAndViewMessage("Creating token data...");
tokensData = new HashMap<>();
// get tokens data from resource file // get tokens data from resource file
try(InputStream inputStream = this.getClass().getResourceAsStream("/tokens-mtg-onl-list.csv")) { try (InputStream inputStream = this.getClass().getResourceAsStream("/tokens-mtg-onl-list.csv")) {
List<TokenData> fileTokensData = parseTokensData(inputStream); List<TokenData> fileTokensData = parseTokensData(inputStream);
tokensData.addAll(fileTokensData); for (TokenData tokenData : fileTokensData) {
String key = tokenData.getExpansionSetCode() + "/" + tokenData.getName();
ArrayList<TokenData> list = tokensData.get(key);
if (list == null) {
list = new ArrayList<>();
tokensData.put(key, list);
supportedSets.add(tokenData.getExpansionSetCode());
logger.info("Added key: " + key);
}
list.add(tokenData);
}
} catch (Exception exception) { } catch (Exception exception) {
logger.warn("Failed to get tokens description from resource file tokens-mtg-onl-list.csv", exception); logger.warn("Failed to get tokens description from resource file tokens-mtg-onl-list.csv", exception);
} }
@ -188,27 +203,33 @@ public enum TokensMtgImageSource implements CardImageSource {
// description on site may contain new information // description on site may contain new information
// try to add it // try to add it
URL url = new URL("http://tokens.mtg.onl/data/SetsWithTokens.csv"); URL url = new URL("http://tokens.mtg.onl/data/SetsWithTokens.csv");
try(InputStream inputStream = url.openStream()) { try (InputStream inputStream = url.openStream()) {
List<TokenData> siteTokensData = parseTokensData(inputStream); List<TokenData> siteTokensData = parseTokensData(inputStream);
List<TokenData> newTokensData = new ArrayList<>();
for (TokenData siteData : siteTokensData) { for (TokenData siteData : siteTokensData) {
boolean isNew = true; String key = siteData.getExpansionSetCode() + "/" + siteData.getName();
for (TokenData fileData : tokensData) { supportedSets.add(siteData.getExpansionSetCode());
if (siteData.getName().equalsIgnoreCase(fileData.getName()) ArrayList<TokenData> list = tokensData.get(key);
&& siteData.getNumber().equalsIgnoreCase(fileData.getNumber()) if (list == null) {
&& siteData.getExpansionSetCode().equalsIgnoreCase(fileData.getExpansionSetCode())) { list = new ArrayList<>();
isNew = false; tokensData.put(key, list);
break; list.add(siteData);
} else {
boolean newToken = true;
for (TokenData tokenData : list) {
if (siteData.getNumber().equals(tokenData.number)) {
newToken = false;
break;
}
}
if (newToken) {
list.add(siteData);
} }
} }
if (isNew) {
newTokensData.add(siteData);
}
} }
DownloadPictures.getInstance().updateAndViewMessage("");
tokensData.addAll(newTokensData); } catch (Exception ex) {
} catch (Exception exception) { logger.warn("Failed to get tokens description from tokens.mtg.onl", ex);
logger.warn("Failed to get tokens description from tokens.mtg.onl", exception); DownloadPictures.getInstance().updateAndViewMessage(ex.getMessage());
} }
} }
} }
@ -219,8 +240,8 @@ public enum TokensMtgImageSource implements CardImageSource {
private List<TokenData> parseTokensData(InputStream inputStream) throws IOException { private List<TokenData> parseTokensData(InputStream inputStream) throws IOException {
List<TokenData> newTokensData = new ArrayList<>(); List<TokenData> newTokensData = new ArrayList<>();
try(InputStreamReader inputReader = new InputStreamReader(inputStream, "Cp1252"); 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 // we have to specify encoding to read special comma
reader.readLine(); // skip header reader.readLine(); // skip header
@ -285,7 +306,17 @@ public enum TokensMtgImageSource implements CardImageSource {
@Override @Override
public int getTotalImages() { public int getTotalImages() {
return -1; return getTokenImages();
}
@Override
public int getTokenImages() {
try {
getTokensData();
} catch (IOException ex) {
logger.error(getSourceName() + ": Loading available data failed. " + ex.getMessage());
}
return tokensData.size();
} }
@Override @Override
@ -296,4 +327,28 @@ public enum TokensMtgImageSource implements CardImageSource {
@Override @Override
public void doPause(String httpImageUrl) { public void doPause(String httpImageUrl) {
} }
@Override
public ArrayList<String> getSupportedSets() {
ArrayList<String> supportedSetsCopy = new ArrayList<>();
supportedSetsCopy.addAll(supportedSets);
return supportedSetsCopy;
}
@Override
public boolean isImageProvided(String setCode, String cardName) {
try {
getTokensData();
} catch (IOException ex) {
java.util.logging.Logger.getLogger(TokensMtgImageSource.class.getName()).log(Level.SEVERE, null, ex);
}
String key = setCode + "/" + cardName;
return (tokensData.containsKey(key));
}
@Override
public boolean isSetSupportedComplete(String setCode) {
return false;
}
} }

View file

@ -34,10 +34,13 @@ import java.net.HttpURLConnection;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Proxy; import java.net.Proxy;
import java.net.URL; import java.net.URL;
import java.net.URLEncoder; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.Set;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -46,6 +49,7 @@ import mage.client.MageFrame;
import mage.client.dialog.PreferencesDialog; import mage.client.dialog.PreferencesDialog;
import mage.remote.Connection; import mage.remote.Connection;
import mage.remote.Connection.ProxyType; import mage.remote.Connection.ProxyType;
import org.apache.log4j.Logger;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
@ -58,10 +62,13 @@ import org.mage.plugins.card.images.CardDownloadData;
public enum WizardCardsImageSource implements CardImageSource { public enum WizardCardsImageSource implements CardImageSource {
instance; instance;
private Map<String, String> setsAliases;
private Map<String, String> languageAliases;
private final Map<String, Map<String, String>> sets;
private static final Logger logger = Logger.getLogger(WizardCardsImageSource.class);
private final Map<String, String> setsAliases;
private final Map<String, String> languageAliases;
private final Map<String, Map<String, String>> sets;
private final Set<String> supportedSets;
@Override @Override
public String getSourceName() { public String getSourceName() {
@ -69,6 +76,198 @@ public enum WizardCardsImageSource implements CardImageSource {
} }
WizardCardsImageSource() { WizardCardsImageSource() {
supportedSets = new LinkedHashSet<>();
// supportedSets.add("PTC"); // Prerelease Events
supportedSets.add("LEA");
supportedSets.add("LEB");
supportedSets.add("2ED");
supportedSets.add("ARN");
supportedSets.add("ATQ");
supportedSets.add("3ED");
supportedSets.add("LEG");
supportedSets.add("DRK");
supportedSets.add("FEM");
supportedSets.add("4ED");
supportedSets.add("ICE");
supportedSets.add("CHR");
supportedSets.add("HML");
supportedSets.add("ALL");
supportedSets.add("MIR");
supportedSets.add("VIS");
supportedSets.add("5ED");
supportedSets.add("POR");
supportedSets.add("WTH");
supportedSets.add("TMP");
supportedSets.add("STH");
supportedSets.add("EXO");
supportedSets.add("P02");
supportedSets.add("UGL");
supportedSets.add("USG");
supportedSets.add("DD3DVD");
supportedSets.add("DD3EVG");
supportedSets.add("DD3GVL");
supportedSets.add("DD3JVC");
supportedSets.add("ULG");
supportedSets.add("6ED");
supportedSets.add("UDS");
supportedSets.add("PTK");
supportedSets.add("S99");
supportedSets.add("MMQ");
// supportedSets.add("BRB");Battle Royale Box Set
supportedSets.add("NEM");
supportedSets.add("S00");
supportedSets.add("PCY");
supportedSets.add("INV");
// supportedSets.add("BTD"); // Beatdown Boxset
supportedSets.add("PLS");
supportedSets.add("7ED");
supportedSets.add("APC");
supportedSets.add("ODY");
// supportedSets.add("DKM"); // Deckmasters 2001
supportedSets.add("TOR");
supportedSets.add("JUD");
supportedSets.add("ONS");
supportedSets.add("LGN");
supportedSets.add("SCG");
supportedSets.add("8ED");
supportedSets.add("MRD");
supportedSets.add("DST");
supportedSets.add("5DN");
supportedSets.add("CHK");
supportedSets.add("UNH");
supportedSets.add("BOK");
supportedSets.add("SOK");
supportedSets.add("9ED");
supportedSets.add("RAV");
supportedSets.add("GPT");
supportedSets.add("DIS");
supportedSets.add("CSP");
supportedSets.add("TSP");
supportedSets.add("TSB");
supportedSets.add("PLC");
supportedSets.add("FUT");
supportedSets.add("10E");
supportedSets.add("MED");
supportedSets.add("LRW");
supportedSets.add("EVG");
supportedSets.add("MOR");
supportedSets.add("SHM");
supportedSets.add("EVE");
supportedSets.add("DRB");
supportedSets.add("ME2");
supportedSets.add("ALA");
supportedSets.add("DD2");
supportedSets.add("CON");
supportedSets.add("DDC");
supportedSets.add("ARB");
supportedSets.add("M10");
// supportedSets.add("TD0"); // Magic Online Deck Series
supportedSets.add("V09");
supportedSets.add("HOP");
supportedSets.add("ME3");
supportedSets.add("ZEN");
supportedSets.add("DDD");
supportedSets.add("H09");
supportedSets.add("WWK");
supportedSets.add("DDE");
supportedSets.add("ROE");
supportedSets.add("DPA");
supportedSets.add("ARC");
supportedSets.add("M11");
supportedSets.add("V10");
supportedSets.add("DDF");
supportedSets.add("SOM");
// supportedSets.add("TD0"); // Commander Theme Decks
supportedSets.add("PD2");
supportedSets.add("ME4");
supportedSets.add("MBS");
supportedSets.add("DDG");
supportedSets.add("NPH");
supportedSets.add("CMD");
supportedSets.add("M12");
supportedSets.add("V11");
supportedSets.add("DDH");
supportedSets.add("ISD");
supportedSets.add("PD3");
supportedSets.add("DKA");
supportedSets.add("DDI");
supportedSets.add("AVR");
supportedSets.add("PC2");
supportedSets.add("M13");
supportedSets.add("V12");
supportedSets.add("DDJ");
supportedSets.add("RTR");
supportedSets.add("CM1");
// supportedSets.add("TD2"); // Duel Decks: Mirrodin Pure vs. New Phyrexia
supportedSets.add("GTC");
supportedSets.add("DDK");
supportedSets.add("DGM");
supportedSets.add("MMA");
supportedSets.add("M14");
supportedSets.add("V13");
supportedSets.add("DDL");
supportedSets.add("THS");
supportedSets.add("C13");
supportedSets.add("BNG");
supportedSets.add("DDM");
supportedSets.add("JOU");
// supportedSets.add("MD1"); // Modern Event Deck
supportedSets.add("CNS");
supportedSets.add("VMA");
supportedSets.add("M15");
supportedSets.add("V14");
supportedSets.add("DDN");
supportedSets.add("KTK");
supportedSets.add("C14");
// supportedSets.add("DD3"); // Duel Decks Anthology
supportedSets.add("FRF");
supportedSets.add("DDO");
supportedSets.add("DTK");
supportedSets.add("TPR");
supportedSets.add("MM2");
supportedSets.add("ORI");
supportedSets.add("V15");
supportedSets.add("DDP");
supportedSets.add("BFZ");
supportedSets.add("EXP");
supportedSets.add("C15");
// supportedSets.add("PZ1"); // Legendary Cube
supportedSets.add("OGW");
supportedSets.add("DDQ");
supportedSets.add("W16");
supportedSets.add("SOI");
supportedSets.add("EMA");
supportedSets.add("EMN");
supportedSets.add("V16");
supportedSets.add("CN2");
supportedSets.add("DDR");
supportedSets.add("KLD");
supportedSets.add("MPS");
// supportedSets.add("PZ2"); // Treasure Chests
supportedSets.add("C16");
supportedSets.add("PCA");
supportedSets.add("AER");
supportedSets.add("MM3");
supportedSets.add("DDS");
supportedSets.add("W17");
supportedSets.add("AKH");
supportedSets.add("MPS");
supportedSets.add("CMA");
supportedSets.add("E01");
supportedSets.add("HOU");
supportedSets.add("C17");
// supportedSets.add("XLN");
// supportedSets.add("DDT");
// supportedSets.add("IMA");
// supportedSets.add("E02");
// supportedSets.add("V17");
// supportedSets.add("UST");
// supportedSets.add("RIX");
// supportedSets.add("A25");
// supportedSets.add("DOM");
// supportedSets.add("M19");
sets = new HashMap<>(); sets = new HashMap<>();
setsAliases = new HashMap<>(); setsAliases = new HashMap<>();
setsAliases.put("2ED", "Unlimited Edition"); setsAliases.put("2ED", "Unlimited Edition");
@ -102,6 +301,7 @@ public enum WizardCardsImageSource implements CardImageSource {
setsAliases.put("C14", "Commander 2014"); setsAliases.put("C14", "Commander 2014");
setsAliases.put("C15", "Commander 2015"); setsAliases.put("C15", "Commander 2015");
setsAliases.put("C16", "Commander 2016"); setsAliases.put("C16", "Commander 2016");
setsAliases.put("C17", "Commander 2017");
setsAliases.put("CMA", "Commander Anthology"); setsAliases.put("CMA", "Commander Anthology");
setsAliases.put("CHK", "Champions of Kamigawa"); setsAliases.put("CHK", "Champions of Kamigawa");
setsAliases.put("CHR", "Chronicles"); setsAliases.put("CHR", "Chronicles");
@ -140,6 +340,7 @@ public enum WizardCardsImageSource implements CardImageSource {
setsAliases.put("DRK", "The Dark"); setsAliases.put("DRK", "The Dark");
setsAliases.put("DST", "Darksteel"); setsAliases.put("DST", "Darksteel");
setsAliases.put("DTK", "Dragons of Tarkir"); setsAliases.put("DTK", "Dragons of Tarkir");
setsAliases.put("E01", "Archenemy: Nicol Bolas");
setsAliases.put("EMN", "Eldritch Moon"); setsAliases.put("EMN", "Eldritch Moon");
setsAliases.put("EMA", "Eternal Masters"); setsAliases.put("EMA", "Eternal Masters");
setsAliases.put("EVE", "Eventide"); setsAliases.put("EVE", "Eventide");
@ -272,18 +473,20 @@ public enum WizardCardsImageSource implements CardImageSource {
} }
private Map<String, String> getSetLinks(String cardSet) { private Map<String, String> getSetLinks(String cardSet) {
ConcurrentHashMap<String, String> setLinks = new ConcurrentHashMap<>(); LinkedHashMap<String, String> setLinks = new LinkedHashMap<>();
ExecutorService executor = Executors.newFixedThreadPool(10); ExecutorService executor = Executors.newFixedThreadPool(10);
try { try {
String setNames = setsAliases.get(cardSet); String setNames = setsAliases.get(cardSet);
String preferedLanguage = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PREF_LANGUAGE, "en"); String preferedLanguage = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PREF_LANGUAGE, "en");
for (String setName : setNames.split("\\^")) { for (String setName : setNames.split("\\^")) {
String URLSetName = URLEncoder.encode(setName, "UTF-8"); // String URLSetName = URLEncoder.encode(setName, "UTF-8");
String URLSetName = setName.replaceAll(" ", "%20");
int page = 0; int page = 0;
int firstMultiverseIdLastPage = 0; int firstMultiverseIdLastPage = 0;
Pages: Pages:
while (page < 999) { while (page < 999) {
String searchUrl = "http://gatherer.wizards.com/Pages/Search/Default.aspx?page=" + page + "&output=spoiler&method=visual&action=advanced&set=+[%22" + URLSetName + "%22]"; String searchUrl = "http://gatherer.wizards.com/Pages/Search/Default.aspx?sort=cn+&page=" + page + "&action=advanced&output=spoiler&method=visual&set=+%5B%22" + URLSetName + "%22%5D";
logger.debug("URL: " + searchUrl);
Document doc = getDocument(searchUrl); Document doc = getDocument(searchUrl);
Elements cardsImages = doc.select("img[src^=../../Handlers/]"); Elements cardsImages = doc.select("img[src^=../../Handlers/]");
if (cardsImages.isEmpty()) { if (cardsImages.isEmpty()) {
@ -307,7 +510,7 @@ public enum WizardCardsImageSource implements CardImageSource {
} }
} }
} catch (IOException ex) { } catch (IOException ex) {
System.out.println("Exception when parsing the wizards page: " + ex.getMessage()); logger.error("Exception when parsing the wizards page: " + ex.getMessage());
} }
executor.shutdown(); executor.shutdown();
@ -327,14 +530,15 @@ public enum WizardCardsImageSource implements CardImageSource {
Connection.ProxyType proxyType = Connection.ProxyType.valueByText(prefs.get("proxyType", "None")); Connection.ProxyType proxyType = Connection.ProxyType.valueByText(prefs.get("proxyType", "None"));
Document doc; Document doc;
if (proxyType == ProxyType.NONE) { if (proxyType == ProxyType.NONE) {
doc = Jsoup.connect(urlString).get(); doc = Jsoup.connect(urlString).timeout(60 * 1000).get();
} else { } else {
String proxyServer = prefs.get("proxyAddress", ""); String proxyServer = prefs.get("proxyAddress", "");
int proxyPort = Integer.parseInt(prefs.get("proxyPort", "0")); int proxyPort = Integer.parseInt(prefs.get("proxyPort", "0"));
URL url = new URL(urlString); URL url = new URL(urlString);
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyServer, proxyPort)); Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyServer, proxyPort));
HttpURLConnection uc = (HttpURLConnection) url.openConnection(proxy); HttpURLConnection uc = (HttpURLConnection) url.openConnection(proxy);
uc.setConnectTimeout(10000);
uc.setReadTimeout(60000);
uc.connect(); uc.connect();
String line; String line;
@ -356,7 +560,7 @@ public enum WizardCardsImageSource implements CardImageSource {
if (!variations.isEmpty()) { if (!variations.isEmpty()) {
int landNumber = 1; int landNumber = 1;
for (Element variation : variations) { for (Element variation : variations) {
Integer landMultiverseId = Integer.parseInt(variation.attr("onclick").replaceAll("[^\\d]", "")); Integer landMultiverseId = Integer.parseInt(variation.attr("href").replaceAll("[^\\d]", ""));
links.put((cardName + landNumber).toLowerCase(), generateLink(landMultiverseId)); links.put((cardName + landNumber).toLowerCase(), generateLink(landMultiverseId));
landNumber++; landNumber++;
} }
@ -435,6 +639,9 @@ public enum WizardCardsImageSource implements CardImageSource {
String setNames = setsAliases.get(cardSet); String setNames = setsAliases.get(cardSet);
if (setNames != null) { if (setNames != null) {
Map<String, String> setLinks = sets.computeIfAbsent(cardSet, k -> getSetLinks(cardSet)); Map<String, String> setLinks = sets.computeIfAbsent(cardSet, k -> getSetLinks(cardSet));
if (setLinks == null || setLinks.isEmpty()) {
return null;
}
String link = setLinks.get(card.getDownloadName().toLowerCase()); String link = setLinks.get(card.getDownloadName().toLowerCase());
if (link == null) { if (link == null) {
int length = collectorId.length(); int length = collectorId.length();
@ -444,11 +651,11 @@ public enum WizardCardsImageSource implements CardImageSource {
} }
int number = Integer.parseInt(collectorId.substring(0, length)); int number = Integer.parseInt(collectorId.substring(0, length));
List<String> l = new ArrayList<>(setLinks.values());
if (setLinks.size() >= number) { if (l.size() >= number) {
link = setLinks.get(Integer.toString(number - 1)); link = l.get(number - 1);
} else { } else {;
link = setLinks.get(Integer.toString(number - 21)); link = l.get(number - 21);
if (link != null) { if (link != null) {
link = link.replace(Integer.toString(number - 20), (Integer.toString(number - 20) + 'a')); link = link.replace(Integer.toString(number - 20), (Integer.toString(number - 20) + 'a'));
} }
@ -460,6 +667,7 @@ public enum WizardCardsImageSource implements CardImageSource {
return link; return link;
} }
return null; return null;
} }
@Override @Override
@ -474,16 +682,24 @@ public enum WizardCardsImageSource implements CardImageSource {
private final class GetImageLinkTask implements Runnable { private final class GetImageLinkTask implements Runnable {
private final int multiverseId; private int multiverseId;
private final String cardName; private String cardName;
private final String preferedLanguage; private String preferedLanguage;
private final ConcurrentHashMap setLinks; private LinkedHashMap setLinks;
public GetImageLinkTask(int multiverseId, String cardName, String preferedLanguage, ConcurrentHashMap setLinks) { public GetImageLinkTask(int multiverseId, String cardName, String preferedLanguage, LinkedHashMap setLinks) {
this.multiverseId = multiverseId; try {
this.cardName = cardName; this.multiverseId = multiverseId;
this.preferedLanguage = preferedLanguage; this.cardName = cardName;
this.setLinks = setLinks; this.preferedLanguage = preferedLanguage;
this.setLinks = setLinks;
} catch (Exception ex) {
logger.error(ex.getMessage());
logger.error("multiverseId: " + multiverseId);
logger.error("cardName: " + cardName);
logger.error("preferedLanguage: " + preferedLanguage);
logger.error("setLinks: " + setLinks.toString());
}
} }
@Override @Override
@ -496,7 +712,7 @@ public enum WizardCardsImageSource implements CardImageSource {
setLinks.put(cardName.toLowerCase(), generateLink(preferedMultiverseId)); setLinks.put(cardName.toLowerCase(), generateLink(preferedMultiverseId));
} }
} catch (IOException | NumberFormatException ex) { } catch (IOException | NumberFormatException ex) {
System.out.println("Exception when parsing the wizards page: " + ex.getMessage()); logger.error("Exception when parsing the wizards page: " + ex.getMessage());
} }
} }
@ -515,4 +731,12 @@ public enum WizardCardsImageSource implements CardImageSource {
@Override @Override
public void doPause(String httpImageUrl) { public void doPause(String httpImageUrl) {
} }
@Override
public ArrayList<String> getSupportedSets() {
ArrayList<String> supportedSetsCopy = new ArrayList<>();
supportedSetsCopy.addAll(supportedSets);
return supportedSetsCopy;
}
} }

View file

@ -40,7 +40,7 @@ public class CardDownloadData {
} }
public CardDownloadData(String name, String set, String collectorId, boolean usesVariousArt, Integer type, String tokenSetCode, String tokenDescriptor, boolean token, boolean twoFacedCard, boolean secondSide) { public CardDownloadData(String name, String set, String collectorId, boolean usesVariousArt, Integer type, String tokenSetCode, String tokenDescriptor, boolean token, boolean twoFacedCard, boolean secondSide) {
this(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor, token, false, false, ""); this(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor, token, twoFacedCard, secondSide, "");
} }
public CardDownloadData(String name, String set, String collectorId, boolean usesVariousArt, Integer type, String tokenSetCode, String tokenDescriptor, boolean token, boolean twoFacedCard, boolean secondSide, String tokenClassName) { public CardDownloadData(String name, String set, String collectorId, boolean usesVariousArt, Integer type, String tokenSetCode, String tokenDescriptor, boolean token, boolean twoFacedCard, boolean secondSide, String tokenClassName) {
@ -72,7 +72,7 @@ public class CardDownloadData {
this.usesVariousArt = card.usesVariousArt; this.usesVariousArt = card.usesVariousArt;
this.tokenSetCode = card.tokenSetCode; this.tokenSetCode = card.tokenSetCode;
this.tokenDescriptor = card.tokenDescriptor; this.tokenDescriptor = card.tokenDescriptor;
this.tokenClassName = tokenClassName; this.tokenClassName = card.tokenClassName;
this.fileName = card.fileName; this.fileName = card.fileName;
} }
@ -163,7 +163,7 @@ public class CardDownloadData {
public String getTokenDescriptor() { public String getTokenDescriptor() {
return tokenDescriptor; return tokenDescriptor;
} }
public void setTokenClassName(String tokenClassName) { public void setTokenClassName(String tokenClassName) {
this.tokenClassName = tokenClassName; this.tokenClassName = tokenClassName;
} }

View file

@ -1,6 +1,7 @@
package org.mage.plugins.card.images; package org.mage.plugins.card.images;
import java.awt.*; import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
@ -14,18 +15,25 @@ import java.util.Set;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.imageio.IIOImage; import javax.imageio.IIOImage;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter; import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageOutputStream; import javax.imageio.stream.FileImageOutputStream;
import javax.swing.*; import javax.swing.*;
import mage.cards.ExpansionSet;
import mage.cards.Sets;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo; import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.client.MageFrame;
import mage.client.constants.Constants; import mage.client.constants.Constants;
import mage.client.dialog.PreferencesDialog; import mage.client.dialog.PreferencesDialog;
import mage.client.util.sets.ConstructedFormats; import mage.client.util.sets.ConstructedFormats;
import mage.remote.Connection; import mage.remote.Connection;
import static mage.remote.Connection.ProxyType.HTTP;
import static mage.remote.Connection.ProxyType.NONE;
import static mage.remote.Connection.ProxyType.SOCKS;
import net.java.truevfs.access.TFile; import net.java.truevfs.access.TFile;
import net.java.truevfs.access.TFileOutputStream; import net.java.truevfs.access.TFileOutputStream;
import net.java.truevfs.access.TVFS; import net.java.truevfs.access.TVFS;
@ -37,51 +45,97 @@ import org.mage.plugins.card.utils.CardImageUtils;
public class DownloadPictures extends DefaultBoundedRangeModel implements Runnable { public class DownloadPictures extends DefaultBoundedRangeModel implements Runnable {
private static DownloadPictures instance;
private static final Logger logger = Logger.getLogger(DownloadPictures.class); 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";
private JDialog dialog;
private final JProgressBar bar; private final JProgressBar bar;
private final JOptionPane dlg; private final JOptionPane dlg;
private boolean cancel; private boolean cancel;
private final JButton closeButton; private final JButton closeButton;
private final JButton startDownloadButton; private final JButton startDownloadButton;
private int cardIndex; private int cardIndex;
private List<CardDownloadData> cards; private List<CardDownloadData> allCardsMissingImage;
private List<CardDownloadData> type2cards; private List<CardDownloadData> cardsToDownload;
private final JComboBox jComboBox1;
private final JLabel jLabel1; private int missingCards = 0;
private static boolean offlineMode = false; private int missingTokens = 0;
private JCheckBox checkBox;
List<String> selectedSetCodes = new ArrayList<>();
private final JComboBox jComboBoxServer;
private final JLabel jLabelMessage;
private final JLabel jLabelAllMissing;
private final JLabel jLabelServer;
private final JComboBox jComboBoxSet;
private final JLabel jLabelSet;
private final Object sync = new Object(); private final Object sync = new Object();
private static CardImageSource cardImageSource; private static CardImageSource cardImageSource;
private Proxy p = Proxy.NO_PROXY; private Proxy p = Proxy.NO_PROXY;
// private ExecutorService executor = Executors.newFixedThreadPool(10); enum DownloadSources {
public static void main(String[] args) { WIZARDS("wizards.com", WizardCardsImageSource.instance),
startDownload(null, null); MYTHICSPOILER("mythicspoiler.com", MythicspoilerComSource.instance),
TOKENS("tokens.mtg.onl", TokensMtgImageSource.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;
private final CardImageSource source;
DownloadSources(String text, CardImageSource sourceInstance) {
this.text = text;
this.source = sourceInstance;
}
public CardImageSource getSource() {
return source;
}
@Override
public String toString() {
return text;
}
} }
public static void startDownload(JFrame frame, List<CardInfo> allCards) { public static DownloadPictures getInstance() {
List<CardDownloadData> cards = getNeededCards(allCards); return instance;
}
public static void main(String[] args) {
startDownload();
}
public static void startDownload() {
/* /*
* if (cards == null || cards.isEmpty()) { * if (cards == null || cards.isEmpty()) {
* JOptionPane.showMessageDialog(null, * JOptionPane.showMessageDialog(null,
* "All card pictures have been downloaded."); return; } * "All card pictures have been downloaded."); return; }
*/ */
DownloadPictures download = new DownloadPictures(cards); instance = new DownloadPictures(MageFrame.getInstance());
JDialog dlg = download.getDlg(frame); Thread t1 = new Thread(new LoadMissingCardData(instance));
dlg.setVisible(true); t1.start();
dlg.dispose(); instance.getDlg().setVisible(true);
download.cancel = true; instance.getDlg().dispose();
instance.cancel = true;
} }
public JDialog getDlg(JFrame frame) { public JDialog getDlg() {
String title = "Downloading";
final JDialog dialog = this.dlg.createDialog(frame, title);
closeButton.addActionListener(e -> dialog.setVisible(false));
return dialog; return dialog;
} }
@ -89,75 +143,77 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
this.cancel = cancel; this.cancel = cancel;
} }
public DownloadPictures(List<CardDownloadData> cards) { static int WIDTH = 400;
this.cards = cards;
bar = new JProgressBar(this); public DownloadPictures(JFrame frame) {
cardsToDownload = new ArrayList<>();
JPanel p0 = new JPanel(); JPanel p0 = new JPanel();
p0.setLayout(new BoxLayout(p0, BoxLayout.Y_AXIS)); p0.setLayout(new BoxLayout(p0, BoxLayout.Y_AXIS));
p0.add(Box.createVerticalStrut(5)); p0.add(Box.createVerticalStrut(5));
jLabel1 = new JLabel();
jLabel1.setText("Please select server:");
jLabel1.setAlignmentX(Component.LEFT_ALIGNMENT); jLabelMessage = new JLabel();
jLabelMessage.setAlignmentX(Component.CENTER_ALIGNMENT);
p0.add(jLabel1); jLabelMessage.setText("Initializing image download...");
p0.add(jLabelMessage);
p0.add(Box.createVerticalStrut(5)); p0.add(Box.createVerticalStrut(5));
ComboBoxModel jComboBox1Model = new DefaultComboBoxModel(new String[]{
// "magiccards.info",
"wizards.com",
"mythicspoiler.com",
"tokens.mtg.onl", //"mtgimage.com (HQ)",
"mtg.onl",
"alternative.mtg.onl",
"GrabBag",
"magidex.com",
"scryfall.com", //"mtgathering.ru HQ",
//"mtgathering.ru MQ",
//"mtgathering.ru LQ",
});
jComboBox1 = new JComboBox();
cardImageSource = MagicCardsImageSource.instance; jLabelAllMissing = new JLabel();
jComboBox1.setModel(jComboBox1Model); jLabelAllMissing.setAlignmentX(Component.LEFT_ALIGNMENT);
jComboBox1.setAlignmentX(Component.LEFT_ALIGNMENT); // jLabelAllMissing.setText("Computing number of missing images...");
jComboBox1.addActionListener(e -> { p0.add(jLabelAllMissing);
JComboBox cb = (JComboBox) e.getSource(); p0.add(Box.createVerticalStrut(5));
switch (cb.getSelectedIndex() + 1) {
case 0: jLabelServer = new JLabel();
cardImageSource = MagicCardsImageSource.instance; jLabelServer.setText("Please select image source:");
break; jLabelServer.setAlignmentX(Component.LEFT_ALIGNMENT);
case 1: jLabelServer.setVisible(false);
cardImageSource = WizardCardsImageSource.instance; p0.add(jLabelServer);
break;
case 2: p0.add(Box.createVerticalStrut(5));
cardImageSource = MythicspoilerComSource.instance;
break; jComboBoxServer = new JComboBox();
case 3: jComboBoxServer.setModel(new DefaultComboBoxModel(DownloadSources.values()));
cardImageSource = TokensMtgImageSource.instance; jComboBoxServer.setAlignmentX(Component.LEFT_ALIGNMENT);
break; jComboBoxServer.setAlignmentY(Component.LEFT_ALIGNMENT);
case 4: jComboBoxServer.addItemListener((ItemEvent event) -> {
cardImageSource = MtgOnlTokensImageSource.instance; if (event.getStateChange() == ItemEvent.SELECTED) {
break; comboBoxServerItemSelected(event);
case 5:
cardImageSource = AltMtgOnlTokensImageSource.instance;
break;
case 6:
cardImageSource = GrabbagImageSource.instance;
break;
case 7:
cardImageSource = MagidexImageSource.instance;
break;
case 8:
cardImageSource = ScryfallImageSource.instance;
break;
} }
updateCardsToDownload();
}); });
p0.add(jComboBox1); Dimension d = jComboBoxServer.getPreferredSize();
d.width = WIDTH;
jComboBoxServer.setPreferredSize(d);
p0.add(jComboBoxServer);
jComboBoxServer.setVisible(false);
// set the first source as default
cardImageSource = WizardCardsImageSource.instance;
p0.add(Box.createVerticalStrut(5));
// Set selection ---------------------------------
jLabelSet = new JLabel();
jLabelSet.setText("Please select sets to download the images for:");
jLabelSet.setAlignmentX(Component.LEFT_ALIGNMENT);
jLabelSet.setVisible(false);
p0.add(jLabelSet);
jComboBoxSet = new JComboBox();
// jComboBoxSet.setModel(new DefaultComboBoxModel<>(getSetsForCurrentImageSource()));
jComboBoxSet.setAlignmentX(Component.LEFT_ALIGNMENT);
jComboBoxSet.addItemListener((ItemEvent event) -> {
if (event.getStateChange() == ItemEvent.SELECTED) {
comboBoxSetItemSelected(event);
}
});
p0.add(jComboBoxSet);
jComboBoxSet.setVisible(false);
p0.add(Box.createVerticalStrut(5)); p0.add(Box.createVerticalStrut(5));
// Start // Start
@ -165,78 +221,168 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
startDownloadButton.addActionListener(e -> { startDownloadButton.addActionListener(e -> {
new Thread(DownloadPictures.this).start(); new Thread(DownloadPictures.this).start();
startDownloadButton.setEnabled(false); startDownloadButton.setEnabled(false);
checkBox.setEnabled(false);
}); });
p0.add(Box.createVerticalStrut(5)); p0.add(Box.createVerticalStrut(5));
// Progress // Progress
bar = new JProgressBar(this);
p0.add(bar); p0.add(bar);
bar.setStringPainted(true); bar.setStringPainted(true);
int count = cards.size();
float mb = (count * cardImageSource.getAverageSize()) / 1024; d = bar.getPreferredSize();
bar.setString(String.format(cardIndex == cards.size() ? "%d of %d cards finished! Please close!" d.width = WIDTH;
: "%d of %d cards finished! Please wait! [%.1f Mb]", 0, cards.size(), mb));
Dimension d = bar.getPreferredSize();
d.width = 300;
bar.setPreferredSize(d); bar.setPreferredSize(d);
bar.setVisible(false);
p0.add(Box.createVerticalStrut(5));
checkBox = new JCheckBox("Download images for Standard (Type2) only");
p0.add(checkBox);
p0.add(Box.createVerticalStrut(5));
checkBox.addActionListener(e -> updateCardsToDownload());
// JOptionPane // JOptionPane
Object[] options = {startDownloadButton, closeButton = new JButton("Cancel")}; Object[] options = {startDownloadButton, closeButton = new JButton("Cancel")};
startDownloadButton.setVisible(false);
closeButton.addActionListener(e -> dialog.setVisible(false));
closeButton.setVisible(false);
dlg = new JOptionPane(p0, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION, null, options, options[1]); dlg = new JOptionPane(p0, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION, null, options, options[1]);
dialog = this.dlg.createDialog(frame, "Downloading images");
} }
public static boolean checkForMissingCardImages(List<CardInfo> allCards) { public void setAllMissingCards() {
AtomicBoolean missedCardTFiles = new AtomicBoolean(); updateAndViewMessage("Get all available cards from the repository...");
allCards.parallelStream().forEach(card -> { List<CardInfo> cards = CardRepository.instance.findCards(new CardCriteria());
if (!missedCardTFiles.get()) { updateAndViewMessage("Check which images are missing ...");
if (!card.getCardNumber().isEmpty() && !"0".equals(card.getCardNumber()) && !card.getSetCode().isEmpty()) { this.allCardsMissingImage = getNeededCards(cards);
CardDownloadData url = new CardDownloadData(card.getName(), card.getSetCode(), card.getCardNumber(), card.usesVariousArt(), updateAndViewMessage("Check which images the current source is providing ...");
0, "", "", false, card.isDoubleFaced(), card.isNightCard()); jComboBoxSet.setModel(new DefaultComboBoxModel<>(getSetsForCurrentImageSource()));
TFile file = new TFile(CardImageUtils.generateImagePath(url));
if (!file.exists()) { updateCardsToDownload(jComboBoxSet.getSelectedItem().toString());
missedCardTFiles.set(true);
jComboBoxServer.setVisible(true);
jLabelServer.setVisible(true);
jComboBoxSet.setVisible(true);
jLabelSet.setVisible(true);
bar.setVisible(true);
startDownloadButton.setVisible(true);
closeButton.setVisible(true);
updateAndViewMessage("");
}
private void comboBoxServerItemSelected(ItemEvent evt) {
if (jComboBoxServer.isEnabled()) {
cardImageSource = ((DownloadSources) evt.getItem()).getSource();
// update the available sets / token comboBox
jComboBoxSet.setModel(new DefaultComboBoxModel<>(getSetsForCurrentImageSource()));
updateCardsToDownload(jComboBoxSet.getSelectedItem().toString());
}
}
public void updateAndViewMessage(String text) {
jLabelMessage.setText(text);
if (dialog != null) {
dialog.pack();
dialog.validate();
dialog.repaint();
}
}
private Object[] getSetsForCurrentImageSource() {
// Set the available sets to the combo box
ArrayList<String> supportedSets = cardImageSource.getSupportedSets();
List<String> setNames = new ArrayList<>();
if (supportedSets != null) {
setNames.add(ALL_IMAGES);
setNames.add(ALL_STANDARD_IMAGES);
}
if (cardImageSource.isTokenSource()) {
setNames.add(ALL_TOKENS);
}
if (supportedSets != null) {
for (String setCode : supportedSets) {
ExpansionSet expansionSet = Sets.findSet(setCode);
if (expansionSet != null) {
setNames.add(expansionSet.getName());
} else {
logger.error(cardImageSource.getSourceName() + ": Expansion set for code " + setCode + " not found!");
}
}
}
if (setNames.isEmpty()) {
logger.error("Source " + cardImageSource.getSourceName() + " creates no selectable items.");
setNames.add("not avalable");
}
return setNames.toArray(new String[0]);
}
private void updateCardsToDownload(String itemText) {
selectedSetCodes.clear();
switch (itemText) {
case ALL_IMAGES:
if (cardImageSource.getSupportedSets() == null) {
selectedSetCodes = cardImageSource.getSupportedSets();
} else {
selectedSetCodes.addAll(cardImageSource.getSupportedSets());
}
break;
case ALL_STANDARD_IMAGES:
List<String> standardSets = ConstructedFormats.getSetsByFormat(ConstructedFormats.STANDARD);
for (String setCode : cardImageSource.getSupportedSets()) {
if (standardSets.contains(setCode)) {
selectedSetCodes.add(setCode);
} else {
logger.debug("Set code " + setCode + " from download source " + cardImageSource.getSourceName());
}
}
break;
case ALL_TOKENS:
break;
default:
int nonSetEntries = 0;
if (cardImageSource.getSupportedSets() != null) {
nonSetEntries = 2;
}
if (cardImageSource.isTokenSource()) {
nonSetEntries++;
}
selectedSetCodes.add(cardImageSource.getSupportedSets().get(jComboBoxSet.getSelectedIndex() - nonSetEntries));
}
cardsToDownload.clear();
int numberTokenImagesAvailable = 0;
int numberCardImagesAvailable = 0;
for (CardDownloadData data : allCardsMissingImage) {
if (data.isToken()) {
if (cardImageSource.isTokenSource() && cardImageSource.isImageProvided(data.getSet(), data.getName())) {
numberTokenImagesAvailable++;
cardsToDownload.add(data);
}
} else {
if (selectedSetCodes != null && selectedSetCodes.contains(data.getSet())) {
if (cardImageSource.isSetSupportedComplete(data.getSet()) || cardImageSource.isImageProvided(data.getSet(), data.getName())) {
numberCardImagesAvailable++;
cardsToDownload.add(data);
} }
} }
} }
});
return missedCardTFiles.get();
}
private void updateCardsToDownload() {
List<CardDownloadData> cardsToDownload = cards;
if (type2cardsOnly()) {
selectType2andTokenCardsIfNotYetDone();
cardsToDownload = type2cards;
} }
updateProgressText(cardsToDownload.size()); updateProgressText(numberCardImagesAvailable, numberTokenImagesAvailable);
} }
private boolean type2cardsOnly() { private void comboBoxSetItemSelected(ItemEvent event) {
return checkBox.isSelected(); // Update the cards to download related to the selected set
updateCardsToDownload(event.getItem().toString());
} }
private void selectType2andTokenCardsIfNotYetDone() { private void updateProgressText(int cardCount, int tokenCount) {
if (type2cards == null) { missingTokens = 0;
type2cards = new ArrayList<>(); for (CardDownloadData card : allCardsMissingImage) {
for (CardDownloadData data : cards) { if (card.isToken()) {
if (data.isType2() || data.isToken()) { missingTokens++;
type2cards.add(data);
}
} }
} }
} missingCards = allCardsMissingImage.size() - missingTokens;
jLabelAllMissing.setText("Missing: " + missingCards + " card images / " + missingTokens + " token images");
private void updateProgressText(int cardCount) { int imageSum = cardCount + tokenCount;
float mb = (cardCount * cardImageSource.getAverageSize()) / 1024; float mb = (imageSum * cardImageSource.getAverageSize()) / 1024;
bar.setString(String.format(cardIndex == cardCount ? "%d of %d cards finished! Please close!" bar.setString(String.format(cardIndex == imageSum ? "%d of %d (%d cards/%d tokens) image downloads finished! Please close!"
: "%d of %d cards finished! Please wait! [%.1f Mb]", 0, cardCount, mb)); : "%d of %d (%d cards/%d tokens) image downloads finished! Please wait! [%.1f Mb]", 0, imageSum, cardCount, tokenCount, mb));
} }
private static String createDownloadName(CardInfo card) { private static String createDownloadName(CardInfo card) {
@ -262,11 +408,8 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
logger.warn("No formats defined. Try connecting to a server first!"); logger.warn("No formats defined. Try connecting to a server first!");
} }
int numberCardImages = allCards.size();
int numberWithoutTokens = 0;
List<CardDownloadData> allCardsUrls = Collections.synchronizedList(new ArrayList<>()); List<CardDownloadData> allCardsUrls = Collections.synchronizedList(new ArrayList<>());
try { try {
offlineMode = true;
allCards.parallelStream().forEach(card -> { allCards.parallelStream().forEach(card -> {
if (!card.getCardNumber().isEmpty() && !"0".equals(card.getCardNumber()) && !card.getSetCode().isEmpty() if (!card.getCardNumber().isEmpty() && !"0".equals(card.getCardNumber()) && !card.getSetCode().isEmpty()
&& !ignoreUrls.contains(card.getSetCode())) { && !ignoreUrls.contains(card.getSetCode())) {
@ -286,6 +429,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
if (card.getSecondSideName() == null || card.getSecondSideName().trim().isEmpty()) { if (card.getSecondSideName() == null || card.getSecondSideName().trim().isEmpty()) {
throw new IllegalStateException("Second side card can't have empty name."); throw new IllegalStateException("Second side card can't have empty name.");
} }
url = new CardDownloadData(card.getSecondSideName(), card.getSetCode(), card.getCardNumber(), card.usesVariousArt(), 0, "", "", false, card.isDoubleFaced(), true); url = new CardDownloadData(card.getSecondSideName(), card.getSetCode(), card.getCardNumber(), card.usesVariousArt(), 0, "", "", false, card.isDoubleFaced(), true);
url.setType2(isType2); url.setType2(isType2);
allCardsUrls.add(url); allCardsUrls.add(url);
@ -301,21 +445,18 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
allCardsUrls.add(cardDownloadData); allCardsUrls.add(cardDownloadData);
} }
} else if (card.getCardNumber().isEmpty() || "0".equals(card.getCardNumber())) { } else if (card.getCardNumber().isEmpty() || "0".equals(card.getCardNumber())) {
System.err.println("There was a critical error!");
logger.error("Card has no collector ID and won't be sent to client: " + card.getName()); logger.error("Card has no collector ID and won't be sent to client: " + card.getName());
} else if (card.getSetCode().isEmpty()) { } else if (card.getSetCode().isEmpty()) {
System.err.println("There was a critical error!");
logger.error("Card has no set name and won't be sent to client:" + card.getName()); logger.error("Card has no set name and won't be sent to client:" + card.getName());
} else {
logger.info("Card was not selected: " + card.getName());
} }
}); });
numberWithoutTokens = allCards.size();
allCardsUrls.addAll(getTokenCardUrls()); allCardsUrls.addAll(getTokenCardUrls());
} catch (Exception e) { } catch (Exception e) {
logger.error(e); logger.error(e);
} }
int numberAllTokenImages = allCardsUrls.size() - numberWithoutTokens;
/** /**
* check to see which cards we already have * check to see which cards we already have
*/ */
@ -329,22 +470,13 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
} }
}); });
int tokenImages = 0;
for (CardDownloadData card : cardsToDownload) {
logger.debug((card.isToken() ? "Token" : "Card") + " image to download: " + card.getName() + " (" + card.getSet() + ')');
if (card.isToken()) {
tokenImages++;
}
}
logger.info("Check download images (total card images: " + numberCardImages + ", total token images: " + numberAllTokenImages + ')');
logger.info(" => Missing card images: " + (cardsToDownload.size() - tokenImages));
logger.info(" => Missing token images: " + tokenImages);
return new ArrayList<>(cardsToDownload); return new ArrayList<>(cardsToDownload);
} }
public static ArrayList<CardDownloadData> getTokenCardUrls() throws RuntimeException { public static ArrayList<CardDownloadData> getTokenCardUrls() throws RuntimeException {
ArrayList<CardDownloadData> list = new ArrayList<>(); ArrayList<CardDownloadData> list = new ArrayList<>();
InputStream in = DownloadPictures.class.getClassLoader().getResourceAsStream("card-pictures-tok.txt"); InputStream in = DownloadPictures.class
.getClassLoader().getResourceAsStream("card-pictures-tok.txt");
if (in == null) { if (in == null) {
logger.error("resources input stream is null"); logger.error("resources input stream is null");
@ -446,9 +578,8 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
if (p != null) { if (p != null) {
HashSet<String> ignoreUrls = SettingsManager.getIntance().getIgnoreUrls(); HashSet<String> ignoreUrls = SettingsManager.getIntance().getIgnoreUrls();
List<CardDownloadData> cardsToDownload = this.checkBox.isSelected() ? type2cards : cards;
update(0, cardsToDownload.size()); update(0, cardsToDownload.size());
logger.info("Started download of " + cardsToDownload.size() + " images from source: " + cardImageSource.getSourceName());
int numberOfThreads = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_THREADS, "10")); int numberOfThreads = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_THREADS, "10"));
ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads); ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads);
@ -457,7 +588,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
CardDownloadData card = cardsToDownload.get(i); CardDownloadData card = cardsToDownload.get(i);
logger.debug("Downloading card: " + card.getName() + " (" + card.getSet() + ')'); logger.debug("Downloading image: " + card.getName() + " (" + card.getSet() + ')');
String url; String url;
if (ignoreUrls.contains(card.getSet()) || card.isToken()) { if (ignoreUrls.contains(card.getSet()) || card.isToken()) {
@ -482,7 +613,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
} catch (Exception ex) { } catch (Exception ex) {
} }
} else if (cardImageSource.getTotalImages() == -1) { } else if (cardImageSource.getTotalImages() == -1) {
logger.info("Card not available on " + cardImageSource.getSourceName() + ": " + card.getName() + " (" + card.getSet() + ')'); logger.info("Image not available on " + cardImageSource.getSourceName() + ": " + card.getName() + " (" + card.getSet() + ')');
synchronized (sync) { synchronized (sync) {
update(cardIndex + 1, cardsToDownload.size()); update(cardIndex + 1, cardsToDownload.size());
} }
@ -513,6 +644,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
System.gc(); System.gc();
} }
closeButton.setText("Close"); closeButton.setText("Close");
updateCardsToDownload(jComboBoxSet.getSelectedItem().toString());
} }
static String convertStreamToString(java.io.InputStream is) { static String convertStreamToString(java.io.InputStream is) {
@ -604,6 +736,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
if (temporaryFile != null && temporaryFile.length() > 100) { if (temporaryFile != null && temporaryFile.length() > 100) {
useTempFile = true; useTempFile = true;
} else { } else {
cardImageSource.doPause(url.getPath()); cardImageSource.doPause(url.getPath());
httpConn = url.openConnection(p); httpConn = url.openConnection(p);
httpConn.connect(); httpConn.connect();
@ -708,29 +841,52 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
if (cardIndex < count) { if (cardIndex < count) {
float mb = ((count - card) * cardImageSource.getAverageSize()) / 1024; float mb = ((count - card) * cardImageSource.getAverageSize()) / 1024;
bar.setString(String.format("%d of %d cards finished! Please wait! [%.1f Mb]", bar.setString(String.format("%d of %d image downloads finished! Please wait! [%.1f Mb]",
card, count, mb)); card, count, mb));
} else { } else {
List<CardDownloadData> remainingCards = Collections.synchronizedList(new ArrayList<>()); List<CardDownloadData> remainingCards = Collections.synchronizedList(new ArrayList<>());
DownloadPictures.this.cards.parallelStream().forEach(cardDownloadData -> { DownloadPictures.this.allCardsMissingImage.parallelStream().forEach(cardDownloadData -> {
TFile file = new TFile(CardImageUtils.generateImagePath(cardDownloadData)); TFile file = new TFile(CardImageUtils.generateImagePath(cardDownloadData));
if (!file.exists()) { if (!file.exists()) {
remainingCards.add(cardDownloadData); remainingCards.add(cardDownloadData);
} }
}); });
DownloadPictures.this.cards = new ArrayList<>(remainingCards); // remove the cards not downloaded to get the siccessfull downloaded cards
DownloadPictures.this.cardsToDownload.removeAll(remainingCards);
DownloadPictures.this.allCardsMissingImage.removeAll(DownloadPictures.this.cardsToDownload);
count = DownloadPictures.this.cards.size(); count = remainingCards.size();
if (count == 0) { if (count == 0) {
bar.setString("0 cards remaining! Please close!"); bar.setString("0 images remaining! Please close!");
} else { } else {
bar.setString(String.format("%d cards remaining! Please choose another source!", count)); // bar.setString(String.format("%d cards remaining! Please choose another source!", count));
startDownloadButton.setEnabled(true); startDownloadButton.setEnabled(true);
} }
} }
} }
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
}
class LoadMissingCardData implements Runnable {
private static DownloadPictures downloadPictures;
public LoadMissingCardData(DownloadPictures downloadPictures) {
LoadMissingCardData.downloadPictures = downloadPictures;
}
@Override
public void run() {
downloadPictures.setAllMissingCards();
}
public static void main() {
(new Thread(new LoadMissingCardData(downloadPictures))).start();
}
} }

View file

@ -1096,6 +1096,16 @@
|Generate|TOK:WWK|Snake|||SnakeToken| |Generate|TOK:WWK|Snake|||SnakeToken|
|Generate|TOK:WWK|Soldier Ally|||JoinTheRanksSoldierToken| |Generate|TOK:WWK|Soldier Ally|||JoinTheRanksSoldierToken|
|Generate|TOK:WWK|Wolf|||WolfToken| |Generate|TOK:WWK|Wolf|||WolfToken|
|Generate|TOK:XLN|Dinosaur|||DinosaurToken|
|Generate|TOK:XLN|Illusion|||JaceCunningCastawayIllusionToken|
|Generate|TOK:XLN|Merfolk|||MerfolkHexproofToken|
|Generate|TOK:XLN|Pirate|||PirateToken|
|Generate|TOK:XLN|Plant|||DefenderPlantToken|
|Generate|TOK:XLN|Treasure|1||TreasureToken|
|Generate|TOK:XLN|Treasure|2||TreasureToken|
|Generate|TOK:XLN|Treasure|3||TreasureToken|
|Generate|TOK:XLN|Treasure|4||TreasureToken|
|Generate|TOK:XLN|Vampire|||IxalanVampireToken|
|Generate|TOK:ZEN|Angel|||AngelToken| |Generate|TOK:ZEN|Angel|||AngelToken|
|Generate|TOK:ZEN|Beast|||BeastToken2| |Generate|TOK:ZEN|Beast|||BeastToken2|
|Generate|TOK:ZEN|Bird|||BirdToken| |Generate|TOK:ZEN|Bird|||BirdToken|

View file

@ -74,6 +74,6 @@ dd3evg=ddaevg
dd3gvl=ddagvl dd3gvl=ddagvl
dd3jvc=ddajvc dd3jvc=ddajvc
# Remove setname as soon as the images can be downloaded # Remove setname as soon as the images can be downloaded
ignore.urls=TOK,DDT,V17,IMA,RIX,E02,M19,M25,DOM,UST,H17 ignore.urls=TOK,DDT,V17,RIX,E02,M19,M25,DOM,UST,H17
# sets ordered by release time (newest goes first) # sets ordered by release time (newest goes first)
token.lookup.order=M19,M25,DOM,E02,RIX,UST,XLN,IMA,H17,C17,V17,E01,DDT,CMA,HOU,MM3,DDS,AKH,DD3DVD,DD3EVG,DD3GVL,DD3JVC,H09,AER,PCA,C16,V16,MPS,KLD,DDR,CN2,EMN,EMA,SOI,DDQ,CP,CMA,ARENA,SUS,APAC,EURO,UGIN,C15,OGW,EXP,DDP,BFZ,DRB,V09,V10,V11,V12,V13,V14,V15,TPR,MPRP,DD3,DDO,ORI,MM2,PTC,DTK,FRF,KTK,M15,VMA,CNS,JOU,BNG,THS,DDL,M14,MMA,DGM,GTC,RTR,M13,AVR,DDI,DKA,ISD,M12,NPH,MBS,SOM,M11,ROE,DDE,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS,CHK,GRC token.lookup.order=M19,M25,DOM,E02,RIX,UST,XLN,IMA,H17,C17,V17,E01,DDT,CMA,HOU,MM3,DDS,AKH,DD3DVD,DD3EVG,DD3GVL,DD3JVC,H09,AER,PCA,C16,V16,MPS,KLD,DDR,CN2,EMN,EMA,SOI,DDQ,CP,CMA,ARENA,SUS,APAC,EURO,UGIN,C15,OGW,EXP,DDP,BFZ,DRB,V09,V10,V11,V12,V13,V14,V15,TPR,MPRP,DD3,DDO,ORI,MM2,PTC,DTK,FRF,KTK,M15,VMA,CNS,JOU,BNG,THS,DDL,M14,MMA,DGM,GTC,RTR,M13,AVR,DDI,DKA,ISD,M12,NPH,MBS,SOM,M11,ROE,DDE,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS,CHK,GRC

View file

@ -222,7 +222,7 @@ Eldrazi Spawn, 1a, -, -, -, Creature - Eldrazi Spawn, Aleksi Briclot, Sacrifice
Eldrazi Spawn, 1b, -, -, -, Creature - Eldrazi Spawn, Mark Tedin, Sacrifice this creature: Add {1} to your mana pool. Eldrazi Spawn, 1b, -, -, -, Creature - Eldrazi Spawn, Mark Tedin, Sacrifice this creature: Add {1} to your mana pool.
Eldrazi Spawn, 1c, -, -, -, Creature - Eldrazi Spawn, Veronique Meignaud, Sacrifice this creature: Add {1} to your mana pool. Eldrazi Spawn, 1c, -, -, -, Creature - Eldrazi Spawn, Veronique Meignaud, Sacrifice this creature: Add {1} to your mana pool.
Elemental, 2, R, *|*, -, Creature - Elemental, Jung Park, - Elemental, 2, R, *|*, -, Creature - Elemental, Jung Park, -
Hellion, 3, R, 4|4, -, Creature - Hellion, Anthony Francisco, - Hellion, 1, R, 4|4, -, Creature - Hellion, Anthony Francisco, -
Ooze, 4, G, *|*, -, Creature - Ooze, Daniel Ljunggren, - Ooze, 4, G, *|*, -, Creature - Ooze, Daniel Ljunggren, -
Tuktuk The Returned, 5, -, 5|5, -, Legendary Artifact Creature - Goblin Golem, Franz Vohwinkel, - Tuktuk The Returned, 5, -, 5|5, -, Legendary Artifact Creature - Goblin Golem, Franz Vohwinkel, -
@ -344,7 +344,7 @@ Soldier, 3, W, 1|1, -, Creature - Soldier, Greg Staples, -
Drake, 4, U, 2|2, -, Creature - Drake, Svetlin Velinov, Flying Drake, 4, U, 2|2, -, Creature - Drake, Svetlin Velinov, Flying
Zombie, 5, B, 2|2, -, Creature - Zombie, Lucas Graciano, - Zombie, 5, B, 2|2, -, Creature - Zombie, Lucas Graciano, -
Goblin, 6, R, 1|1, -, Creature - Goblin, Karl Kopinski, - Goblin, 6, R, 1|1, -, Creature - Goblin, Karl Kopinski, -
Hellion, 7, R, 4|4, -, Creature - Hellion, Anthony Francisco, - Hellion, 1, R, 4|4, -, Creature - Hellion, Anthony Francisco, -
Beast, 8, G, 3|3, -, Creature - Beast, John Donahue, - Beast, 8, G, 3|3, -, Creature - Beast, John Donahue, -
Saproling, 9, G, 1|1, -, Creature - Saproling, Brad Rigney, - Saproling, 9, G, 1|1, -, Creature - Saproling, Brad Rigney, -
Wurm, 10, G, 6|6, -, Creature - Wurm, Anthony Francisco, - Wurm, 10, G, 6|6, -, Creature - Wurm, Anthony Francisco, -
@ -664,7 +664,7 @@ DDP - Duel Decks: Zendikar vs. Eldrazi (2015-08-28)
Eldrazi Spawn, 076, -, -, -, Creature - Eldrazi Spawn, Aleksi Briclot, Sacrifice this creature: Add {1} to your mana pool. Eldrazi Spawn, 076, -, -, -, Creature - Eldrazi Spawn, Aleksi Briclot, Sacrifice this creature: Add {1} to your mana pool.
Eldrazi Spawn, 077, -, -, -, Creature - Eldrazi Spawn, Veronique Meignaud, Sacrifice this creature: Add {1} to your mana pool. Eldrazi Spawn, 077, -, -, -, Creature - Eldrazi Spawn, Veronique Meignaud, Sacrifice this creature: Add {1} to your mana pool.
Eldrazi Spawn, 078, -, -, -, Creature - Eldrazi Spawn, Mark Tedin, Sacrifice this creature: Add {1} to your mana pool. Eldrazi Spawn, 078, -, -, -, Creature - Eldrazi Spawn, Mark Tedin, Sacrifice this creature: Add {1} to your mana pool.
Hellion, 079, R, 4|4, -, Creature - Hellion, Anthony Francisco, - Hellion, 1, R, 4|4, -, Creature - Hellion, Anthony Francisco, -
Plant, 080, G, -, -, Creature - Plant, Daren Bader, - Plant, 080, G, -, -, Creature - Plant, Daren Bader, -
BFZ - Battle for Zendikar (2015-10-09) BFZ - Battle for Zendikar (2015-10-09)
@ -745,3 +745,20 @@ Clue, 015, -, -, -, Artifact - Clue, James Paick, {2} Sacrifice this Artifact
Clue, 016, -, -, -, Artifact - Clue, Franz Vohwinkel, {2} Sacrifice this Artifact: Draw a card. Clue, 016, -, -, -, Artifact - Clue, Franz Vohwinkel, {2} Sacrifice this Artifact: Draw a card.
Jace Emblem, 017, -, -, -, Emblem - Jace, Tyler Jacobson, Whenever an opponent casts his or her first spell each turn<72> counter that spell. Jace Emblem, 017, -, -, -, Emblem - Jace, Tyler Jacobson, Whenever an opponent casts his or her first spell each turn<72> counter that spell.
Arlinn Emblem, 018, -, -, -, Emblem - Arlinn, Winona Nelson, Creatures you control have haste and '{T}: This creature deals damage equal to its power to target creature or player.' Arlinn Emblem, 018, -, -, -, Emblem - Arlinn, Winona Nelson, Creatures you control have haste and '{T}: This creature deals damage equal to its power to target creature or player.'
SWS - Star Wars Custom set
Rebel, 001, W, 1|1, -, Creature - Rebel, Alex Konstad, -
Trooper, 002, W, 1|1, -, Creature - Trooper, Darren Tan, -
Tusken Raider, 003, W, 1|1, -, Creature - Tusken Raider, William O'Connor, -
Ewok, 004, G, 1|1, -, Creature - Ewok, Chris NG, -
Hunter, 005, R, 4|4, -, Creature - Hunter, Steve Argyle, -
Royal Guard, 006, R, 2|2, -, Creature - Soldier, Aldo Katayanagi, First Strike
AT-AT, 007, -, 5|5, -, Artifact Creature - AT-AT, Prokhoda, When this creature dies<65> create two 1/1 white Trooper creature tokens.
B-Wing, 008, -, 1|1, -, Artifact Creature - Rebel Starship, Anthony Devine, Spaceflight
Droid, 009, -, 1|1, -, Artifact Creature - Droid, PeetuGee, -
TIE Fighter, 010, -, 1|1, -, Artifact Creature - Starship, Darren Tan, Spaceflight
Yoda Emblem, 011, -, -, -, Emblem - Yoda, Jerry Vanderstelt, Hexproof<6F> you and your creatures have.
Obi-Wan Kenobi Emblem, 012, -, -, -, Emblem - Obi-Wan Kenobi, Jerry Vanderstelt, Creatures you control get +1/+1 and have vigilance<63> first strike<6B> and lifelink.
Aurra Sing Emblem, 013, -, -, -, Emblem - Aurra Sing, Willman1701, Whenever a nontoken creature you control leaves the battlefield<6C> discard a card.

Can't render this file because it contains an unexpected character in line 549 and column 140.

View file

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

View file

@ -1,5 +1,5 @@
#Generated by Maven #Generated by Maven
#Mon Aug 28 09:53:46 CEST 2017 #Fri Sep 15 22:14:29 CEST 2017
version=1.4.26 version=1.4.26
groupId=org.mage groupId=org.mage
artifactId=mage-game-pennydreadfulcommanderfreeforall artifactId=mage-game-pennydreadfulcommanderfreeforall

View file

@ -1,18 +1,17 @@
package mage.player.ai.ma; package mage.player.ai.ma;
import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.effects.Effect; import mage.abilities.effects.Effect;
import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.HasteAbility;
import mage.cards.Card; import mage.cards.Card;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.SubType;
import mage.counters.CounterType; import mage.counters.CounterType;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import java.util.UUID;
import mage.constants.SubType;
/** /**
* @author ubeefx, nantuko * @author ubeefx, nantuko
*/ */
@ -104,7 +103,7 @@ public final class ArtificialScoringSystem {
} }
score += equipments * 50 + enchantments * 100; score += equipments * 50 + enchantments * 100;
if (!permanent.canAttack(game)) { if (!permanent.canAttack(null, game)) {
score -= 100; score -= 100;
} }

View file

@ -1,16 +1,16 @@
/* /*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, are * Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met: * permitted provided that the following conditions are met:
* *
* 1. Redistributions of source code must retain the above copyright notice, this list of * 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer. * conditions and the following disclaimer.
* *
* 2. Redistributions in binary form must reproduce the above copyright notice, this list * 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials * of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution. * provided with the distribution.
* *
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
@ -20,24 +20,22 @@
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* The views and conclusions contained in the software and documentation are those of the * The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.player.ai; package mage.player.ai;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import mage.abilities.keyword.DoubleStrikeAbility; import mage.abilities.keyword.DoubleStrikeAbility;
import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.FirstStrikeAbility;
import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.TrampleAbility;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/** /**
* *
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
@ -50,15 +48,16 @@ public class CombatEvaluator {
public int evaluate(Permanent creature, Game game) { public int evaluate(Permanent creature, Game game) {
if (!values.containsKey(creature.getId())) { if (!values.containsKey(creature.getId())) {
int value = 0; int value = 0;
if (creature.canAttack(game)) if (creature.canAttack(null, game)) {
value += 2; value += 2;
}
value += creature.getPower().getValue(); value += creature.getPower().getValue();
value += creature.getToughness().getValue(); value += creature.getToughness().getValue();
value += creature.getAbilities().getEvasionAbilities().size(); value += creature.getAbilities().getEvasionAbilities().size();
value += creature.getAbilities().getProtectionAbilities().size(); value += creature.getAbilities().getProtectionAbilities().size();
value += creature.getAbilities().containsKey(FirstStrikeAbility.getInstance().getId())?1:0; value += creature.getAbilities().containsKey(FirstStrikeAbility.getInstance().getId()) ? 1 : 0;
value += creature.getAbilities().containsKey(DoubleStrikeAbility.getInstance().getId())?2:0; value += creature.getAbilities().containsKey(DoubleStrikeAbility.getInstance().getId()) ? 2 : 0;
value += creature.getAbilities().containsKey(TrampleAbility.getInstance().getId())?1:0; value += creature.getAbilities().containsKey(TrampleAbility.getInstance().getId()) ? 1 : 0;
values.put(creature.getId(), value); values.put(creature.getId(), value);
} }
return values.get(creature.getId()); return values.get(creature.getId());

View file

@ -1939,7 +1939,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
protected int combatPotential(Permanent creature, Game game) { protected int combatPotential(Permanent creature, Game game) {
log.debug("combatPotential"); log.debug("combatPotential");
if (!creature.canAttack(game)) { if (!creature.canAttack(null, game)) {
return 0; return 0;
} }
int potential = creature.getPower().getValue(); int potential = creature.getPower().getValue();

View file

@ -46,6 +46,7 @@ import mage.choices.ChoiceImpl;
import mage.constants.*; import mage.constants.*;
import static mage.constants.PlayerAction.REQUEST_AUTO_ANSWER_RESET_ALL; import static mage.constants.PlayerAction.REQUEST_AUTO_ANSWER_RESET_ALL;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL; import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL;
import mage.filter.StaticFilters;
import mage.filter.common.FilterAttackingCreature; import mage.filter.common.FilterAttackingCreature;
import mage.filter.common.FilterBlockingCreature; import mage.filter.common.FilterBlockingCreature;
import mage.filter.common.FilterCreatureForCombat; import mage.filter.common.FilterCreatureForCombat;
@ -1055,6 +1056,7 @@ public class HumanPlayer extends PlayerImpl {
updateGameStatePriority("selectAttackers", game); updateGameStatePriority("selectAttackers", game);
FilterCreatureForCombat filter = filterCreatureForCombat.copy(); FilterCreatureForCombat filter = filterCreatureForCombat.copy();
filter.add(new ControllerIdPredicate(attackingPlayerId)); filter.add(new ControllerIdPredicate(attackingPlayerId));
while (!abort) { while (!abort) {
if (passedAllTurns if (passedAllTurns
|| passedUntilEndStepBeforeMyTurn || passedUntilEndStepBeforeMyTurn
@ -1063,13 +1065,15 @@ public class HumanPlayer extends PlayerImpl {
|| passedTurnSkipStack || passedTurnSkipStack
|| passedUntilEndOfTurn || passedUntilEndOfTurn
|| passedUntilNextMain))) { || passedUntilNextMain))) {
return; if (checkIfAttackersValid(game)) {
return;
}
} }
Map<String, Serializable> options = new HashMap<>(); Map<String, Serializable> options = new HashMap<>();
List<UUID> possibleAttackers = new ArrayList<>(); List<UUID> possibleAttackers = new ArrayList<>();
for (Permanent possibleAttacker : game.getBattlefield().getActivePermanents(filter, attackingPlayerId, game)) { for (Permanent possibleAttacker : game.getBattlefield().getActivePermanents(filter, attackingPlayerId, game)) {
if (possibleAttacker.canAttack(game)) { if (possibleAttacker.canAttack(null, game)) {
possibleAttackers.add(possibleAttacker.getId()); possibleAttackers.add(possibleAttacker.getId());
} }
} }
@ -1108,46 +1112,14 @@ public class HumanPlayer extends PlayerImpl {
// attack selected default defender // attack selected default defender
declareAttacker(attacker.getId(), attackedDefender, game, false); declareAttacker(attacker.getId(), attackedDefender, game, false);
} }
} else if (response.getBoolean() != null) { } else if (response.getInteger() != null) { // F-Key
// check if enough attackers are declared if (checkIfAttackersValid(game)) {
if (!game.getCombat().getCreaturesForcedToAttack().isEmpty()) { return;
if (!game.getCombat().getAttackers().containsAll(game.getCombat().getCreaturesForcedToAttack().keySet())) { }
int forcedAttackers = 0; } else if (response.getBoolean() != null) { // ok button
StringBuilder sb = new StringBuilder(); if (checkIfAttackersValid(game)) {
for (UUID creatureId : game.getCombat().getCreaturesForcedToAttack().keySet()) { return;
boolean validForcedAttacker = false;
if (game.getCombat().getAttackers().contains(creatureId)) {
Set<UUID> possibleDefender = game.getCombat().getCreaturesForcedToAttack().get(creatureId);
if (possibleDefender.isEmpty()
|| possibleDefender.contains(game.getCombat().getDefenderId(creatureId))) {
validForcedAttacker = true;
}
}
if (validForcedAttacker) {
forcedAttackers++;
} else {
Permanent creature = game.getPermanent(creatureId);
if (creature != null) {
sb.append(creature.getIdName()).append(' ');
}
}
}
if (game.getCombat().getMaxAttackers() > forcedAttackers) {
int requireToAttack = Math.min(game.getCombat().getMaxAttackers() - forcedAttackers, game.getCombat().getCreaturesForcedToAttack().size() - forcedAttackers);
String message = (requireToAttack == 1 ? " more attacker that is " : " more attackers that are ")
+ "forced to attack.\nCreature"
+ (requireToAttack == 1 ? "" : "s") + " forced to attack: ";
game.informPlayer(this, sb.insert(0, message)
.insert(0, requireToAttack)
.insert(0, "You have to attack with ").toString());
continue;
}
}
} }
return;
} else if (response.getInteger() != null) {
return;
} else if (response.getUUID() != null) { } else if (response.getUUID() != null) {
Permanent attacker = game.getPermanent(response.getUUID()); Permanent attacker = game.getPermanent(response.getUUID());
if (attacker != null) { if (attacker != null) {
@ -1161,8 +1133,95 @@ public class HumanPlayer extends PlayerImpl {
} }
} }
private boolean checkIfAttackersValid(Game game) {
if (!game.getCombat().getCreaturesForcedToAttack().isEmpty()) {
if (!game.getCombat().getAttackers().containsAll(game.getCombat().getCreaturesForcedToAttack().keySet())) {
int forcedAttackers = 0;
StringBuilder sb = new StringBuilder();
for (UUID creatureId : game.getCombat().getCreaturesForcedToAttack().keySet()) {
boolean validForcedAttacker = false;
if (game.getCombat().getAttackers().contains(creatureId)) {
Set<UUID> possibleDefender = game.getCombat().getCreaturesForcedToAttack().get(creatureId);
if (possibleDefender.isEmpty()
|| possibleDefender.contains(game.getCombat().getDefenderId(creatureId))) {
validForcedAttacker = true;
}
}
if (validForcedAttacker) {
forcedAttackers++;
} else {
Permanent creature = game.getPermanent(creatureId);
if (creature != null) {
sb.append(creature.getIdName()).append(' ');
}
}
}
if (game.getCombat().getMaxAttackers() > forcedAttackers) {
int requireToAttack = Math.min(game.getCombat().getMaxAttackers() - forcedAttackers, game.getCombat().getCreaturesForcedToAttack().size() - forcedAttackers);
String message = (requireToAttack == 1 ? " more attacker that is " : " more attackers that are ")
+ "forced to attack.\nCreature"
+ (requireToAttack == 1 ? "" : "s") + " forced to attack: ";
game.informPlayer(this, sb.insert(0, message)
.insert(0, requireToAttack)
.insert(0, "You have to attack with ").toString());
return false;
}
}
}
// check if enough attackers are declared
// check if players have to be attacked
Set<UUID> playersToAttackIfAble = new HashSet<>();
for (Map.Entry<RequirementEffect, Set<Ability>> entry : game.getContinuousEffects().getApplicableRequirementEffects(null, true, game).entrySet()) {
RequirementEffect effect = entry.getKey();
for (Ability ability : entry.getValue()) {
UUID playerToAttack = effect.playerMustBeAttackedIfAble(ability, game);
if (playerToAttack != null) {
playersToAttackIfAble.add(playerToAttack);
}
}
}
if (!playersToAttackIfAble.isEmpty()) {
Set<UUID> checkPlayersToAttackIfAble = new HashSet<>(playersToAttackIfAble);
for (CombatGroup combatGroup : game.getCombat().getGroups()) {
checkPlayersToAttackIfAble.remove(combatGroup.getDefendingPlayerId());
}
for (UUID forcedToAttackId : checkPlayersToAttackIfAble) {
Player forcedToAttack = game.getPlayer(forcedToAttackId);
for (Permanent attacker : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, getId(), game)) {
if (game.getContinuousEffects().checkIfThereArePayCostToAttackBlockEffects(
GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKER,
forcedToAttackId, attacker.getId(), attacker.getControllerId()), game)) {
continue;
}
// if attacker needs a specific defender to attack so select that one instead
if (game.getCombat().getCreaturesForcedToAttack().containsKey(attacker.getId())) {
Set<UUID> possibleDefenders = game.getCombat().getCreaturesForcedToAttack().get(attacker.getId());
if (!possibleDefenders.isEmpty() && !possibleDefenders.contains(forcedToAttackId)) {
continue;
}
}
UUID defendingPlayerId = game.getCombat().getDefendingPlayerId(attacker.getId(), game);
if (playersToAttackIfAble.contains(defendingPlayerId)) {
// already attacks other player taht has to be attacked
continue;
}
if (defendingPlayerId != null || attacker.canAttackInPrinciple(forcedToAttackId, game)) {
game.informPlayer(this, "You are forced to attack " + forcedToAttack.getName() + " or a controlled planeswalker e.g. with " + attacker.getIdName() + ".");
return false;
}
}
}
}
return true;
}
private void removeAttackerIfPossible(Game game, Permanent attacker) { private void removeAttackerIfPossible(Game game, Permanent attacker) {
for (Map.Entry entry : game.getContinuousEffects().getApplicableRequirementEffects(attacker, game).entrySet()) { for (Map.Entry entry : game.getContinuousEffects().getApplicableRequirementEffects(attacker, false, game).entrySet()) {
RequirementEffect effect = (RequirementEffect) entry.getKey(); RequirementEffect effect = (RequirementEffect) entry.getKey();
if (effect.mustAttack(game)) { if (effect.mustAttack(game)) {
if (game.getCombat().getMaxAttackers() >= game.getCombat().getCreaturesForcedToAttack().size() if (game.getCombat().getMaxAttackers() >= game.getCombat().getCreaturesForcedToAttack().size()

View file

@ -53,6 +53,7 @@ import mage.filter.predicate.Predicate;
import mage.filter.predicate.mageobject.SubtypePredicate; import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.filter.predicate.permanent.ControllerPredicate; import mage.filter.predicate.permanent.ControllerPredicate;
import mage.game.Game; import mage.game.Game;
import mage.game.events.DamagedPlayerEvent;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.target.common.TargetNonlandPermanent; import mage.target.common.TargetNonlandPermanent;
@ -86,7 +87,7 @@ public class AdmiralBeckettBrass extends CardImpl {
// At the beginning of your end step, gain control of target nonland permanent controlled by a player who was dealt combat damage by three or more Pirates this turn. // At the beginning of your end step, gain control of target nonland permanent controlled by a player who was dealt combat damage by three or more Pirates this turn.
Ability ability = new BeginningOfEndStepTriggeredAbility(new GainControlTargetEffect(Duration.Custom), TargetController.YOU, false); Ability ability = new BeginningOfEndStepTriggeredAbility(new GainControlTargetEffect(Duration.Custom), TargetController.YOU, false);
ability.addTarget(new TargetNonlandPermanent()); ability.addTarget(new TargetNonlandPermanent(new FilterNonlandPermanent("nonland permanent controlled by a player who was dealt combat damage by three or more Pirates this turn")));
originalId = ability.getOriginalId(); originalId = ability.getOriginalId();
this.addAbility(ability, new DamagedByPiratesWatcher()); this.addAbility(ability, new DamagedByPiratesWatcher());
} }
@ -137,15 +138,17 @@ class DamagedByPiratesWatcher extends Watcher {
@Override @Override
public void watch(GameEvent event, Game game) { public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER && event.getFlag()) { if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER) {
Permanent creature = game.getPermanentOrLKIBattlefield(event.getSourceId()); if (((DamagedPlayerEvent) event).isCombatDamage()) {
if (creature != null && creature.getSubtype(game).contains(SubType.PIRATE)) { Permanent creature = game.getPermanentOrLKIBattlefield(event.getSourceId());
if (damageSourceIds.keySet().contains(event.getTargetId())) { if (creature != null && creature.getSubtype(game).contains(SubType.PIRATE)) {
damageSourceIds.get(event.getTargetId()).add(creature.getId()); if (damageSourceIds.keySet().contains(event.getTargetId())) {
} else { damageSourceIds.get(event.getTargetId()).add(creature.getId());
Set<UUID> creatureSet = new HashSet(); } else {
creatureSet.add(creature.getId()); Set<UUID> creatureSet = new HashSet();
damageSourceIds.put(event.getTargetId(), creatureSet); creatureSet.add(creature.getId());
damageSourceIds.put(event.getTargetId(), creatureSet);
}
} }
} }
} }

View file

@ -51,7 +51,7 @@ public class AdroitHateflayer extends CardImpl {
this.toughness = new MageInt(3); this.toughness = new MageInt(3);
// Menace // Menace
this.addAbility(MenaceAbility.getInstance()); this.addAbility(new MenaceAbility());
// Whenever Adroit Hateflayer attacks, each opponent loses 2 life. // Whenever Adroit Hateflayer attacks, each opponent loses 2 life.
this.addAbility(new AttacksTriggeredAbility(new LoseLifeOpponentsEffect(2), false)); this.addAbility(new AttacksTriggeredAbility(new LoseLifeOpponentsEffect(2), false));

View file

@ -70,6 +70,7 @@ public class AlexiZephyrMage extends CardImpl {
this.power = new MageInt(3); this.power = new MageInt(3);
this.toughness = new MageInt(3); this.toughness = new MageInt(3);
//TODO: Make ability properly copiable
// {X}{U}, {tap}, Discard two cards: Return X target creatures to their owners' hands. // {X}{U}, {tap}, Discard two cards: Return X target creatures to their owners' hands.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), new ManaCostsImpl("{X}{U}")); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), new ManaCostsImpl("{X}{U}"));
ability.addCost(new TapSourceCost()); ability.addCost(new TapSourceCost());

View file

@ -50,7 +50,7 @@ public class AlleyStrangler extends CardImpl {
this.toughness = new MageInt(3); this.toughness = new MageInt(3);
// Menace // Menace
this.addAbility(MenaceAbility.getInstance()); this.addAbility(new MenaceAbility());
} }
public AlleyStrangler(final AlleyStrangler card) { public AlleyStrangler(final AlleyStrangler card) {

View file

@ -0,0 +1,110 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.a;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.common.AsEntersBattlefieldAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.ChooseCreatureTypeEffect;
import mage.abilities.effects.common.DontUntapInControllersUntapStepAllEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
/**
*
* @author TheElk801
*/
public class AnZerrinRuins extends CardImpl {
public AnZerrinRuins(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}{R}");
// As An-Zerrin Ruins enters the battlefield, choose a creature type.
this.addAbility(new AsEntersBattlefieldAbility(new ChooseCreatureTypeEffect(Outcome.UnboostCreature)));
// Creatures of the chosen type don't untap during their controllers' untap steps.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AnZerrinRuinsDontUntapEffect()));
}
public AnZerrinRuins(final AnZerrinRuins card) {
super(card);
}
@Override
public AnZerrinRuins copy() {
return new AnZerrinRuins(this);
}
}
class AnZerrinRuinsDontUntapEffect extends DontUntapInControllersUntapStepAllEffect {
public AnZerrinRuinsDontUntapEffect() {
super(Duration.WhileOnBattlefield, TargetController.ANY, new FilterCreaturePermanent());
}
public AnZerrinRuinsDontUntapEffect(final AnZerrinRuinsDontUntapEffect effect) {
super(effect);
}
@Override
public AnZerrinRuinsDontUntapEffect copy() {
return new AnZerrinRuinsDontUntapEffect(this);
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (super.applies(event, source, game)) {
Permanent sourcePerm = game.getPermanent(source.getSourceId());
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent != null && sourcePerm != null) {
SubType subtype = (SubType) game.getState().getValue(sourcePerm.getId() + "_type");
if (permanent.getSubtype(game).contains(subtype)) {
return true;
}
}
}
return false;
}
@Override
public String getText(Mode mode) {
return "Creatures of the chosen type don't untap during their controllers' untap steps.";
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
/**
*
* @author caldover
*/
public class AncientBrontodon extends CardImpl {
public AncientBrontodon(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{G}{G}");
this.subtype.add(SubType.DINOSAUR);
this.power = new MageInt(9);
this.toughness = new MageInt(9);
}
public AncientBrontodon(final AncientBrontodon card) {
super(card);
}
@Override
public AncientBrontodon copy() {
return new AncientBrontodon(this);
}
}

View file

@ -25,28 +25,24 @@
* authors and should not be interpreted as representing official policies, either expressed * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.cards.a; package mage.cards.a;
import java.util.UUID; import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.ActivatedAbilityImpl; import mage.abilities.condition.common.SourceAttackingCondition;
import mage.abilities.costs.Cost; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.costs.CostImpl; import mage.abilities.decorator.ConditionalActivatedAbility;
import mage.abilities.costs.mana.ColoredManaCost;
import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.ColoredManaSymbol;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetCreaturePermanent;
/** /**
@ -55,19 +51,38 @@ import mage.target.common.TargetCreaturePermanent;
*/ */
public class AncientHellkite extends CardImpl { public class AncientHellkite extends CardImpl {
private final UUID originalId;
public AncientHellkite(UUID ownerId, CardSetInfo setInfo) { public AncientHellkite(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{R}{R}{R}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}{R}");
this.subtype.add(SubType.DRAGON); this.subtype.add(SubType.DRAGON);
this.power = new MageInt(6); this.power = new MageInt(6);
this.toughness = new MageInt(6); this.toughness = new MageInt(6);
//TODO: Make ability properly copiable
this.addAbility(FlyingAbility.getInstance()); this.addAbility(FlyingAbility.getInstance());
this.addAbility(new AncientHellkiteAbility()); Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new ManaCostsImpl("{R}"), SourceAttackingCondition.instance);
ability.addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature defending player controls")));
originalId = ability.getOriginalId();
this.addAbility(ability);
} }
public AncientHellkite(final AncientHellkite card) { public AncientHellkite(final AncientHellkite card) {
super(card); super(card);
this.originalId = card.originalId;
}
@Override
public void adjustTargets(Ability ability, Game game) {
if (ability.getOriginalId().equals(originalId)) {
ability.getTargets().clear();
FilterCreaturePermanent filter = new FilterCreaturePermanent("creature defending player controls");
UUID defenderId = game.getCombat().getDefenderId(ability.getSourceId());
filter.add(new ControllerIdPredicate(defenderId));
TargetCreaturePermanent target = new TargetCreaturePermanent(filter);
ability.addTarget(target);
}
} }
@Override @Override
@ -76,71 +91,3 @@ public class AncientHellkite extends CardImpl {
} }
} }
class AncientHellkiteAbility extends ActivatedAbilityImpl {
private static final FilterCreaturePermanent filterTemplate = new FilterCreaturePermanent("creature defending player controls");
public AncientHellkiteAbility() {
super(Zone.BATTLEFIELD, new DamageTargetEffect(1));
addCost(new AncientHellkiteCost());
addManaCost(new ColoredManaCost(ColoredManaSymbol.R));
addTarget(new TargetCreaturePermanent(filterTemplate));
}
public AncientHellkiteAbility(final AncientHellkiteAbility ability) {
super(ability);
}
@Override
public AncientHellkiteAbility copy() {
return new AncientHellkiteAbility(this);
}
@Override
public boolean activate(Game game, boolean noMana) {
UUID defenderId = game.getCombat().getDefenderId(sourceId);
if (defenderId != null) {
FilterCreaturePermanent filter = filterTemplate.copy();
filter.add(new ControllerIdPredicate(defenderId));
this.getTargets().clear();
TargetCreaturePermanent target = new TargetCreaturePermanent(filter);
this.addTarget(target);
return super.activate(game, noMana);
}
return false;
}
}
class AncientHellkiteCost extends CostImpl {
public AncientHellkiteCost() {
this.text = "Activate this ability only if Ancient Hellkite is attacking";
}
public AncientHellkiteCost(final AncientHellkiteCost cost) {
super(cost);
}
@Override
public AncientHellkiteCost copy() {
return new AncientHellkiteCost(this);
}
@Override
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null && permanent.isAttacking()) {
return true;
}
return false;
}
@Override
public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) {
this.paid = true;
return paid;
}
}

View file

@ -50,7 +50,7 @@ public class AngrathsMarauders extends CardImpl {
public AngrathsMarauders(UUID ownerId, CardSetInfo setInfo) { public AngrathsMarauders(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}{R}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}{R}");
this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.PIRATE); this.subtype.add(SubType.PIRATE);
this.power = new MageInt(4); this.power = new MageInt(4);
@ -69,6 +69,7 @@ public class AngrathsMarauders extends CardImpl {
return new AngrathsMarauders(this); return new AngrathsMarauders(this);
} }
} }
class AngrathsMaraudersEffect extends ReplacementEffectImpl { class AngrathsMaraudersEffect extends ReplacementEffectImpl {
public AngrathsMaraudersEffect() { public AngrathsMaraudersEffect() {
@ -96,11 +97,11 @@ class AngrathsMaraudersEffect extends ReplacementEffectImpl {
return true; return true;
} }
return false; return false;
} }
@Override @Override
public boolean applies(GameEvent event, Ability source, Game game) { public boolean applies(GameEvent event, Ability source, Game game) {
return event.getSourceId().equals(source.getControllerId()); return game.getControllerId(event.getSourceId()).equals(source.getControllerId());
} }
@Override @Override

View file

@ -0,0 +1,78 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.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.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author caldover
*/
public class AnointedDeacon extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Vampire");
static {
filter.add(new SubtypePredicate(SubType.VAMPIRE));
}
public AnointedDeacon(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}");
this.subtype.add(SubType.VAMPIRE);
this.subtype.add(SubType.CLERIC);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// At the beginning of combat on your turn, you may have target Vampire get +2/+0 until end of turn.
Ability ability = new BeginningOfCombatTriggeredAbility(Zone.BATTLEFIELD,
new BoostTargetEffect(2, 0, Duration.EndOfTurn).setText("you may have target Vampire get +2/+0 until end of turn"),
TargetController.YOU, true, false);
ability.addTarget(new TargetCreaturePermanent(filter));
this.addAbility(ability);
}
public AnointedDeacon(final AnointedDeacon card) {
super(card);
}
@Override
public AnointedDeacon copy() {
return new AnointedDeacon(this);
}
}

View file

@ -49,7 +49,7 @@ public class AradaraExpress extends CardImpl {
this.toughness = new MageInt(6); this.toughness = new MageInt(6);
// Menace // Menace
this.addAbility(MenaceAbility.getInstance()); this.addAbility(new MenaceAbility());
// Crew 4 // Crew 4
this.addAbility(new CrewAbility(4)); this.addAbility(new CrewAbility(4));
} }

View file

@ -52,7 +52,7 @@ public class AshesOfTheAbhorrent extends CardImpl {
public AshesOfTheAbhorrent(UUID ownerId, CardSetInfo setInfo) { public AshesOfTheAbhorrent(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}");
// Players can't cast spells from graveyards or activate abilities from graveyards. // Players can't cast spells from graveyards or activate abilities of cards in graveyards.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AshesOfTheAbhorrentEffect())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AshesOfTheAbhorrentEffect()));
// Whenever a creature dies, you gain 1 life. // Whenever a creature dies, you gain 1 life.
@ -73,7 +73,7 @@ class AshesOfTheAbhorrentEffect extends ContinuousRuleModifyingEffectImpl {
public AshesOfTheAbhorrentEffect() { public AshesOfTheAbhorrentEffect() {
super(Duration.WhileOnBattlefield, Outcome.Neutral); super(Duration.WhileOnBattlefield, Outcome.Neutral);
staticText = "Players can't cast spells from graveyards or activate abilities from graveyards"; staticText = "Players can't cast spells from graveyards or activate abilities of cards in graveyards";
} }
public AshesOfTheAbhorrentEffect(final AshesOfTheAbhorrentEffect effect) { public AshesOfTheAbhorrentEffect(final AshesOfTheAbhorrentEffect effect) {

View file

@ -63,7 +63,7 @@ public class AtarkaPummeler extends CardImpl {
// <i>Formidable</i> - {3}{R}{R}: Creatures you control gain menace until end of turn. Activate this ability only if creature you control have total power 8 or greater. (They can't be blocked except by two or more creatures.) // <i>Formidable</i> - {3}{R}{R}: Creatures you control gain menace until end of turn. Activate this ability only if creature you control have total power 8 or greater. (They can't be blocked except by two or more creatures.)
Ability ability = new ActivateIfConditionActivatedAbility( Ability ability = new ActivateIfConditionActivatedAbility(
Zone.BATTLEFIELD, Zone.BATTLEFIELD,
new GainAbilityAllEffect(MenaceAbility.getInstance(), Duration.EndOfTurn, filter), new GainAbilityAllEffect(new MenaceAbility(), Duration.EndOfTurn, filter),
new ManaCostsImpl("{3}{R}{R}"), new ManaCostsImpl("{3}{R}{R}"),
FormidableCondition.instance); FormidableCondition.instance);
ability.setAbilityWord(AbilityWord.FORMIDABLE); ability.setAbilityWord(AbilityWord.FORMIDABLE);

View file

@ -35,14 +35,11 @@ import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.keyword.DoubleStrikeAbility; import mage.abilities.keyword.DoubleStrikeAbility;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.TrampleAbility;
import mage.abilities.text.TextPartSubType;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.*; import mage.constants.*;
import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.filter.predicate.mageobject.TextPartSubtypePredicate;
import mage.filter.predicate.permanent.TappedPredicate;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
@ -68,11 +65,7 @@ public class AtarkaWorldRender extends CardImpl {
this.addAbility(TrampleAbility.getInstance()); this.addAbility(TrampleAbility.getInstance());
// Whenever a Dragon you control attacks, it gains double strike until end of turn. // Whenever a Dragon you control attacks, it gains double strike until end of turn.
TextPartSubType textPart1 = (TextPartSubType) addTextPart(new TextPartSubType(SubType.DRAGON)); this.addAbility(new AtarkaWorldRenderEffect());
FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Dragon you control");
filter.add(new TextPartSubtypePredicate(textPart1));
filter.add(Predicates.not(new TappedPredicate()));
this.addAbility(new AtarkaWorldRenderEffect(filter));
} }
@ -88,16 +81,18 @@ public class AtarkaWorldRender extends CardImpl {
class AtarkaWorldRenderEffect extends TriggeredAbilityImpl { class AtarkaWorldRenderEffect extends TriggeredAbilityImpl {
FilterControlledCreaturePermanent filter; private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("dragon you control");
public AtarkaWorldRenderEffect(FilterControlledCreaturePermanent filter) { static {
filter.add(new SubtypePredicate(SubType.DRAGON));
}
public AtarkaWorldRenderEffect() {
super(Zone.BATTLEFIELD, new GainAbilityTargetEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn)); super(Zone.BATTLEFIELD, new GainAbilityTargetEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn));
this.filter = filter;
} }
public AtarkaWorldRenderEffect(final AtarkaWorldRenderEffect ability) { public AtarkaWorldRenderEffect(final AtarkaWorldRenderEffect ability) {
super(ability); super(ability);
this.filter = ability.filter.copy();
} }
@Override @Override

View file

@ -0,0 +1,84 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.FightTargetSourceEffect;
import mage.constants.SubType;
import mage.abilities.keyword.ReachAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.AnotherPredicate;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author TheElk801
*/
public class AtzocanArcher extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("another creature");
static {
filter.add(new AnotherPredicate());
}
public AtzocanArcher(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.ARCHER);
this.power = new MageInt(1);
this.toughness = new MageInt(4);
// Reach
this.addAbility(ReachAbility.getInstance());
// When Atzocan Archer enters the battlefield, you may have it fight another target creature.
Effect effect = new FightTargetSourceEffect();
effect.setText("you may have it fight another target creature");
Ability ability = new EntersBattlefieldTriggeredAbility(effect, true);
ability.addTarget(new TargetCreaturePermanent(filter));
this.addAbility(ability);
}
public AtzocanArcher(final AtzocanArcher card) {
super(card);
}
@Override
public AtzocanArcher copy() {
return new AtzocanArcher(this);
}
}

View file

@ -0,0 +1,113 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.b;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureAllEffect;
import mage.abilities.keyword.CyclingAbility;
import mage.abilities.keyword.MorphAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicate;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.AbilityPredicate;
import mage.filter.predicate.permanent.PermanentIdPredicate;
import mage.game.Game;
import mage.target.Target;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author TheElk801
*/
public class Backslide extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with a morph ability");
static {
filter.add(new AbilityPredicate(MorphAbility.class));
}
public Backslide(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}");
// Turn target creature with a morph ability face down.
this.getSpellAbility().addEffect(new BackslideEffect());
this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter));
// Cycling {U}
this.addAbility(new CyclingAbility(new ManaCostsImpl("{U}")));
}
public Backslide(final Backslide card) {
super(card);
}
@Override
public Backslide copy() {
return new Backslide(this);
}
}
class BackslideEffect extends OneShotEffect {
BackslideEffect() {
super(Outcome.Benefit);
this.staticText = "Turn target creature with a morph ability face down.";
}
BackslideEffect(final BackslideEffect effect) {
super(effect);
}
@Override
public BackslideEffect copy() {
return new BackslideEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Predicate pred = new PermanentIdPredicate(UUID.randomUUID());
for (Target target : source.getTargets()) {
for (UUID targetId : target.getTargets()) {
pred = Predicates.or(pred, new PermanentIdPredicate(targetId));
}
}
FilterCreaturePermanent filter = new FilterCreaturePermanent();
filter.add(pred);
game.addEffect(new BecomesFaceDownCreatureAllEffect(filter), source);
return true;
}
}

View file

@ -52,7 +52,7 @@ public class BehindTheScenes extends CardImpl {
// Creatures you control have skulk. // Creatures you control have skulk.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new GainAbilityControlledEffect(SkulkAbility.getInstance(), Duration.WhileOnBattlefield, FILTER_PERMANENT_CREATURES))); new GainAbilityControlledEffect(new SkulkAbility(), Duration.WhileOnBattlefield, FILTER_PERMANENT_CREATURES)));
// {4}{W}: Creatures you control get +1/+1 until end of turn. // {4}{W}: Creatures you control get +1/+1 until end of turn.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD,

View file

@ -37,7 +37,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.*; import mage.constants.*;
import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerPredicate; import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game; import mage.game.Game;
/** /**
@ -54,7 +54,7 @@ public class BelligerentBrontodon extends CardImpl {
this.toughness = new MageInt(6); this.toughness = new MageInt(6);
// Each creature you control assigns combat damage equal to its toughness rather than its power. // Each creature you control assigns combat damage equal to its toughness rather than its power.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MilitantDinosaurCombatDamageRuleEffect())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BelligerentBrontodonCombatDamageRuleEffect()));
} }
public BelligerentBrontodon(final BelligerentBrontodon card) { public BelligerentBrontodon(final BelligerentBrontodon card) {
@ -67,31 +67,27 @@ public class BelligerentBrontodon extends CardImpl {
} }
} }
class MilitantDinosaurCombatDamageRuleEffect extends ContinuousEffectImpl { class BelligerentBrontodonCombatDamageRuleEffect extends ContinuousEffectImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); public BelligerentBrontodonCombatDamageRuleEffect() {
static {
filter.add(new ControllerPredicate(TargetController.YOU));
}
public MilitantDinosaurCombatDamageRuleEffect() {
super(Duration.WhileOnBattlefield, Outcome.Detriment); super(Duration.WhileOnBattlefield, Outcome.Detriment);
staticText = "Each creature you control assigns combat damage equal to its toughness rather than its power"; staticText = "Each creature you control assigns combat damage equal to its toughness rather than its power";
} }
public MilitantDinosaurCombatDamageRuleEffect(final MilitantDinosaurCombatDamageRuleEffect effect) { public BelligerentBrontodonCombatDamageRuleEffect(final BelligerentBrontodonCombatDamageRuleEffect effect) {
super(effect); super(effect);
} }
@Override @Override
public MilitantDinosaurCombatDamageRuleEffect copy() { public BelligerentBrontodonCombatDamageRuleEffect copy() {
return new MilitantDinosaurCombatDamageRuleEffect(this); return new BelligerentBrontodonCombatDamageRuleEffect(this);
} }
@Override @Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
// Change the rule // Change the rule
FilterCreaturePermanent filter = new FilterCreaturePermanent();
filter.add(new ControllerIdPredicate(source.getControllerId()));
game.getCombat().setUseToughnessForDamage(true); game.getCombat().setUseToughnessForDamage(true);
game.getCombat().addUseToughnessForDamageFilter(filter); game.getCombat().addUseToughnessForDamageFilter(filter);
return true; return true;

View file

@ -62,7 +62,7 @@ public class BelligerentSliver extends CardImpl {
this.toughness = new MageInt(2); this.toughness = new MageInt(2);
// Sliver creatures you control have menace. (They can't be blocked except by two or more creatures.)" // Sliver creatures you control have menace. (They can't be blocked except by two or more creatures.)"
Effect effect = new GainAbilityAllEffect(MenaceAbility.getInstance(), Duration.WhileOnBattlefield, filter); Effect effect = new GainAbilityAllEffect(new MenaceAbility(), Duration.WhileOnBattlefield, filter);
effect.setText("Sliver creatures you control have menace. (They can't be blocked except by two or more creatures.)"); effect.setText("Sliver creatures you control have menace. (They can't be blocked except by two or more creatures.)");
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect));
} }

View file

@ -36,8 +36,8 @@ import mage.abilities.keyword.VigilanceAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.ComparisonType; import mage.constants.ComparisonType;
import mage.constants.SubType;
import mage.filter.FilterCard; import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.mageobject.CardTypePredicate;
@ -49,7 +49,7 @@ import mage.target.common.TargetCardInYourGraveyard;
* @author TacomenX * @author TacomenX
*/ */
public class BishopOfRebirth extends CardImpl { public class BishopOfRebirth extends CardImpl {
private static final FilterCard filter = new FilterCreatureCard("creature card with converted mana cost 3 or less from your graveyard"); private static final FilterCard filter = new FilterCreatureCard("creature card with converted mana cost 3 or less from your graveyard");
static { static {
@ -59,7 +59,7 @@ public class BishopOfRebirth extends CardImpl {
public BishopOfRebirth(UUID ownerId, CardSetInfo setInfo) { public BishopOfRebirth(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}");
this.subtype.add(SubType.VAMPIRE); this.subtype.add(SubType.VAMPIRE);
this.subtype.add(SubType.CLERIC); this.subtype.add(SubType.CLERIC);
this.power = new MageInt(3); this.power = new MageInt(3);
@ -69,7 +69,8 @@ public class BishopOfRebirth extends CardImpl {
this.addAbility(VigilanceAbility.getInstance()); this.addAbility(VigilanceAbility.getInstance());
// Whenever Bishop of Rebirth attacks, you may return target creature card with converted mana cost 3 or less from your graveyard to the battlefield. // Whenever Bishop of Rebirth attacks, you may return target creature card with converted mana cost 3 or less from your graveyard to the battlefield.
Ability ability = new AttacksTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), true); Ability ability = new AttacksTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect()
.setText("you may return target creature card with converted mana cost 3 or less from your graveyard to the battlefield"), true);
ability.addTarget(new TargetCardInYourGraveyard(filter)); ability.addTarget(new TargetCardInYourGraveyard(filter));
this.addAbility(ability); this.addAbility(ability);
} }

View file

@ -33,15 +33,15 @@ import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.LoseLifeTargetEffect;
import mage.constants.SubType;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.TargetController; import mage.constants.TargetController;
import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.SubtypePredicate; import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.filter.predicate.permanent.ControllerPredicate; import mage.filter.predicate.permanent.ControllerPredicate;
import mage.target.TargetPlayer; import mage.target.common.TargetOpponent;
/** /**
* *
@ -64,9 +64,9 @@ public class BishopOfTheBloodstained extends CardImpl {
this.power = new MageInt(3); this.power = new MageInt(3);
this.toughness = new MageInt(3); this.toughness = new MageInt(3);
// When Bishop of the Bloodstained enters the battlefield, target player loses 1 life for each vampire you control. // When Bishop of the Bloodstained enters the battlefield, target opponent loses 1 life for each vampire you control.
Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(new PermanentsOnBattlefieldCount(filter))); Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(new PermanentsOnBattlefieldCount(filter)));
ability.addTarget(new TargetPlayer()); ability.addTarget(new TargetOpponent());
this.addAbility(ability); this.addAbility(ability);
} }

View file

@ -0,0 +1,65 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.b;
import java.util.UUID;
import mage.MageInt;
import mage.constants.SubType;
import mage.abilities.keyword.LifelinkAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
/**
*
* @author TheElk801
*/
public class BishopsSoldier extends CardImpl {
public BishopsSoldier(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
this.subtype.add(SubType.VAMPIRE);
this.subtype.add(SubType.SOLDIER);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// Lifelink
this.addAbility(LifelinkAbility.getInstance());
}
public BishopsSoldier(final BishopsSoldier card) {
super(card);
}
@Override
public BishopsSoldier copy() {
return new BishopsSoldier(this);
}
}

View file

@ -0,0 +1,81 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.b;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.SacrificeSourceCost;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.LoseLifeTargetEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.target.common.TargetOpponent;
/**
*
* @author TheElk801
*/
public class BlightKeeper extends CardImpl {
public BlightKeeper(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}");
this.subtype.add(SubType.BAT);
this.subtype.add(SubType.IMP);
this.power = new MageInt(1);
this.toughness = new MageInt(1);
// Flying
this.addAbility(FlyingAbility.getInstance());
// {7}{B}, {T}, Sacrifice Blight Keeper: Target opponent loses 4 life and you gain 4 life.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new LoseLifeTargetEffect(4), new ManaCostsImpl("{7}{B}"));
ability.addEffect(new GainLifeEffect(4).setText("and you gain 4 life"));
ability.addTarget(new TargetOpponent());
ability.addCost(new TapSourceCost());
ability.addCost(new SacrificeSourceCost());
this.addAbility(ability);
}
public BlightKeeper(final BlightKeeper card) {
super(card);
}
@Override
public BlightKeeper copy() {
return new BlightKeeper(this);
}
}

View file

@ -0,0 +1,64 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.b;
import java.util.UUID;
import mage.abilities.effects.common.PreventAllDamageToAllEffect;
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
import mage.abilities.keyword.HexproofAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import static mage.filter.StaticFilters.FILTER_PERMANENT_CREATURE;
import static mage.filter.StaticFilters.FILTER_PERMANENT_CREATURES;
/**
*
* @author LevelX2
*/
public class BlindingFog extends CardImpl {
public BlindingFog(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}");
// Prevent all damage that would be dealt to creatures this turn.
this.getSpellAbility().addEffect(new PreventAllDamageToAllEffect(Duration.EndOfTurn, FILTER_PERMANENT_CREATURES));
// Creatures you control gain hexproof until end of turn.
this.getSpellAbility().addEffect(new GainAbilityControlledEffect(HexproofAbility.getInstance(), Duration.EndOfTurn, FILTER_PERMANENT_CREATURE, false));
}
public BlindingFog(final BlindingFog card) {
super(card);
}
@Override
public BlindingFog copy() {
return new BlindingFog(this);
}
}

View file

@ -60,7 +60,7 @@ public class BloodChinRager extends CardImpl {
this.toughness = new MageInt(2); this.toughness = new MageInt(2);
// Whenever Blood-Chin Rager attacks, Warrior creatures you control gain menace until end of turn. (They can't be blocked except by two or more creatures.) // Whenever Blood-Chin Rager attacks, Warrior creatures you control gain menace until end of turn. (They can't be blocked except by two or more creatures.)
this.addAbility(new AttacksTriggeredAbility(new GainAbilityAllEffect(MenaceAbility.getInstance(), Duration.EndOfTurn, filter), false)); this.addAbility(new AttacksTriggeredAbility(new GainAbilityAllEffect(new MenaceAbility(), Duration.EndOfTurn, filter), false));
} }
public BloodChinRager(final BloodChinRager card) { public BloodChinRager(final BloodChinRager card) {

View file

@ -0,0 +1,145 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.b;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.Cards;
import mage.choices.Choice;
import mage.choices.ChoiceImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.game.Game;
import mage.players.Player;
/**
*
* @author TheElk801
*/
public class BloodOath extends CardImpl {
public BloodOath(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{R}");
// Choose a card type. Target opponent reveals his or her hand. Blood Oath deals 3 damage to that player for each card of the chosen type revealed this way.
this.getSpellAbility().addEffect(new BloodOathEffect());
}
public BloodOath(final BloodOath card) {
super(card);
}
@Override
public BloodOath copy() {
return new BloodOath(this);
}
}
class BloodOathEffect extends OneShotEffect {
private static final Set<String> choice = new LinkedHashSet<>();
static {
choice.add(CardType.ARTIFACT.toString());
choice.add(CardType.CREATURE.toString());
choice.add(CardType.ENCHANTMENT.toString());
choice.add(CardType.INSTANT.toString());
choice.add(CardType.LAND.toString());
choice.add(CardType.PLANESWALKER.toString());
choice.add(CardType.SORCERY.toString());
choice.add(CardType.TRIBAL.toString());
}
public BloodOathEffect() {
super(Outcome.Benefit);
staticText = "Choose a card type. Target opponent reveals his or her hand. {this} deals 3 damage to that player for each card of the chosen type revealed this way";
}
public BloodOathEffect(final BloodOathEffect effect) {
super(effect);
}
@Override
public BloodOathEffect copy() {
return new BloodOathEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
MageObject sourceObject = game.getObject(source.getSourceId());
Player player = game.getPlayer(source.getControllerId());
Player opponent = game.getPlayer(source.getFirstTarget());
if (player != null && opponent != null && sourceObject != null) {
Choice choiceImpl = new ChoiceImpl();
choiceImpl.setChoices(choice);
while (player.canRespond() && !player.choose(Outcome.Neutral, choiceImpl, game)) {
}
CardType type = null;
String choosenType = choiceImpl.getChoice();
if (choosenType.equals(CardType.ARTIFACT.toString())) {
type = CardType.ARTIFACT;
} else if (choosenType.equals(CardType.LAND.toString())) {
type = CardType.LAND;
} else if (choosenType.equals(CardType.CREATURE.toString())) {
type = CardType.CREATURE;
} else if (choosenType.equals(CardType.ENCHANTMENT.toString())) {
type = CardType.ENCHANTMENT;
} else if (choosenType.equals(CardType.INSTANT.toString())) {
type = CardType.INSTANT;
} else if (choosenType.equals(CardType.SORCERY.toString())) {
type = CardType.SORCERY;
} else if (choosenType.equals(CardType.PLANESWALKER.toString())) {
type = CardType.PLANESWALKER;
} else if (choosenType.equals(CardType.TRIBAL.toString())) {
type = CardType.TRIBAL;
}
if (type != null) {
Cards hand = opponent.getHand();
opponent.revealCards(sourceObject.getIdName(), hand, game);
Set<Card> cards = hand.getCards(game);
int count = 0;
for (Card card : cards) {
if (card != null && card.getCardType().contains(type)) {
count += 1;
}
}
opponent.damage(count * 3, source.getSourceId(), game, false, true);
return true;
}
}
return false;
}
}

View file

@ -85,7 +85,7 @@ public class BloodbondMarch extends CardImpl {
return false; return false;
} }
Spell spell = (Spell) game.getStack().getStackObject(targetPointer.getFirst(game, source)); Spell spell = game.getSpellOrLKIStack(this.getTargetPointer().getFirst(game, source));
if (spell == null) { if (spell == null) {
return false; return false;

View file

@ -0,0 +1,70 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.b;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.common.UntapTargetEffect;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Zone;
import mage.target.common.TargetLandPermanent;
/**
*
* @author TheElk801
*/
public class BlossomDryad extends CardImpl {
public BlossomDryad(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
this.subtype.add(SubType.DRYAD);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// {t}: Untap target land.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new UntapTargetEffect(), new TapSourceCost());
ability.addTarget(new TargetLandPermanent());
this.addAbility(ability);
}
public BlossomDryad(final BlossomDryad card) {
super(card);
}
@Override
public BlossomDryad copy() {
return new BlossomDryad(this);
}
}

View file

@ -60,7 +60,7 @@ public class BobaFett extends CardImpl {
this.toughness = new MageInt(4); this.toughness = new MageInt(4);
// Menace // Menace
this.addAbility(MenaceAbility.getInstance()); this.addAbility(new MenaceAbility());
// Deathtouch // Deathtouch
this.addAbility(DeathtouchAbility.getInstance()); this.addAbility(DeathtouchAbility.getInstance());

View file

@ -48,7 +48,7 @@ public class BoggartBrute extends CardImpl {
this.toughness = new MageInt(2); this.toughness = new MageInt(2);
// Menace // Menace
this.addAbility(MenaceAbility.getInstance()); this.addAbility(new MenaceAbility());
} }
public BoggartBrute(final BoggartBrute card) { public BoggartBrute(final BoggartBrute card) {

View file

@ -0,0 +1,65 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.b;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.keyword.CantAttackAloneAbility;
import mage.abilities.keyword.CantBlockAloneAbility;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
/**
*
* @author TheElk801
*/
public class BondedHorncrest extends CardImpl {
public BondedHorncrest(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}");
this.subtype.add(SubType.DINOSAUR);
this.power = new MageInt(5);
this.toughness = new MageInt(5);
// Bonded Horncrest can't attack or block alone.
this.addAbility(new CantAttackAloneAbility());
this.addAbility(CantBlockAloneAbility.getInstance());
}
public BondedHorncrest(final BondedHorncrest card) {
super(card);
}
@Override
public BondedHorncrest copy() {
return new BondedHorncrest(this);
}
}

View file

@ -65,7 +65,7 @@ public class BontuTheGlorified extends CardImpl {
this.toughness = new MageInt(6); this.toughness = new MageInt(6);
//Menace //Menace
this.addAbility(MenaceAbility.getInstance()); this.addAbility(new MenaceAbility());
//Indestructible //Indestructible
this.addAbility(IndestructibleAbility.getInstance()); this.addAbility(IndestructibleAbility.getInstance());

View file

@ -48,7 +48,7 @@ import mage.players.Player;
public class BounteousKirin extends CardImpl { public class BounteousKirin extends CardImpl {
public BounteousKirin(UUID ownerId, CardSetInfo setInfo) { public BounteousKirin(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{G}{G}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}{G}");
addSuperType(SuperType.LEGENDARY); addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.KIRIN, SubType.SPIRIT); this.subtype.add(SubType.KIRIN, SubType.SPIRIT);
@ -89,7 +89,7 @@ class BounteousKirinEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Spell spell = game.getState().getStack().getSpell(getTargetPointer().getFirst(game, source)); Spell spell = game.getSpellOrLKIStack(this.getTargetPointer().getFirst(game, source));
if (spell != null) { if (spell != null) {
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (controller != null) { if (controller != null) {

View file

@ -0,0 +1,63 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.b;
import java.util.UUID;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.target.common.TargetAttackingCreature;
/**
*
* @author TheElk801
*/
public class BrightReprisal extends CardImpl {
public BrightReprisal(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{W}");
// Destroy target attacking creature.
this.getSpellAbility().addEffect(new DestroyTargetEffect());
this.getSpellAbility().addTarget(new TargetAttackingCreature());
// Draw a card.
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1));
}
public BrightReprisal(final BrightReprisal card) {
super(card);
}
@Override
public BrightReprisal copy() {
return new BrightReprisal(this);
}
}

View file

@ -29,13 +29,13 @@ package mage.cards.b;
import java.util.UUID; import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.LimitedTimesPerTurnActivatedAbility; import mage.abilities.common.LimitedTimesPerTurnActivatedAbility;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.LookLibraryControllerEffect; import mage.abilities.effects.common.LookLibraryControllerEffect;
import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
@ -43,6 +43,7 @@ import mage.abilities.keyword.FirstStrikeAbility;
import mage.cards.*; import mage.cards.*;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
@ -55,7 +56,7 @@ import mage.players.Player;
public class BrutalDeceiver extends CardImpl { public class BrutalDeceiver extends CardImpl {
public BrutalDeceiver(UUID ownerId, CardSetInfo setInfo) { public BrutalDeceiver(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}");
this.subtype.add(SubType.SPIRIT); this.subtype.add(SubType.SPIRIT);
this.power = new MageInt(2); this.power = new MageInt(2);
@ -65,9 +66,7 @@ public class BrutalDeceiver extends CardImpl {
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new LookLibraryControllerEffect(), new GenericManaCost(1))); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new LookLibraryControllerEffect(), new GenericManaCost(1)));
// {2}: Reveal the top card of your library. If it's a land card, {this} gets +1/+0 and gains first strike until end of turn. // {2}: Reveal the top card of your library. If it's a land card, {this} gets +1/+0 and gains first strike until end of turn.
Ability ability = new BrutalDeceiverAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{2}")); this.addAbility(new LimitedTimesPerTurnActivatedAbility(Zone.BATTLEFIELD, new BrutalDeceiverEffect(), new ManaCostsImpl("{2}")));
ability.addEffect(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn));
this.addAbility(ability);
} }
public BrutalDeceiver(final BrutalDeceiver card) { public BrutalDeceiver(final BrutalDeceiver card) {
@ -80,38 +79,39 @@ public class BrutalDeceiver extends CardImpl {
} }
} }
class BrutalDeceiverAbility extends LimitedTimesPerTurnActivatedAbility { class BrutalDeceiverEffect extends OneShotEffect {
public BrutalDeceiverAbility(Zone zone, Effect effect, Cost cost) { public BrutalDeceiverEffect() {
super(zone, effect, cost); super(Outcome.BoostCreature);
this.staticText = "Reveal the top card of your library. If it's a land card, {this} gets +1/+0 and gains first strike until end of turn";
} }
public BrutalDeceiverAbility(BrutalDeceiverAbility ability) { public BrutalDeceiverEffect(final BrutalDeceiverEffect effect) {
super(ability); super(effect);
} }
@Override @Override
public BrutalDeceiverAbility copy() { public BrutalDeceiverEffect copy() {
return new BrutalDeceiverAbility(this); return new BrutalDeceiverEffect(this);
} }
@Override @Override
public boolean checkIfClause(Game game) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(this.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (player != null) { MageObject sourceObject = source.getSourceObject(game);
if (controller != null && sourceObject != null) {
Cards cards = new CardsImpl(); Cards cards = new CardsImpl();
Card card = player.getLibrary().getFromTop(game); Card card = controller.getLibrary().getFromTop(game);
cards.add(card); if (card != null) {
player.revealCards("Brutal Deceiver", cards, game); cards.add(card);
if (card != null && card.isLand()) { controller.revealCards(sourceObject.getIdName(), cards, game);
return true; if (card.isLand()) {
game.addEffect(new BoostSourceEffect(1, 0, Duration.EndOfTurn), source);
game.addEffect(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), source);
}
} }
return true;
} }
return false; return false;
} }
@Override
public String getRule() {
return "{2}: Reveal the top card of your library. If it's a land card, {this} gets +1/+0 and gains first strike until end of turn. Activate this ability only once each turn.";
}
} }

View file

@ -59,7 +59,7 @@ public class BullRancor extends CardImpl {
// As long as Bull Rancor is monstrous, creatures you control have menace. // As long as Bull Rancor is monstrous, creatures you control have menace.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(
new GainAbilityControlledEffect(MenaceAbility.getInstance(), Duration.WhileOnBattlefield), new GainAbilityControlledEffect(new MenaceAbility(), Duration.WhileOnBattlefield),
MonstrousCondition.instance, MonstrousCondition.instance,
"As long as Bull Rancor is monstrous, creatures you control have menace") "As long as Bull Rancor is monstrous, creatures you control have menace")
)); ));

View file

@ -52,11 +52,10 @@ import mage.target.common.TargetCardInYourGraveyard;
* *
* @author cbt33 * @author cbt33
*/ */
public class CabalInquisitor extends CardImpl { public class CabalInquisitor extends CardImpl {
public CabalInquisitor(UUID ownerId, CardSetInfo setInfo) { public CabalInquisitor(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}");
this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.MINION); this.subtype.add(SubType.MINION);
@ -82,11 +81,8 @@ public class CabalInquisitor extends CardImpl {
} }
} }
class ActivateAsSorceryConditionalActivatedAbility extends ActivatedAbilityImpl { class ActivateAsSorceryConditionalActivatedAbility extends ActivatedAbilityImpl {
private Condition condition;
private static final Effects emptyEffects = new Effects(); private static final Effects emptyEffects = new Effects();
public ActivateAsSorceryConditionalActivatedAbility(Zone zone, Effect effect, ManaCosts cost, Condition condition) { public ActivateAsSorceryConditionalActivatedAbility(Zone zone, Effect effect, ManaCosts cost, Condition condition) {
@ -95,10 +91,8 @@ class ActivateAsSorceryConditionalActivatedAbility extends ActivatedAbilityImpl
timing = TimingRule.SORCERY; timing = TimingRule.SORCERY;
} }
public ActivateAsSorceryConditionalActivatedAbility(final ActivateAsSorceryConditionalActivatedAbility ability) { public ActivateAsSorceryConditionalActivatedAbility(final ActivateAsSorceryConditionalActivatedAbility ability) {
super(ability); super(ability);
this.condition = ability.condition;
} }
@Override @Override
@ -109,14 +103,6 @@ class ActivateAsSorceryConditionalActivatedAbility extends ActivatedAbilityImpl
return super.getEffects(game, effectType); return super.getEffects(game, effectType);
} }
@Override
public boolean canActivate(UUID playerId, Game game) {
if (!condition.apply(game, this)) {
return false;
}
return super.canActivate(playerId, game);
}
@Override @Override
public ActivateAsSorceryConditionalActivatedAbility copy() { public ActivateAsSorceryConditionalActivatedAbility copy() {
return new ActivateAsSorceryConditionalActivatedAbility(this); return new ActivateAsSorceryConditionalActivatedAbility(this);

View file

@ -56,7 +56,7 @@ import mage.players.Player;
public class CallousDeceiver extends CardImpl { public class CallousDeceiver extends CardImpl {
public CallousDeceiver(UUID ownerId, CardSetInfo setInfo) { public CallousDeceiver(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
this.subtype.add(SubType.SPIRIT); this.subtype.add(SubType.SPIRIT);
this.power = new MageInt(1); this.power = new MageInt(1);

View file

@ -53,6 +53,7 @@ public class CandelabraOfTawnos extends CardImpl {
public CandelabraOfTawnos(UUID ownerId, CardSetInfo setInfo) { public CandelabraOfTawnos(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}"); super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}");
//TODO: Make ability properly copiable
// {X}, {T}: Untap X target lands. // {X}, {T}: Untap X target lands.
Effect effect = new UntapTargetEffect(); Effect effect = new UntapTargetEffect();
effect.setText("untap X target lands"); effect.setText("untap X target lands");

View file

@ -66,13 +66,13 @@ public class CaterwaulingBoggart extends CardImpl {
// Each Goblin you control has menace. (They can't be blocked except by two or more creatures.) // Each Goblin you control has menace. (They can't be blocked except by two or more creatures.)
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect( this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(
MenaceAbility.getInstance(), new MenaceAbility(),
Duration.WhileOnBattlefield, filterGoblin, Duration.WhileOnBattlefield, filterGoblin,
"Each Goblin you control has menace. (They can't be blocked except by two or more creatures.)"))); "Each Goblin you control has menace. (They can't be blocked except by two or more creatures.)")));
// Each Elemental you control has menace. (They can't be blocked except by two or more creatures.) // Each Elemental you control has menace. (They can't be blocked except by two or more creatures.)
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect( this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(
MenaceAbility.getInstance(), new MenaceAbility(),
Duration.WhileOnBattlefield, filterElemental, Duration.WhileOnBattlefield, filterElemental,
"Each Elemental you control has menace. (They can't be blocked except by two or more creatures.)"))); "Each Elemental you control has menace. (They can't be blocked except by two or more creatures.)")));
} }

View file

@ -50,7 +50,7 @@ import mage.game.stack.Spell;
public class CelestialKirin extends CardImpl { public class CelestialKirin extends CardImpl {
public CelestialKirin(UUID ownerId, CardSetInfo setInfo) { public CelestialKirin(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}{W}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}");
addSuperType(SuperType.LEGENDARY); addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.KIRIN); this.subtype.add(SubType.KIRIN);
this.subtype.add(SubType.SPIRIT); this.subtype.add(SubType.SPIRIT);
@ -92,7 +92,7 @@ class CelestialKirinEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Spell spell = game.getState().getStack().getSpell(getTargetPointer().getFirst(game, source)); Spell spell = game.getSpellOrLKIStack(this.getTargetPointer().getFirst(game, source));
if (spell != null) { if (spell != null) {
int cmc = spell.getConvertedManaCost(); int cmc = spell.getConvertedManaCost();
FilterPermanent filter = new FilterPermanent(); FilterPermanent filter = new FilterPermanent();

View file

@ -75,6 +75,7 @@ public class ChampionOfStraySouls extends CardImpl {
* ability, before you pay any costs. You can't target any of the * ability, before you pay any costs. You can't target any of the
* creatures you sacrifice. * creatures you sacrifice.
*/ */
//TODO: Make ability properly copiable
// {3}{B}{B}, {T}, Sacrifice X other creatures: Return X target creatures from your graveyard to the battlefield. // {3}{B}{B}, {T}, Sacrifice X other creatures: Return X target creatures from your graveyard to the battlefield.
Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect(); Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect();
effect.setText("Return X target creatures from your graveyard to the battlefield"); effect.setText("Return X target creatures from your graveyard to the battlefield");

View file

@ -91,7 +91,7 @@ public class ChandraTheFirebrand extends CardImpl {
class ChandraTheFirebrandAbility extends DelayedTriggeredAbility { class ChandraTheFirebrandAbility extends DelayedTriggeredAbility {
ChandraTheFirebrandAbility() { ChandraTheFirebrandAbility() {
super(new CopyTargetSpellEffect(), Duration.EndOfTurn); super(new CopyTargetSpellEffect(true), Duration.EndOfTurn);
} }
ChandraTheFirebrandAbility(final ChandraTheFirebrandAbility ability) { ChandraTheFirebrandAbility(final ChandraTheFirebrandAbility ability) {

View file

@ -56,7 +56,7 @@ public class ChitinousCloak extends CardImpl {
// Equipped creature gets +2/+2 and has menace. // Equipped creature gets +2/+2 and has menace.
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(2, 2)); Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(2, 2));
Effect effect = new GainAbilityAttachedEffect(MenaceAbility.getInstance(), AttachmentType.EQUIPMENT); Effect effect = new GainAbilityAttachedEffect(new MenaceAbility(), AttachmentType.EQUIPMENT);
effect.setText("and has menace"); effect.setText("and has menace");
ability.addEffect(effect); ability.addEffect(effect);
this.addAbility(ability); this.addAbility(ability);

View file

@ -61,13 +61,13 @@ public class ChitteringHost extends MeldCard {
this.addAbility(HasteAbility.getInstance()); this.addAbility(HasteAbility.getInstance());
// Menace <i>(This creature can't be blocked except by two or more creatures. // Menace <i>(This creature can't be blocked except by two or more creatures.
this.addAbility(MenaceAbility.getInstance()); this.addAbility(new MenaceAbility());
// When Chittering Host enters the battlefield, other creatures you control get +1/+0 and gain menace until end of turn. // When Chittering Host enters the battlefield, other creatures you control get +1/+0 and gain menace until end of turn.
Effect effect = new BoostControlledEffect(1, 0, Duration.EndOfTurn, true); Effect effect = new BoostControlledEffect(1, 0, Duration.EndOfTurn, true);
effect.setText("other creatures you control get +1/+0"); effect.setText("other creatures you control get +1/+0");
Ability ability = new EntersBattlefieldTriggeredAbility(effect, false); Ability ability = new EntersBattlefieldTriggeredAbility(effect, false);
effect = new GainAbilityAllEffect(MenaceAbility.getInstance(), Duration.EndOfTurn, new FilterControlledCreaturePermanent("other creatures"), true); effect = new GainAbilityAllEffect(new MenaceAbility(), Duration.EndOfTurn, new FilterControlledCreaturePermanent("other creatures"), true);
effect.setText("and gain menace until end of turn"); effect.setText("and gain menace until end of turn");
ability.addEffect(effect); ability.addEffect(effect);
this.addAbility(ability); this.addAbility(ability);

View file

@ -59,14 +59,14 @@ import mage.game.permanent.token.Token;
public class ChronatogTotem extends CardImpl { public class ChronatogTotem extends CardImpl {
public ChronatogTotem(UUID ownerId, CardSetInfo setInfo) { public ChronatogTotem(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
// {tap}: Add {U} to your mana pool. // {tap}: Add {U} to your mana pool.
this.addAbility(new BlueManaAbility()); this.addAbility(new BlueManaAbility());
// {1}{U}: Chronatog Totem becomes a 1/2 blue Atog artifact creature until end of turn. // {1}{U}: Chronatog Totem becomes a 1/2 blue Atog artifact creature until end of turn.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new ChronatogTotemToken(), "", Duration.EndOfTurn), new ManaCostsImpl<>("{1}{U}"))); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new ChronatogTotemToken(), "", Duration.EndOfTurn), new ManaCostsImpl<>("{1}{U}")));
// {0}: Chronatog Totem gets +3/+3 until end of turn. You skip your next turn. Activate this ability only once each turn and only if Chronatog Totem is a creature. // {0}: Chronatog Totem gets +3/+3 until end of turn. You skip your next turn. Activate this ability only once each turn and only if Chronatog Totem is a creature.
Ability ability = new ChronatogTotemAbility( Ability ability = new ChronatogTotemAbility(
Zone.BATTLEFIELD, Zone.BATTLEFIELD,
@ -91,8 +91,6 @@ class ChronatogTotemAbility extends LimitedTimesPerTurnActivatedAbility {
private static final Effects emptyEffects = new Effects(); private static final Effects emptyEffects = new Effects();
private final Condition condition;
public ChronatogTotemAbility(Zone zone, Effect effect, Cost cost, Condition condition) { public ChronatogTotemAbility(Zone zone, Effect effect, Cost cost, Condition condition) {
super(zone, effect, cost); super(zone, effect, cost);
this.condition = condition; this.condition = condition;
@ -100,7 +98,6 @@ class ChronatogTotemAbility extends LimitedTimesPerTurnActivatedAbility {
public ChronatogTotemAbility(ChronatogTotemAbility ability) { public ChronatogTotemAbility(ChronatogTotemAbility ability) {
super(ability); super(ability);
this.condition = ability.condition;
} }
@Override @Override
@ -111,14 +108,6 @@ class ChronatogTotemAbility extends LimitedTimesPerTurnActivatedAbility {
return super.getEffects(game, effectType); return super.getEffects(game, effectType);
} }
@Override
public boolean canActivate(UUID playerId, Game game) {
if (!condition.apply(game, this)) {
return false;
}
return super.canActivate(playerId, game);
}
@Override @Override
public ChronatogTotemAbility copy() { public ChronatogTotemAbility copy() {
return new ChronatogTotemAbility(this); return new ChronatogTotemAbility(this);

View file

@ -54,7 +54,7 @@ import mage.target.TargetPlayer;
public class CloudhoofKirin extends CardImpl { public class CloudhoofKirin extends CardImpl {
public CloudhoofKirin(UUID ownerId, CardSetInfo setInfo) { public CloudhoofKirin(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}{U}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}");
addSuperType(SuperType.LEGENDARY); addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.KIRIN); this.subtype.add(SubType.KIRIN);
this.subtype.add(SubType.SPIRIT); this.subtype.add(SubType.SPIRIT);
@ -98,10 +98,10 @@ class CloudhoofKirinEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); Spell spell = game.getSpellOrLKIStack(this.getTargetPointer().getFirst(game, source));
if (spell != null) { if (spell != null) {
Player targetPlayer = null; Player targetPlayer = null;
for(Target target: source.getTargets()) { for (Target target : source.getTargets()) {
if (target instanceof TargetPlayer) { if (target instanceof TargetPlayer) {
targetPlayer = game.getPlayer(target.getFirstTarget()); targetPlayer = game.getPlayer(target.getFirstTarget());
} }

View file

@ -60,7 +60,7 @@ public class ClovenCasting extends CardImpl {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{5}{U}{R}"); super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{5}{U}{R}");
// Whenever you cast a multicolored instant or sorcery spell, you may pay {1}. If you do, copy that spell. You may choose new targets for the copy. // Whenever you cast a multicolored instant or sorcery spell, you may pay {1}. If you do, copy that spell. You may choose new targets for the copy.
Effect effect = new CopyTargetSpellEffect(); Effect effect = new CopyTargetSpellEffect(true);
effect.setText("copy that spell. You may choose new targets for the copy"); effect.setText("copy that spell. You may choose new targets for the copy");
this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid(effect, new GenericManaCost(1)), filter, true, true)); this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid(effect, new GenericManaCost(1)), filter, true, true));
} }

View file

@ -0,0 +1,64 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.c;
import java.util.UUID;
import mage.MageInt;
import mage.constants.SubType;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
/**
*
* @author TheElk801
*/
public class ColossalDreadmaw extends CardImpl {
public ColossalDreadmaw(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}");
this.subtype.add(SubType.DINOSAUR);
this.power = new MageInt(6);
this.toughness = new MageInt(6);
// Trample
this.addAbility(TrampleAbility.getInstance());
}
public ColossalDreadmaw(final ColossalDreadmaw card) {
super(card);
}
@Override
public ColossalDreadmaw copy() {
return new ColossalDreadmaw(this);
}
}

View file

@ -33,7 +33,7 @@ import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.DrawDiscardControllerEffect; import mage.abilities.effects.common.DrawDiscardControllerEffect;
import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
import mage.abilities.mana.ColorlessManaAbility; import mage.abilities.mana.ColorlessManaAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
@ -71,7 +71,7 @@ public class ConquerorsFoothold extends CardImpl {
// {6}, {T}: Return target card from your graveyard to your hand. // {6}, {T}: Return target card from your graveyard to your hand.
SimpleActivatedAbility ability3 = new SimpleActivatedAbility(Zone.BATTLEFIELD, SimpleActivatedAbility ability3 = new SimpleActivatedAbility(Zone.BATTLEFIELD,
new ReturnToHandTargetEffect(), new ReturnFromGraveyardToHandTargetEffect(),
new ManaCostsImpl("{6}")); new ManaCostsImpl("{6}"));
ability3.addCost(new TapSourceCost()); ability3.addCost(new TapSourceCost());
ability3.addTarget(new TargetCardInYourGraveyard()); ability3.addTarget(new TargetCardInYourGraveyard());

View file

@ -0,0 +1,62 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.c;
import java.util.UUID;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.game.permanent.token.TreasureToken;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author TheElk801
*/
public class ContractKilling extends CardImpl {
public ContractKilling(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}{B}");
// Destroy target creature. Create two colorless Treasure artifact tokens with "{T}, Sacrifice this artifact: Add one mana of any color to your mana pool."
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
this.getSpellAbility().addEffect(new DestroyTargetEffect());
this.getSpellAbility().addEffect(new CreateTokenEffect(new TreasureToken(), 2));
}
public ContractKilling(final ContractKilling card) {
super(card);
}
@Override
public ContractKilling copy() {
return new ContractKilling(this);
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.c;
import java.util.UUID;
import mage.abilities.costs.common.SacrificeTargetCost;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.common.FilterControlledPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.target.common.TargetControlledPermanent;
/**
*
* @author TheElk801
*/
public class CostlyPlunder extends CardImpl {
private static final FilterControlledPermanent filter = new FilterControlledPermanent("an artifact or creature");
static {
filter.add(Predicates.or(
new CardTypePredicate(CardType.ARTIFACT),
new CardTypePredicate(CardType.CREATURE)
));
}
public CostlyPlunder(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}");
// As an additional cost to cast Costly Plunder, sacrifice an artifact or creature.
this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledPermanent(1, 1, filter, true)));
// Draw two cards.
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2));
}
public CostlyPlunder(final CostlyPlunder card) {
super(card);
}
@Override
public CostlyPlunder copy() {
return new CostlyPlunder(this);
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.c;
import java.util.UUID;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author TheElk801
*/
public class CrashTheRamparts extends CardImpl {
public CrashTheRamparts(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}");
// Target creature gets +3/+3 and gains trample until end of turn.
Effect effect = new BoostTargetEffect(3, 3, Duration.EndOfTurn);
effect.setText("Target creature gets +3/+3");
this.getSpellAbility().addEffect(effect);
effect = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn);
effect.setText("and gains trample until end of turn");
this.getSpellAbility().addEffect(effect);
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
}
public CrashTheRamparts(final CrashTheRamparts card) {
super(card);
}
@Override
public CrashTheRamparts copy() {
return new CrashTheRamparts(this);
}
}

View file

@ -68,6 +68,7 @@ public class CrownOfDoom extends CardImpl {
effect.setText("it gets +2/+0 until end of turn"); effect.setText("it gets +2/+0 until end of turn");
this.addAbility(new AttacksAllTriggeredAbility(effect, false, StaticFilters.FILTER_PERMANENT_CREATURE, SetTargetPointer.PERMANENT, true)); this.addAbility(new AttacksAllTriggeredAbility(effect, false, StaticFilters.FILTER_PERMANENT_CREATURE, SetTargetPointer.PERMANENT, true));
//TODO: Make ability properly copiable
// {2}: Target player other than Crown of Doom's owner gains control of it. Activate this ability only during your turn. // {2}: Target player other than Crown of Doom's owner gains control of it. Activate this ability only during your turn.
Ability ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD, new CrownOfDoomEffect(), new ManaCostsImpl("{2}"), MyTurnCondition.instance); Ability ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD, new CrownOfDoomEffect(), new ManaCostsImpl("{2}"), MyTurnCondition.instance);
ability.addTarget(new TargetPlayer(1, 1, false, new FilterPlayer("player other than Crown of Doom's owner"))); ability.addTarget(new TargetPlayer(1, 1, false, new FilterPlayer("player other than Crown of Doom's owner")));

View file

@ -29,16 +29,17 @@ package mage.cards.c;
import java.util.UUID; import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.DealsDamageToACreatureTriggeredAbility;
import mage.abilities.common.LimitedTimesPerTurnActivatedAbility; import mage.abilities.common.LimitedTimesPerTurnActivatedAbility;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.abilities.effects.common.LookLibraryControllerEffect; import mage.abilities.effects.common.LookLibraryControllerEffect;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.*; import mage.cards.*;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType; import mage.constants.SubType;
@ -55,7 +56,7 @@ import mage.players.Player;
public class CruelDeceiver extends CardImpl { public class CruelDeceiver extends CardImpl {
public CruelDeceiver(UUID ownerId, CardSetInfo setInfo) { public CruelDeceiver(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}");
this.subtype.add(SubType.SPIRIT); this.subtype.add(SubType.SPIRIT);
this.power = new MageInt(2); this.power = new MageInt(2);
@ -82,7 +83,7 @@ class CruelDeceiverEffect extends OneShotEffect {
public CruelDeceiverEffect() { public CruelDeceiverEffect() {
super(Outcome.AddAbility); super(Outcome.AddAbility);
this.staticText = "Reveal the top card of your library. If it's a land card, {this} gets +2/+2 and gains trample until end of turn"; this.staticText = "Reveal the top card of your library. If it's a land card, {this} gains \"Whenever Cruel Deceiver deals damage to a creature, destroy that creature\" until end of turn";
} }
public CruelDeceiverEffect(final CruelDeceiverEffect effect) { public CruelDeceiverEffect(final CruelDeceiverEffect effect) {
@ -96,15 +97,17 @@ class CruelDeceiverEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (player != null) { MageObject sourceObject = source.getSourceObject(game);
if (controller != null && sourceObject != null) {
Cards cards = new CardsImpl(); Cards cards = new CardsImpl();
Card card = player.getLibrary().getFromTop(game); Card card = controller.getLibrary().getFromTop(game);
cards.add(card); if (card != null) {
player.revealCards("Cruel Deceiver", cards, game); cards.add(card);
if (card != null && card.isLand()) { controller.revealCards(sourceObject.getIdName(), cards, game);
game.addEffect(new BoostSourceEffect(2,2,Duration.EndOfTurn), source); if (card.isLand()) {
game.addEffect(new GainAbilitySourceEffect(TrampleAbility.getInstance(),Duration.EndOfTurn), source); game.addEffect(new GainAbilitySourceEffect(new DealsDamageToACreatureTriggeredAbility(new DestroyTargetEffect(true), false, false, true), Duration.EndOfTurn), source);
}
} }
return true; return true;
} }

View file

@ -0,0 +1,76 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.c;
import java.util.UUID;
import mage.abilities.Mode;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.AbilityPredicate;
import mage.target.common.TargetCreaturePermanent;
import mage.target.common.TargetEnchantmentPermanent;
/**
*
* @author TheElk801
*/
public class CrushingCanopy extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with flying");
static {
filter.add(new AbilityPredicate(FlyingAbility.class));
}
public CrushingCanopy(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}");
// Choose one --
// * Destroy target creature with flying.
this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter));
this.getSpellAbility().addEffect(new DestroyTargetEffect());
// * Destroy target enchantment.
Mode mode = new Mode();
mode.getTargets().add(new TargetEnchantmentPermanent());
mode.getEffects().add(new DestroyTargetEffect());
this.getSpellAbility().addMode(mode);
}
public CrushingCanopy(final CrushingCanopy card) {
super(card);
}
@Override
public CrushingCanopy copy() {
return new CrushingCanopy(this);
}
}

View file

@ -0,0 +1,136 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.c;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.GainControlTargetEffect;
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.ControllerIdPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPlayer;
import mage.target.common.TargetCreaturePermanent;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author TheElk801
*/
public class CulturalExchange extends CardImpl {
public CulturalExchange(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{U}{U}");
// Choose any number of creatures target player controls. Choose the same number of creatures another target player controls. Those players exchange control of those creatures.
this.getSpellAbility().addEffect(new CulturalExchangeEffect());
this.getSpellAbility().addTarget(new TargetPlayer(2));
}
public CulturalExchange(final CulturalExchange card) {
super(card);
}
@Override
public CulturalExchange copy() {
return new CulturalExchange(this);
}
}
class CulturalExchangeEffect extends OneShotEffect {
CulturalExchangeEffect() {
super(Outcome.Benefit);
this.staticText = "Choose any number of creatures target player controls. "
+ "Choose the same number of creatures another target player controls. "
+ "Those players exchange control of those creatures.";
}
CulturalExchangeEffect(final CulturalExchangeEffect effect) {
super(effect);
}
@Override
public CulturalExchangeEffect copy() {
return new CulturalExchangeEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player1 = game.getPlayer(targetPointer.getTargets(game, source).get(0));
Player player2 = game.getPlayer(targetPointer.getTargets(game, source).get(1));
Player controller = game.getPlayer(source.getControllerId());
if (player1 == null || player2 == null || controller == null) {
return false;
}
FilterCreaturePermanent filter1 = new FilterCreaturePermanent("creatures " + player1.getLogName() + " controls");
FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creatures " + player2.getLogName() + " controls");
filter1.add(new ControllerIdPredicate(player1.getId()));
filter2.add(new ControllerIdPredicate(player2.getId()));
int creatureCount1 = game.getBattlefield().count(filter1, source.getSourceId(), source.getControllerId(), game);
int creatureCount2 = game.getBattlefield().count(filter2, source.getSourceId(), source.getControllerId(), game);
int creaturesToSwitch = Math.min(creatureCount1, creatureCount2);
if (creaturesToSwitch == 0) {
return true;
}
TargetCreaturePermanent target1 = new TargetCreaturePermanent(0, creaturesToSwitch, filter1, true);
if (target1.choose(Outcome.Benefit, controller.getId(), source.getSourceId(), game)) {
int otherToSwitch = target1.getTargets().size();
TargetCreaturePermanent target2 = new TargetCreaturePermanent(otherToSwitch, otherToSwitch, filter2, true);
if (target2.choose(Outcome.Benefit, controller.getId(), source.getSourceId(), game)) {
for (UUID creatureId : target1.getTargets()) {
Permanent creature = game.getPermanent(creatureId);
if (creature != null) {
ContinuousEffect effect = new GainControlTargetEffect(Duration.Custom, player2.getId());
game.informPlayers(player2.getLogName() + " gains control of " + creature.getLogName());
effect.setTargetPointer(new FixedTarget(creature, game));
game.addEffect(effect, source);
}
}
for (UUID creatureId : target2.getTargets()) {
Permanent creature = game.getPermanent(creatureId);
if (creature != null) {
ContinuousEffect effect = new GainControlTargetEffect(Duration.Custom, player1.getId());
game.informPlayers(player1.getLogName() + " gains control of " + creature.getLogName());
effect.setTargetPointer(new FixedTarget(creature, game));
game.addEffect(effect, source);
}
}
}
}
return true;
}
}

View file

@ -62,6 +62,7 @@ public class CuombajjWitches extends CardImpl {
this.power = new MageInt(1); this.power = new MageInt(1);
this.toughness = new MageInt(3); this.toughness = new MageInt(3);
//TODO: Make ability properly copiable
// {T}: Cuombajj Witches deals 1 damage to target creature or player and 1 damage to target creature or player of an opponent's choice. // {T}: Cuombajj Witches deals 1 damage to target creature or player and 1 damage to target creature or player of an opponent's choice.
Effect effect = new DamageTargetEffect(1); Effect effect = new DamageTargetEffect(1);
effect.setText("{this} deals 1 damage to target creature or player and 1 damage to target creature or player of an opponent's choice"); effect.setText("{this} deals 1 damage to target creature or player and 1 damage to target creature or player of an opponent's choice");

View file

@ -58,7 +58,7 @@ import mage.target.targetpointer.FixedTarget;
public class CurseOfEchoes extends CardImpl { public class CurseOfEchoes extends CardImpl {
public CurseOfEchoes(UUID ownerId, CardSetInfo setInfo) { public CurseOfEchoes(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{4}{U}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}");
this.subtype.add(SubType.AURA, SubType.CURSE); this.subtype.add(SubType.AURA, SubType.CURSE);
// Enchant player // Enchant player
@ -143,7 +143,7 @@ class CurseOfEchoesEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); Spell spell = game.getSpellOrLKIStack(this.getTargetPointer().getFirst(game, source));
if (spell != null) { if (spell != null) {
String chooseMessage = "Copy target spell? You may choose new targets for the copy."; String chooseMessage = "Copy target spell? You may choose new targets for the copy.";
for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {

View file

@ -50,7 +50,7 @@ public class CursedMinotaur extends CardImpl {
this.toughness = new MageInt(2); this.toughness = new MageInt(2);
// Menace // Menace
this.addAbility(MenaceAbility.getInstance()); this.addAbility(new MenaceAbility());
} }

View file

@ -28,21 +28,17 @@
package mage.cards.c; package mage.cards.c;
import java.util.UUID; import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.Effect;
import mage.abilities.effects.common.ReturnToHandFromBattlefieldAllEffect;
import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect;
import mage.abilities.keyword.OverloadAbility; import mage.abilities.keyword.OverloadAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.TargetController; import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.common.FilterNonlandPermanent; import mage.filter.common.FilterNonlandPermanent;
import mage.filter.predicate.permanent.ControllerPredicate; import mage.filter.predicate.permanent.ControllerPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.common.TargetNonlandPermanent; import mage.target.common.TargetNonlandPermanent;
/** /**
@ -58,14 +54,16 @@ public class CyclonicRift extends CardImpl {
} }
public CyclonicRift(UUID ownerId, CardSetInfo setInfo) { public CyclonicRift(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}"); super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}");
// Return target nonland permanent you don't control to its owner's hand. // Return target nonland permanent you don't control to its owner's hand.
this.getSpellAbility().addTarget(new TargetNonlandPermanent(filter)); this.getSpellAbility().addTarget(new TargetNonlandPermanent(filter));
this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); this.getSpellAbility().addEffect(new ReturnToHandTargetEffect());
// Overload {6}{U} (You may cast this spell for its overload cost. If you do, change its text by replacing all instances of "target" with "each.") // Overload {6}{U} (You may cast this spell for its overload cost. If you do, change its text by replacing all instances of "target" with "each.")
this.addAbility(new OverloadAbility(this, new CyclonicRiftEffect(), new ManaCostsImpl("{6}{U}"))); Effect effect = new ReturnToHandFromBattlefieldAllEffect(filter);
effect.setText("Return each nonland permanent you don't control to its owner's hand");
this.addAbility(new OverloadAbility(this, effect, new ManaCostsImpl("{6}{U}")));
} }
public CyclonicRift(final CyclonicRift card) { public CyclonicRift(final CyclonicRift card) {
@ -77,33 +75,3 @@ public class CyclonicRift extends CardImpl {
return new CyclonicRift(this); return new CyclonicRift(this);
} }
} }
class CyclonicRiftEffect extends OneShotEffect {
private static final FilterNonlandPermanent filter = new FilterNonlandPermanent();
public CyclonicRiftEffect() {
super(Outcome.ReturnToHand);
staticText = "Return each nonland permanent you don't control to its owner's hand";
}
public CyclonicRiftEffect(final CyclonicRiftEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
for (Permanent creature : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
if (!creature.getControllerId().equals(source.getControllerId())) {
creature.moveToZone(Zone.HAND, source.getSourceId(), game, true);
}
}
return true;
}
@Override
public CyclonicRiftEffect copy() {
return new CyclonicRiftEffect(this);
}
}

View file

@ -0,0 +1,61 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.d;
import java.util.UUID;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.effects.common.GainLifeEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.target.common.TargetCreatureOrPlayer;
/**
*
* @author TheElk801
*/
public class DarkNourishment extends CardImpl {
public DarkNourishment(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{B}");
// Dark Nourishment deals 3 damage to target creature or player. You gain 3 life.
this.getSpellAbility().addEffect(new DamageTargetEffect(3));
this.getSpellAbility().addTarget(new TargetCreatureOrPlayer());
this.getSpellAbility().addEffect(new GainLifeEffect(3));
}
public DarkNourishment(final DarkNourishment card) {
super(card);
}
@Override
public DarkNourishment copy() {
return new DarkNourishment(this);
}
}

View file

@ -63,7 +63,7 @@ public class DarthVader extends CardImpl {
this.nightCard = true; this.nightCard = true;
// Menace // Menace
this.addAbility(MenaceAbility.getInstance()); this.addAbility(new MenaceAbility());
// Lifelink // Lifelink
this.addAbility(LifelinkAbility.getInstance()); this.addAbility(LifelinkAbility.getInstance());

View file

@ -49,7 +49,10 @@ public class DeadeyeQuartermaster extends CardImpl {
private static final FilterCard filter = new FilterCard("an Equipment or Vehicle card"); private static final FilterCard filter = new FilterCard("an Equipment or Vehicle card");
static { static {
filter.add(Predicates.or(new SubtypePredicate(SubType.EQUIPMENT), new SubtypePredicate(SubType.EQUIPMENT))); filter.add(Predicates.or(
new SubtypePredicate(SubType.EQUIPMENT),
new SubtypePredicate(SubType.VEHICLE)
));
} }
public DeadeyeQuartermaster(UUID ownerId, CardSetInfo setInfo) { public DeadeyeQuartermaster(UUID ownerId, CardSetInfo setInfo) {

View file

@ -40,12 +40,9 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.TargetController;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.FilterCard; import mage.filter.FilterCard;
import mage.filter.predicate.Predicates; import mage.target.common.TargetCardInOpponentsGraveyard;
import mage.filter.predicate.other.OwnerPredicate;
import mage.target.common.TargetCardInASingleGraveyard;
/** /**
* *
@ -53,12 +50,6 @@ import mage.target.common.TargetCardInASingleGraveyard;
*/ */
public class DeadeyeTracker extends CardImpl { public class DeadeyeTracker extends CardImpl {
private static final FilterCard filter = new FilterCard("cards from an opponent's graveyard");
static {
filter.add(Predicates.not(new OwnerPredicate(TargetController.YOU)));
}
public DeadeyeTracker(UUID ownerId, CardSetInfo setInfo) { public DeadeyeTracker(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}");
@ -73,7 +64,7 @@ public class DeadeyeTracker extends CardImpl {
Effect effect = new ExploreSourceEffect(); Effect effect = new ExploreSourceEffect();
effect.setText("{this} explores"); effect.setText("{this} explores");
ability.addEffect(effect); ability.addEffect(effect);
ability.addTarget(new TargetCardInASingleGraveyard(2, 2, filter)); ability.addTarget(new TargetCardInOpponentsGraveyard(2, 2, new FilterCard("cards from an opponent's graveyard"), true));
this.addAbility(ability); this.addAbility(ability);
} }

View file

@ -0,0 +1,111 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.d;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
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.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetCardInGraveyard;
/**
*
* @author TheElk801
*/
public class DeathgorgeScavenger extends CardImpl {
public DeathgorgeScavenger(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
this.subtype.add(SubType.DINOSAUR);
this.power = new MageInt(3);
this.toughness = new MageInt(2);
// Whenever Deathgorge Scavenger enters the battlefield or attacks, you may exile target card from a graveyard. If a creature card is exiled this way, you gain 2 life. If a noncreature card is exiled this way, Deathgorge Scavenger gets +1/+1 until end of turn.
Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new DeathgorgeScavengerEffect(), true);
ability.addTarget(new TargetCardInGraveyard());
this.addAbility(ability);
}
public DeathgorgeScavenger(final DeathgorgeScavenger card) {
super(card);
}
@Override
public DeathgorgeScavenger copy() {
return new DeathgorgeScavenger(this);
}
}
class DeathgorgeScavengerEffect extends OneShotEffect {
public DeathgorgeScavengerEffect() {
super(Outcome.Benefit);
this.staticText = "exile target card from a graveyard. If a creature card is exiled this way, you gain 2 life. If a noncreature card is exiled this way, {this} gets +1/+1 until end of turn";
}
public DeathgorgeScavengerEffect(final DeathgorgeScavengerEffect effect) {
super(effect);
}
@Override
public DeathgorgeScavengerEffect copy() {
return new DeathgorgeScavengerEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Card card = game.getCard(getTargetPointer().getFirst(game, source));
if (card != null) {
controller.moveCards(card, Zone.EXILED, source, game);
if (card.isCreature()) {
controller.gainLife(2, game);
} else {
game.addEffect(new BoostSourceEffect(1, 1, Duration.EndOfTurn), source);
}
}
return true;
}
return false;
}
}

View file

@ -31,7 +31,7 @@ import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapTargetCost; import mage.abilities.costs.common.TapTargetCost;
import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
@ -59,7 +59,7 @@ public class DeathlessAncient extends CardImpl {
public DeathlessAncient(UUID ownerId, CardSetInfo setInfo) { public DeathlessAncient(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}");
this.subtype.add(SubType.VAMPIRE); this.subtype.add(SubType.VAMPIRE);
this.subtype.add(SubType.KNIGHT); this.subtype.add(SubType.KNIGHT);
this.power = new MageInt(4); this.power = new MageInt(4);
@ -69,7 +69,9 @@ public class DeathlessAncient extends CardImpl {
this.addAbility(FlyingAbility.getInstance()); this.addAbility(FlyingAbility.getInstance());
// Tap three untapped Vampires you control: Return Deathless Ancient from your graveyard to your hand. // Tap three untapped Vampires you control: Return Deathless Ancient from your graveyard to your hand.
this.addAbility(new SimpleActivatedAbility(Zone.GRAVEYARD, new ReturnToHandSourceEffect(), new TapTargetCost(new TargetControlledPermanent(3, 3, filter, true)))); this.addAbility(new SimpleActivatedAbility(Zone.GRAVEYARD,
new ReturnSourceFromGraveyardToHandEffect(),
new TapTargetCost(new TargetControlledPermanent(3, 3, filter, true))));
} }

View file

@ -58,6 +58,7 @@ public class DeclarationOfNaught extends CardImpl {
// As Declaration of Naught enters the battlefield, name a card. // As Declaration of Naught enters the battlefield, name a card.
this.addAbility(new AsEntersBattlefieldAbility(new NameACardEffect(NameACardEffect.TypeOfName.ALL))); this.addAbility(new AsEntersBattlefieldAbility(new NameACardEffect(NameACardEffect.TypeOfName.ALL)));
//TODO: Make ability properly copiable
// {U}: Counter target spell with the chosen name. // {U}: Counter target spell with the chosen name.
SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterTargetEffect(), new ManaCostsImpl("{U}")); SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterTargetEffect(), new ManaCostsImpl("{U}"));
ability.addTarget(new TargetSpell(filter)); ability.addTarget(new TargetSpell(filter));

View file

@ -68,6 +68,7 @@ public class DeepfireElemental extends CardImpl {
this.power = new MageInt(4); this.power = new MageInt(4);
this.toughness = new MageInt(4); this.toughness = new MageInt(4);
//TODO: Make ability properly copiable
// {X}{X}{1}: Destroy target artifact or creature with converted mana cost X. // {X}{X}{1}: Destroy target artifact or creature with converted mana cost X.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{X}{X}{1}")); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{X}{X}{1}"));
ability.addTarget(new TargetPermanent(filter)); ability.addTarget(new TargetPermanent(filter));

View file

@ -0,0 +1,69 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.d;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.BecomesBlockedTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
/**
*
* @author LevelX2
*/
public class DeeprootWarrior extends CardImpl {
public DeeprootWarrior(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}");
this.subtype.add(SubType.MERFOLK);
this.subtype.add(SubType.WARRIOR);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// Whenever Deeproot Warrior becomes blocked, it gets +1/+1 until end of turn.
Effect effect = new BoostSourceEffect(1, 1, Duration.EndOfTurn);
effect.setText("it gets +1/+1 until end of turn");
this.addAbility(new BecomesBlockedTriggeredAbility(effect, false));
}
public DeeprootWarrior(final DeeprootWarrior card) {
super(card);
}
@Override
public DeeprootWarrior copy() {
return new DeeprootWarrior(this);
}
}

View file

@ -52,7 +52,7 @@ public class Demoralize extends CardImpl {
// All creatures gain menace until end of turn. (They can't be blocked except by two or more creatures.) // All creatures gain menace until end of turn. (They can't be blocked except by two or more creatures.)
this.getSpellAbility().addEffect(new GainAbilityAllEffect(MenaceAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES)); this.getSpellAbility().addEffect(new GainAbilityAllEffect(new MenaceAbility(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES));
// Threshold If seven or more cards are in your graveyard, creatures can't block this turn. // Threshold If seven or more cards are in your graveyard, creatures can't block this turn.
this.getSpellAbility().addEffect( this.getSpellAbility().addEffect(

View file

@ -0,0 +1,62 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.d;
import java.util.UUID;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.ReturnToHandTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.game.permanent.token.TreasureToken;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author TheElk801
*/
public class DepthsOfDesire extends CardImpl {
public DepthsOfDesire(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}");
// Return target creature to its owner's hand. Create a colorless Treasure token with "{t}, Sacrifice this artifact: Add one mana of any color to your mana pool."
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
this.getSpellAbility().addEffect(new ReturnToHandTargetEffect());
this.getSpellAbility().addEffect(new CreateTokenEffect(new TreasureToken()));
}
public DepthsOfDesire(final DepthsOfDesire card) {
super(card);
}
@Override
public DepthsOfDesire copy() {
return new DepthsOfDesire(this);
}
}

View file

@ -50,7 +50,7 @@ public class DeputizedProtester extends CardImpl {
this.toughness = new MageInt(1); this.toughness = new MageInt(1);
// Menace // Menace
this.addAbility(MenaceAbility.getInstance()); this.addAbility(new MenaceAbility());
// Melee // Melee
this.addAbility(new MeleeAbility()); this.addAbility(new MeleeAbility());
} }

View file

@ -48,7 +48,7 @@ public class DerangedWhelp extends CardImpl {
this.toughness = new MageInt(1); this.toughness = new MageInt(1);
// Menace // Menace
this.addAbility(MenaceAbility.getInstance()); this.addAbility(new MenaceAbility());
} }
public DerangedWhelp(final DerangedWhelp card) { public DerangedWhelp(final DerangedWhelp card) {

View file

@ -64,7 +64,8 @@ public class DireFleetCaptain extends CardImpl {
// Whenever Dire Fleet Captain attacks, it gets +1/+1 until end of turn for each other attacking Pirate. // Whenever Dire Fleet Captain attacks, it gets +1/+1 until end of turn for each other attacking Pirate.
PermanentsOnBattlefieldCount value = new PermanentsOnBattlefieldCount(filter); PermanentsOnBattlefieldCount value = new PermanentsOnBattlefieldCount(filter);
this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(value, value, Duration.EndOfTurn, true), false)); this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(value, value, Duration.EndOfTurn, true)
.setText("it gets +1/+1 until end of turn for each other attacking Pirate"), false));
} }
public DireFleetCaptain(final DireFleetCaptain card) { public DireFleetCaptain(final DireFleetCaptain card) {

View file

@ -0,0 +1,69 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.d;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.keyword.ExploreSourceEffect;
import mage.constants.SubType;
import mage.abilities.keyword.MenaceAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
/**
*
* @author TheElk801
*/
public class DireFleetInterloper extends CardImpl {
public DireFleetInterloper(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.PIRATE);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// Menace
this.addAbility(new MenaceAbility());
// When Dire Fleet Interloper enters the battlefield, it explores.
this.addAbility(new EntersBattlefieldTriggeredAbility(new ExploreSourceEffect()));
}
public DireFleetInterloper(final DireFleetInterloper card) {
super(card);
}
@Override
public DireFleetInterloper copy() {
return new DireFleetInterloper(this);
}
}

View file

@ -58,7 +58,7 @@ public class DireFleetRavager extends CardImpl {
this.toughness = new MageInt(4); this.toughness = new MageInt(4);
// Menace // Menace
this.addAbility(MenaceAbility.getInstance()); this.addAbility(new MenaceAbility());
// Deathtouch // Deathtouch
this.addAbility(DeathtouchAbility.getInstance()); this.addAbility(DeathtouchAbility.getInstance());

View file

@ -0,0 +1,68 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.d;
import java.util.UUID;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.keyword.HexproofAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.target.common.TargetControlledCreaturePermanent;
/**
*
* @author TheElk801
*/
public class DiveDown extends CardImpl {
public DiveDown(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}");
// Target creature you control gets +0/+3 and gains hexproof until end of turn.
Effect effect = new BoostTargetEffect(0, 3, Duration.EndOfTurn);
effect.setText("Target creature you control gets +0/+3");
this.getSpellAbility().addEffect(effect);
effect = new GainAbilityTargetEffect(HexproofAbility.getInstance(), Duration.EndOfTurn);
effect.setText("and gains hexproof until end of turn");
this.getSpellAbility().addEffect(effect);
this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent());
}
public DiveDown(final DiveDown card) {
super(card);
}
@Override
public DiveDown copy() {
return new DiveDown(this);
}
}

View file

@ -36,7 +36,6 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.SetTargetPointer; import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.FilterSpell; import mage.filter.FilterSpell;
import mage.filter.predicate.Predicates; import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.mageobject.CardTypePredicate;
@ -92,12 +91,9 @@ class DovescapeEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Spell spell = game.getStack().getSpell(this.getTargetPointer().getFirst(game, source)); Spell spell = game.getSpellOrLKIStack(this.getTargetPointer().getFirst(game, source));
int spellCMC = 0; int spellCMC = 0;
UUID spellControllerID = null; UUID spellControllerID = null;
if (spell == null) {
spell = (Spell) game.getLastKnownInformation(this.getTargetPointer().getFirst(game, source), Zone.STACK);
}
if (spell != null) { if (spell != null) {
spellCMC = spell.getConvertedManaCost(); spellCMC = spell.getConvertedManaCost();
spellControllerID = spell.getControllerId(); spellControllerID = spell.getControllerId();

View file

@ -58,7 +58,7 @@ public class Dreamstealer extends CardImpl {
this.toughness = new MageInt(2); this.toughness = new MageInt(2);
// Menace // Menace
this.addAbility(MenaceAbility.getInstance()); this.addAbility(new MenaceAbility());
// When Dreamstealer deals combat damage to a player, that player discards that many cards. // When Dreamstealer deals combat damage to a player, that player discards that many cards.
this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new DreamstealerDiscardEffect(), false, true)); this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new DreamstealerDiscardEffect(), false, true));

Some files were not shown because too many files have changed in this diff Show more