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 mage.cards.action.ActionCallback;
import mage.cards.decks.Deck;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.client.cards.BigCard;
import mage.client.chat.ChatPanelBasic;
@ -134,8 +132,6 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
private final BalloonTip balloonTip;
private java.util.List<CardInfo> missingCards;
/**
* @return the session
*/
@ -513,18 +509,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
}
private void checkForNewImages() {
long beforeCall = System.currentTimeMillis();
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);
}
// Removed TODO: Remove related pref code
}
public static void setActive(MagePane frame) {
@ -978,8 +963,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
}//GEN-LAST:event_btnImagesActionPerformed
public void downloadImages() {
java.util.List<CardInfo> cards = CardRepository.instance.findCards(new CardCriteria());
DownloadPictures.startDownload(null, cards);
DownloadPictures.startDownload();
}
public void exitApp() {
@ -1317,7 +1301,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
Plugins.instance.downloadSymbols();
break;
case CLIENT_DOWNLOAD_CARD_IMAGES:
DownloadPictures.startDownload(null, missingCards);
DownloadPictures.startDownload();
break;
case CLIENT_DISCONNECT:
if (SessionHandler.isConnected()) {

View file

@ -1,5 +1,6 @@
package org.mage.plugins.card.dl.sources;
import java.util.ArrayList;
import org.mage.plugins.card.images.CardDownloadData;
/**
@ -9,12 +10,38 @@ import org.mage.plugins.card.images.CardDownloadData;
public interface CardImageSource {
String generateURL(CardDownloadData card) throws Exception;
String generateTokenUrl(CardDownloadData card) throws Exception;
String getNextHttpImageUrl();
String getFileForHttpImage(String httpImageUrl);
String getSourceName();
float getAverageSize();
int getTotalImages();
boolean isTokenSource();
void doPause(String httpImageUrl);
default int getTokenImages() {
return 0;
}
default boolean isTokenSource() {
return false;
}
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;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import mage.client.dialog.PreferencesDialog;
import org.mage.plugins.card.images.CardDownloadData;
import org.mage.plugins.card.utils.CardImageUtils;
@ -13,6 +16,202 @@ import org.mage.plugins.card.utils.CardImageUtils;
public enum MagicCardsImageSource implements CardImageSource {
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>() {
{
put("10E", "tenth-edition");
@ -146,7 +345,6 @@ public enum MagicCardsImageSource implements CardImageSource {
return "magiccards.info";
}
@Override
public String getNextHttpImageUrl() {
return null;
@ -221,7 +419,15 @@ public enum MagicCardsImageSource implements CardImageSource {
return true;
}
@Override
public ArrayList<String> getSupportedSets() {
ArrayList<String> supportedSetsCopy = new ArrayList<>();
supportedSetsCopy.addAll(supportedSets);
return supportedSetsCopy;
}
@Override
public void doPause(String httpImageUrl) {
}
}

View file

@ -25,20 +25,221 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package org.mage.plugins.card.dl.sources;
import org.mage.plugins.card.images.CardDownloadData;
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
*/
public enum MagidexImageSource implements CardImageSource {
instance;
private final Set<String> supportedSets;
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
public String getSourceName() {
return "magidex.com";
@ -68,10 +269,23 @@ public enum MagidexImageSource implements CardImageSource {
}
// 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();
}
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
public String generateTokenUrl(CardDownloadData card) {
return null;
@ -95,4 +309,11 @@ public enum MagidexImageSource implements CardImageSource {
@Override
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;
import org.apache.log4j.Logger;
import org.mage.plugins.card.images.CardDownloadData;
import java.io.IOException;
import java.util.HashMap;
import org.apache.log4j.Logger;
import org.mage.plugins.card.images.CardDownloadData;
/**
*
@ -377,4 +376,10 @@ public enum MtgOnlTokensImageSource implements CardImageSource {
@Override
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.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.prefs.Preferences;
@ -57,10 +59,12 @@ import org.mage.plugins.card.images.CardDownloadData;
public enum MythicspoilerComSource implements CardImageSource {
instance;
private Map<String, String> setsAliases;
private Map<String, String> cardNameAliases;
private Map<String, Set<String>> cardNameAliasesStart;
private final Map<String, String> setsAliases;
private final Map<String, String> cardNameAliases;
private final Map<String, Set<String>> cardNameAliasesStart;
private final Map<String, Map<String, String>> sets;
private final Set<String> supportedSets;
private final Map<String, Map<String, String>> manualLinks;
@Override
public String getSourceName() {
@ -68,6 +72,188 @@ public enum MythicspoilerComSource implements CardImageSource {
}
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<>();
setsAliases = new HashMap<>();
setsAliases.put("exp", "bfz");
@ -88,6 +274,25 @@ public enum MythicspoilerComSource implements CardImageSource {
cardNameAliases.put("AKH-reducerumble", "reducerubble");
cardNameAliases.put("AKH-forsakethewordly", "forsaketheworldly");
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<>();
HashSet<String> names = new HashSet<>();
@ -184,7 +389,12 @@ public enum MythicspoilerComSource implements CardImageSource {
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;
}
@ -243,4 +453,12 @@ public enum MythicspoilerComSource implements CardImageSource {
@Override
public void doPause(String httpImageUrl) {
}
@Override
public ArrayList<String> getSupportedSets() {
ArrayList<String> supportedSetsCopy = new ArrayList<>();
supportedSetsCopy.addAll(supportedSets);
return supportedSetsCopy;
}
}

View file

@ -1,9 +1,11 @@
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.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.mage.plugins.card.images.CardDownloadData;
/**
* @author Quercitron
@ -13,9 +15,209 @@ public enum ScryfallImageSource implements CardImageSource {
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
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
@ -40,7 +242,7 @@ public enum ScryfallImageSource implements CardImageSource {
@Override
public float getAverageSize() {
return 240;
return 90;
}
@Override
@ -75,4 +277,11 @@ public enum ScryfallImageSource implements CardImageSource {
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;
import org.apache.log4j.Logger;
import org.mage.plugins.card.images.CardDownloadData;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
@ -37,8 +34,14 @@ import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
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;
/**
*
@ -49,8 +52,9 @@ public enum TokensMtgImageSource implements CardImageSource {
instance;
private static final Logger logger = Logger.getLogger(TokensMtgImageSource.class);
private List<TokenData> tokensData;
// [[EXP/Name, TokenData>
private HashMap<String, ArrayList<TokenData>> tokensData;
private static final Set<String> supportedSets = new LinkedHashSet<String>();
private final Object tokensDataSync = new Object();
@ -100,7 +104,12 @@ public enum TokensMtgImageSource implements CardImageSource {
"Sorin",
"Tamiyo",
"Teferi",
"Venser",};
"Venser",
// Custom Emblems
"Yoda",
"Obi-Wan Kenobi",
"Aurra Sing"
};
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
// We don't know these numbers, but we can take them from a file
// with tokens information that can be downloaded from the site.
List<TokenData> newTokensData = getTokensData();
if (newTokensData.isEmpty()) {
if (tokensData.isEmpty()) {
logger.info("Source " + getSourceName() + " provides no token data.");
return null;
}
List<TokenData> matchedTokens = new ArrayList<>();
for (TokenData token : newTokensData) {
if (name.equalsIgnoreCase(token.getName()) && set.equalsIgnoreCase(token.getExpansionSetCode())) {
matchedTokens.add(token);
String key = set + "/" + name;
List<TokenData> list = tokensData.get(key);
if (list == null) {
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;
if (type == 0) {
if (matchedTokens.size() > 1) {
if (list.size() > 1) {
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 {
if (type > matchedTokens.size()) {
if (type > list.size()) {
logger.warn("Not enough images for token with type " + type + ", name " + name + ", set " + set + '.');
return null;
}
tokenData = matchedTokens.get(card.getType() - 1);
tokenData = list.get(card.getType() - 1);
}
String url = "http://tokens.mtg.onl/tokens/" + tokenData.getExpansionSetCode().trim() + '_'
@ -172,15 +176,26 @@ public enum TokensMtgImageSource implements CardImageSource {
return url;
}
private List<TokenData> getTokensData() throws IOException {
private HashMap<String, ArrayList<TokenData>> getTokensData() throws IOException {
synchronized (tokensDataSync) {
if (tokensData == null) {
tokensData = new ArrayList<>();
DownloadPictures.getInstance().updateAndViewMessage("Creating token data...");
tokensData = new HashMap<>();
// get tokens data from resource file
try (InputStream inputStream = this.getClass().getResourceAsStream("/tokens-mtg-onl-list.csv")) {
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) {
logger.warn("Failed to get tokens description from resource file tokens-mtg-onl-list.csv", exception);
}
@ -190,25 +205,31 @@ public enum TokensMtgImageSource implements CardImageSource {
URL url = new URL("http://tokens.mtg.onl/data/SetsWithTokens.csv");
try (InputStream inputStream = url.openStream()) {
List<TokenData> siteTokensData = parseTokensData(inputStream);
List<TokenData> newTokensData = new ArrayList<>();
for (TokenData siteData : siteTokensData) {
boolean isNew = true;
for (TokenData fileData : tokensData) {
if (siteData.getName().equalsIgnoreCase(fileData.getName())
&& siteData.getNumber().equalsIgnoreCase(fileData.getNumber())
&& siteData.getExpansionSetCode().equalsIgnoreCase(fileData.getExpansionSetCode())) {
isNew = false;
String key = siteData.getExpansionSetCode() + "/" + siteData.getName();
supportedSets.add(siteData.getExpansionSetCode());
ArrayList<TokenData> list = tokensData.get(key);
if (list == null) {
list = new ArrayList<>();
tokensData.put(key, list);
list.add(siteData);
} else {
boolean newToken = true;
for (TokenData tokenData : list) {
if (siteData.getNumber().equals(tokenData.number)) {
newToken = false;
break;
}
}
if (isNew) {
newTokensData.add(siteData);
if (newToken) {
list.add(siteData);
}
}
tokensData.addAll(newTokensData);
} catch (Exception exception) {
logger.warn("Failed to get tokens description from tokens.mtg.onl", exception);
}
DownloadPictures.getInstance().updateAndViewMessage("");
} catch (Exception ex) {
logger.warn("Failed to get tokens description from tokens.mtg.onl", ex);
DownloadPictures.getInstance().updateAndViewMessage(ex.getMessage());
}
}
}
@ -285,7 +306,17 @@ public enum TokensMtgImageSource implements CardImageSource {
@Override
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
@ -296,4 +327,28 @@ public enum TokensMtgImageSource implements CardImageSource {
@Override
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.Proxy;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@ -46,6 +49,7 @@ import mage.client.MageFrame;
import mage.client.dialog.PreferencesDialog;
import mage.remote.Connection;
import mage.remote.Connection.ProxyType;
import org.apache.log4j.Logger;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
@ -58,10 +62,13 @@ import org.mage.plugins.card.images.CardDownloadData;
public enum WizardCardsImageSource implements CardImageSource {
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
public String getSourceName() {
@ -69,6 +76,198 @@ public enum WizardCardsImageSource implements CardImageSource {
}
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<>();
setsAliases = new HashMap<>();
setsAliases.put("2ED", "Unlimited Edition");
@ -102,6 +301,7 @@ public enum WizardCardsImageSource implements CardImageSource {
setsAliases.put("C14", "Commander 2014");
setsAliases.put("C15", "Commander 2015");
setsAliases.put("C16", "Commander 2016");
setsAliases.put("C17", "Commander 2017");
setsAliases.put("CMA", "Commander Anthology");
setsAliases.put("CHK", "Champions of Kamigawa");
setsAliases.put("CHR", "Chronicles");
@ -140,6 +340,7 @@ public enum WizardCardsImageSource implements CardImageSource {
setsAliases.put("DRK", "The Dark");
setsAliases.put("DST", "Darksteel");
setsAliases.put("DTK", "Dragons of Tarkir");
setsAliases.put("E01", "Archenemy: Nicol Bolas");
setsAliases.put("EMN", "Eldritch Moon");
setsAliases.put("EMA", "Eternal Masters");
setsAliases.put("EVE", "Eventide");
@ -272,18 +473,20 @@ public enum WizardCardsImageSource implements CardImageSource {
}
private Map<String, String> getSetLinks(String cardSet) {
ConcurrentHashMap<String, String> setLinks = new ConcurrentHashMap<>();
LinkedHashMap<String, String> setLinks = new LinkedHashMap<>();
ExecutorService executor = Executors.newFixedThreadPool(10);
try {
String setNames = setsAliases.get(cardSet);
String preferedLanguage = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PREF_LANGUAGE, "en");
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 firstMultiverseIdLastPage = 0;
Pages:
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);
Elements cardsImages = doc.select("img[src^=../../Handlers/]");
if (cardsImages.isEmpty()) {
@ -307,7 +510,7 @@ public enum WizardCardsImageSource implements CardImageSource {
}
}
} 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();
@ -327,14 +530,15 @@ public enum WizardCardsImageSource implements CardImageSource {
Connection.ProxyType proxyType = Connection.ProxyType.valueByText(prefs.get("proxyType", "None"));
Document doc;
if (proxyType == ProxyType.NONE) {
doc = Jsoup.connect(urlString).get();
doc = Jsoup.connect(urlString).timeout(60 * 1000).get();
} else {
String proxyServer = prefs.get("proxyAddress", "");
int proxyPort = Integer.parseInt(prefs.get("proxyPort", "0"));
URL url = new URL(urlString);
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyServer, proxyPort));
HttpURLConnection uc = (HttpURLConnection) url.openConnection(proxy);
uc.setConnectTimeout(10000);
uc.setReadTimeout(60000);
uc.connect();
String line;
@ -356,7 +560,7 @@ public enum WizardCardsImageSource implements CardImageSource {
if (!variations.isEmpty()) {
int landNumber = 1;
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));
landNumber++;
}
@ -435,6 +639,9 @@ public enum WizardCardsImageSource implements CardImageSource {
String setNames = setsAliases.get(cardSet);
if (setNames != null) {
Map<String, String> setLinks = sets.computeIfAbsent(cardSet, k -> getSetLinks(cardSet));
if (setLinks == null || setLinks.isEmpty()) {
return null;
}
String link = setLinks.get(card.getDownloadName().toLowerCase());
if (link == null) {
int length = collectorId.length();
@ -444,11 +651,11 @@ public enum WizardCardsImageSource implements CardImageSource {
}
int number = Integer.parseInt(collectorId.substring(0, length));
if (setLinks.size() >= number) {
link = setLinks.get(Integer.toString(number - 1));
} else {
link = setLinks.get(Integer.toString(number - 21));
List<String> l = new ArrayList<>(setLinks.values());
if (l.size() >= number) {
link = l.get(number - 1);
} else {;
link = l.get(number - 21);
if (link != null) {
link = link.replace(Integer.toString(number - 20), (Integer.toString(number - 20) + 'a'));
}
@ -460,6 +667,7 @@ public enum WizardCardsImageSource implements CardImageSource {
return link;
}
return null;
}
@Override
@ -474,16 +682,24 @@ public enum WizardCardsImageSource implements CardImageSource {
private final class GetImageLinkTask implements Runnable {
private final int multiverseId;
private final String cardName;
private final String preferedLanguage;
private final ConcurrentHashMap setLinks;
private int multiverseId;
private String cardName;
private String preferedLanguage;
private LinkedHashMap setLinks;
public GetImageLinkTask(int multiverseId, String cardName, String preferedLanguage, ConcurrentHashMap setLinks) {
public GetImageLinkTask(int multiverseId, String cardName, String preferedLanguage, LinkedHashMap setLinks) {
try {
this.multiverseId = multiverseId;
this.cardName = cardName;
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
@ -496,7 +712,7 @@ public enum WizardCardsImageSource implements CardImageSource {
setLinks.put(cardName.toLowerCase(), generateLink(preferedMultiverseId));
}
} 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
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) {
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) {
@ -72,7 +72,7 @@ public class CardDownloadData {
this.usesVariousArt = card.usesVariousArt;
this.tokenSetCode = card.tokenSetCode;
this.tokenDescriptor = card.tokenDescriptor;
this.tokenClassName = tokenClassName;
this.tokenClassName = card.tokenClassName;
this.fileName = card.fileName;
}

View file

@ -1,6 +1,7 @@
package org.mage.plugins.card.images;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.*;
@ -14,18 +15,25 @@ import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageOutputStream;
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.CardRepository;
import mage.client.MageFrame;
import mage.client.constants.Constants;
import mage.client.dialog.PreferencesDialog;
import mage.client.util.sets.ConstructedFormats;
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.TFileOutputStream;
import net.java.truevfs.access.TVFS;
@ -37,51 +45,97 @@ import org.mage.plugins.card.utils.CardImageUtils;
public class DownloadPictures extends DefaultBoundedRangeModel implements Runnable {
private static DownloadPictures instance;
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 JOptionPane dlg;
private boolean cancel;
private final JButton closeButton;
private final JButton startDownloadButton;
private int cardIndex;
private List<CardDownloadData> cards;
private List<CardDownloadData> type2cards;
private final JComboBox jComboBox1;
private final JLabel jLabel1;
private static boolean offlineMode = false;
private JCheckBox checkBox;
private List<CardDownloadData> allCardsMissingImage;
private List<CardDownloadData> cardsToDownload;
private int missingCards = 0;
private int missingTokens = 0;
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 static CardImageSource cardImageSource;
private Proxy p = Proxy.NO_PROXY;
// private ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args) {
startDownload(null, null);
enum DownloadSources {
WIZARDS("wizards.com", WizardCardsImageSource.instance),
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 static void startDownload(JFrame frame, List<CardInfo> allCards) {
List<CardDownloadData> cards = getNeededCards(allCards);
public CardImageSource getSource() {
return source;
}
@Override
public String toString() {
return text;
}
}
public static DownloadPictures getInstance() {
return instance;
}
public static void main(String[] args) {
startDownload();
}
public static void startDownload() {
/*
* if (cards == null || cards.isEmpty()) {
* JOptionPane.showMessageDialog(null,
* "All card pictures have been downloaded."); return; }
*/
DownloadPictures download = new DownloadPictures(cards);
JDialog dlg = download.getDlg(frame);
dlg.setVisible(true);
dlg.dispose();
download.cancel = true;
instance = new DownloadPictures(MageFrame.getInstance());
Thread t1 = new Thread(new LoadMissingCardData(instance));
t1.start();
instance.getDlg().setVisible(true);
instance.getDlg().dispose();
instance.cancel = true;
}
public JDialog getDlg(JFrame frame) {
String title = "Downloading";
final JDialog dialog = this.dlg.createDialog(frame, title);
closeButton.addActionListener(e -> dialog.setVisible(false));
public JDialog getDlg() {
return dialog;
}
@ -89,75 +143,77 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
this.cancel = cancel;
}
public DownloadPictures(List<CardDownloadData> cards) {
this.cards = cards;
static int WIDTH = 400;
bar = new JProgressBar(this);
public DownloadPictures(JFrame frame) {
cardsToDownload = new ArrayList<>();
JPanel p0 = new JPanel();
p0.setLayout(new BoxLayout(p0, BoxLayout.Y_AXIS));
p0.add(Box.createVerticalStrut(5));
jLabel1 = new JLabel();
jLabel1.setText("Please select server:");
jLabel1.setAlignmentX(Component.LEFT_ALIGNMENT);
p0.add(jLabel1);
jLabelMessage = new JLabel();
jLabelMessage.setAlignmentX(Component.CENTER_ALIGNMENT);
jLabelMessage.setText("Initializing image download...");
p0.add(jLabelMessage);
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);
jComboBox1.setAlignmentX(Component.LEFT_ALIGNMENT);
jComboBox1.addActionListener(e -> {
JComboBox cb = (JComboBox) e.getSource();
switch (cb.getSelectedIndex() + 1) {
case 0:
cardImageSource = MagicCardsImageSource.instance;
break;
case 1:
cardImageSource = WizardCardsImageSource.instance;
break;
case 2:
cardImageSource = MythicspoilerComSource.instance;
break;
case 3:
cardImageSource = TokensMtgImageSource.instance;
break;
case 4:
cardImageSource = MtgOnlTokensImageSource.instance;
break;
case 5:
cardImageSource = AltMtgOnlTokensImageSource.instance;
break;
case 6:
cardImageSource = GrabbagImageSource.instance;
break;
case 7:
cardImageSource = MagidexImageSource.instance;
break;
case 8:
cardImageSource = ScryfallImageSource.instance;
break;
jLabelAllMissing.setAlignmentX(Component.LEFT_ALIGNMENT);
// jLabelAllMissing.setText("Computing number of missing images...");
p0.add(jLabelAllMissing);
p0.add(Box.createVerticalStrut(5));
jLabelServer = new JLabel();
jLabelServer.setText("Please select image source:");
jLabelServer.setAlignmentX(Component.LEFT_ALIGNMENT);
jLabelServer.setVisible(false);
p0.add(jLabelServer);
p0.add(Box.createVerticalStrut(5));
jComboBoxServer = new JComboBox();
jComboBoxServer.setModel(new DefaultComboBoxModel(DownloadSources.values()));
jComboBoxServer.setAlignmentX(Component.LEFT_ALIGNMENT);
jComboBoxServer.setAlignmentY(Component.LEFT_ALIGNMENT);
jComboBoxServer.addItemListener((ItemEvent event) -> {
if (event.getStateChange() == ItemEvent.SELECTED) {
comboBoxServerItemSelected(event);
}
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));
// Start
@ -165,78 +221,168 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
startDownloadButton.addActionListener(e -> {
new Thread(DownloadPictures.this).start();
startDownloadButton.setEnabled(false);
checkBox.setEnabled(false);
});
p0.add(Box.createVerticalStrut(5));
// Progress
bar = new JProgressBar(this);
p0.add(bar);
bar.setStringPainted(true);
int count = cards.size();
float mb = (count * cardImageSource.getAverageSize()) / 1024;
bar.setString(String.format(cardIndex == cards.size() ? "%d of %d cards finished! Please close!"
: "%d of %d cards finished! Please wait! [%.1f Mb]", 0, cards.size(), mb));
Dimension d = bar.getPreferredSize();
d.width = 300;
d = bar.getPreferredSize();
d.width = WIDTH;
bar.setPreferredSize(d);
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());
bar.setVisible(false);
// JOptionPane
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]);
dialog = this.dlg.createDialog(frame, "Downloading images");
}
public static boolean checkForMissingCardImages(List<CardInfo> allCards) {
AtomicBoolean missedCardTFiles = new AtomicBoolean();
allCards.parallelStream().forEach(card -> {
if (!missedCardTFiles.get()) {
if (!card.getCardNumber().isEmpty() && !"0".equals(card.getCardNumber()) && !card.getSetCode().isEmpty()) {
CardDownloadData url = new CardDownloadData(card.getName(), card.getSetCode(), card.getCardNumber(), card.usesVariousArt(),
0, "", "", false, card.isDoubleFaced(), card.isNightCard());
TFile file = new TFile(CardImageUtils.generateImagePath(url));
if (!file.exists()) {
missedCardTFiles.set(true);
}
}
}
});
return missedCardTFiles.get();
public void setAllMissingCards() {
updateAndViewMessage("Get all available cards from the repository...");
List<CardInfo> cards = CardRepository.instance.findCards(new CardCriteria());
updateAndViewMessage("Check which images are missing ...");
this.allCardsMissingImage = getNeededCards(cards);
updateAndViewMessage("Check which images the current source is providing ...");
jComboBoxSet.setModel(new DefaultComboBoxModel<>(getSetsForCurrentImageSource()));
updateCardsToDownload(jComboBoxSet.getSelectedItem().toString());
jComboBoxServer.setVisible(true);
jLabelServer.setVisible(true);
jComboBoxSet.setVisible(true);
jLabelSet.setVisible(true);
bar.setVisible(true);
startDownloadButton.setVisible(true);
closeButton.setVisible(true);
updateAndViewMessage("");
}
private void updateCardsToDownload() {
List<CardDownloadData> cardsToDownload = cards;
if (type2cardsOnly()) {
selectType2andTokenCardsIfNotYetDone();
cardsToDownload = type2cards;
}
updateProgressText(cardsToDownload.size());
}
private boolean type2cardsOnly() {
return checkBox.isSelected();
}
private void selectType2andTokenCardsIfNotYetDone() {
if (type2cards == null) {
type2cards = new ArrayList<>();
for (CardDownloadData data : cards) {
if (data.isType2() || data.isToken()) {
type2cards.add(data);
}
}
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());
}
}
private void updateProgressText(int cardCount) {
float mb = (cardCount * cardImageSource.getAverageSize()) / 1024;
bar.setString(String.format(cardIndex == cardCount ? "%d of %d cards finished! Please close!"
: "%d of %d cards finished! Please wait! [%.1f Mb]", 0, cardCount, mb));
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);
}
}
}
}
updateProgressText(numberCardImagesAvailable, numberTokenImagesAvailable);
}
private void comboBoxSetItemSelected(ItemEvent event) {
// Update the cards to download related to the selected set
updateCardsToDownload(event.getItem().toString());
}
private void updateProgressText(int cardCount, int tokenCount) {
missingTokens = 0;
for (CardDownloadData card : allCardsMissingImage) {
if (card.isToken()) {
missingTokens++;
}
}
missingCards = allCardsMissingImage.size() - missingTokens;
jLabelAllMissing.setText("Missing: " + missingCards + " card images / " + missingTokens + " token images");
int imageSum = cardCount + tokenCount;
float mb = (imageSum * cardImageSource.getAverageSize()) / 1024;
bar.setString(String.format(cardIndex == imageSum ? "%d of %d (%d cards/%d tokens) image downloads finished! Please close!"
: "%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) {
@ -262,11 +408,8 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
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<>());
try {
offlineMode = true;
allCards.parallelStream().forEach(card -> {
if (!card.getCardNumber().isEmpty() && !"0".equals(card.getCardNumber()) && !card.getSetCode().isEmpty()
&& !ignoreUrls.contains(card.getSetCode())) {
@ -286,6 +429,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
if (card.getSecondSideName() == null || card.getSecondSideName().trim().isEmpty()) {
throw new IllegalStateException("Second side card can't have empty name.");
}
url = new CardDownloadData(card.getSecondSideName(), card.getSetCode(), card.getCardNumber(), card.usesVariousArt(), 0, "", "", false, card.isDoubleFaced(), true);
url.setType2(isType2);
allCardsUrls.add(url);
@ -301,21 +445,18 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
allCardsUrls.add(cardDownloadData);
}
} 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());
} 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());
} else {
logger.info("Card was not selected: " + card.getName());
}
});
numberWithoutTokens = allCards.size();
allCardsUrls.addAll(getTokenCardUrls());
} catch (Exception e) {
logger.error(e);
}
int numberAllTokenImages = allCardsUrls.size() - numberWithoutTokens;
/**
* 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);
}
public static ArrayList<CardDownloadData> getTokenCardUrls() throws RuntimeException {
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) {
logger.error("resources input stream is null");
@ -446,9 +578,8 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
if (p != null) {
HashSet<String> ignoreUrls = SettingsManager.getIntance().getIgnoreUrls();
List<CardDownloadData> cardsToDownload = this.checkBox.isSelected() ? type2cards : cards;
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"));
ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads);
@ -457,7 +588,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
CardDownloadData card = cardsToDownload.get(i);
logger.debug("Downloading card: " + card.getName() + " (" + card.getSet() + ')');
logger.debug("Downloading image: " + card.getName() + " (" + card.getSet() + ')');
String url;
if (ignoreUrls.contains(card.getSet()) || card.isToken()) {
@ -482,7 +613,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
} catch (Exception ex) {
}
} 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) {
update(cardIndex + 1, cardsToDownload.size());
}
@ -513,6 +644,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
System.gc();
}
closeButton.setText("Close");
updateCardsToDownload(jComboBoxSet.getSelectedItem().toString());
}
static String convertStreamToString(java.io.InputStream is) {
@ -604,6 +736,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
if (temporaryFile != null && temporaryFile.length() > 100) {
useTempFile = true;
} else {
cardImageSource.doPause(url.getPath());
httpConn = url.openConnection(p);
httpConn.connect();
@ -708,29 +841,52 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
if (cardIndex < count) {
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));
} else {
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));
if (!file.exists()) {
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) {
bar.setString("0 cards remaining! Please close!");
bar.setString("0 images remaining! Please close!");
} 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);
}
}
}
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|Soldier Ally|||JoinTheRanksSoldierToken|
|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|Beast|||BeastToken2|
|Generate|TOK:ZEN|Bird|||BirdToken|

View file

@ -74,6 +74,6 @@ dd3evg=ddaevg
dd3gvl=ddagvl
dd3jvc=ddajvc
# 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)
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, 1c, -, -, -, Creature - Eldrazi Spawn, Veronique Meignaud, Sacrifice this creature: Add {1} to your mana pool.
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, -
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
Zombie, 5, B, 2|2, -, Creature - Zombie, Lucas Graciano, -
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, -
Saproling, 9, G, 1|1, -, Creature - Saproling, Brad Rigney, -
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, 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.
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, -
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.
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.'
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_MINOR = 4;
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 = "";
private final int major;

View file

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

View file

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

View file

@ -25,19 +25,17 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
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.FirstStrikeAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.game.Game;
import mage.game.permanent.Permanent;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
*
* @author BetaSteward_at_googlemail.com
@ -50,8 +48,9 @@ public class CombatEvaluator {
public int evaluate(Permanent creature, Game game) {
if (!values.containsKey(creature.getId())) {
int value = 0;
if (creature.canAttack(game))
if (creature.canAttack(null, game)) {
value += 2;
}
value += creature.getPower().getValue();
value += creature.getToughness().getValue();
value += creature.getAbilities().getEvasionAbilities().size();

View file

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

View file

@ -46,6 +46,7 @@ import mage.choices.ChoiceImpl;
import mage.constants.*;
import static mage.constants.PlayerAction.REQUEST_AUTO_ANSWER_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.FilterBlockingCreature;
import mage.filter.common.FilterCreatureForCombat;
@ -1055,6 +1056,7 @@ public class HumanPlayer extends PlayerImpl {
updateGameStatePriority("selectAttackers", game);
FilterCreatureForCombat filter = filterCreatureForCombat.copy();
filter.add(new ControllerIdPredicate(attackingPlayerId));
while (!abort) {
if (passedAllTurns
|| passedUntilEndStepBeforeMyTurn
@ -1063,13 +1065,15 @@ public class HumanPlayer extends PlayerImpl {
|| passedTurnSkipStack
|| passedUntilEndOfTurn
|| passedUntilNextMain))) {
if (checkIfAttackersValid(game)) {
return;
}
}
Map<String, Serializable> options = new HashMap<>();
List<UUID> possibleAttackers = new ArrayList<>();
for (Permanent possibleAttacker : game.getBattlefield().getActivePermanents(filter, attackingPlayerId, game)) {
if (possibleAttacker.canAttack(game)) {
if (possibleAttacker.canAttack(null, game)) {
possibleAttackers.add(possibleAttacker.getId());
}
}
@ -1108,8 +1112,28 @@ public class HumanPlayer extends PlayerImpl {
// attack selected default defender
declareAttacker(attacker.getId(), attackedDefender, game, false);
}
} else if (response.getBoolean() != null) {
// check if enough attackers are declared
} else if (response.getInteger() != null) { // F-Key
if (checkIfAttackersValid(game)) {
return;
}
} else if (response.getBoolean() != null) { // ok button
if (checkIfAttackersValid(game)) {
return;
}
} else if (response.getUUID() != null) {
Permanent attacker = game.getPermanent(response.getUUID());
if (attacker != null) {
if (filterCreatureForCombat.match(attacker, null, playerId, game)) {
selectDefender(game.getCombat().getDefenders(), attacker.getId(), game);
} else if (filterAttack.match(attacker, null, playerId, game) && game.getStack().isEmpty()) {
removeAttackerIfPossible(game, attacker);
}
}
}
}
}
private boolean checkIfAttackersValid(Game game) {
if (!game.getCombat().getCreaturesForcedToAttack().isEmpty()) {
if (!game.getCombat().getAttackers().containsAll(game.getCombat().getCreaturesForcedToAttack().keySet())) {
int forcedAttackers = 0;
@ -1141,28 +1165,63 @@ public class HumanPlayer extends PlayerImpl {
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;
}
return;
} else if (response.getInteger() != null) {
return;
} else if (response.getUUID() != null) {
Permanent attacker = game.getPermanent(response.getUUID());
if (attacker != null) {
if (filterCreatureForCombat.match(attacker, null, playerId, game)) {
selectDefender(game.getCombat().getDefenders(), attacker.getId(), game);
} else if (filterAttack.match(attacker, null, playerId, game) && game.getStack().isEmpty()) {
removeAttackerIfPossible(game, attacker);
}
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) {
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();
if (effect.mustAttack(game)) {
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.permanent.ControllerPredicate;
import mage.game.Game;
import mage.game.events.DamagedPlayerEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
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.
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();
this.addAbility(ability, new DamagedByPiratesWatcher());
}
@ -137,7 +138,8 @@ class DamagedByPiratesWatcher extends Watcher {
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER && event.getFlag()) {
if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER) {
if (((DamagedPlayerEvent) event).isCombatDamage()) {
Permanent creature = game.getPermanentOrLKIBattlefield(event.getSourceId());
if (creature != null && creature.getSubtype(game).contains(SubType.PIRATE)) {
if (damageSourceIds.keySet().contains(event.getTargetId())) {
@ -150,6 +152,7 @@ class DamagedByPiratesWatcher extends Watcher {
}
}
}
}
public boolean damagedByEnoughPirates(UUID sourceId, Game game) {
return damageSourceIds.keySet().contains(sourceId) && damageSourceIds.get(sourceId).size() > 2;

View file

@ -51,7 +51,7 @@ public class AdroitHateflayer extends CardImpl {
this.toughness = new MageInt(3);
// Menace
this.addAbility(MenaceAbility.getInstance());
this.addAbility(new MenaceAbility());
// Whenever Adroit Hateflayer attacks, each opponent loses 2 life.
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.toughness = new MageInt(3);
//TODO: Make ability properly copiable
// {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.addCost(new TapSourceCost());

View file

@ -50,7 +50,7 @@ public class AlleyStrangler extends CardImpl {
this.toughness = new MageInt(3);
// Menace
this.addAbility(MenaceAbility.getInstance());
this.addAbility(new MenaceAbility());
}
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
* 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.ActivatedAbilityImpl;
import mage.abilities.costs.Cost;
import mage.abilities.costs.CostImpl;
import mage.abilities.costs.mana.ColoredManaCost;
import mage.abilities.condition.common.SourceAttackingCondition;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.decorator.ConditionalActivatedAbility;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.ColoredManaSymbol;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.common.TargetCreaturePermanent;
/**
@ -55,6 +51,8 @@ import mage.target.common.TargetCreaturePermanent;
*/
public class AncientHellkite extends CardImpl {
private final UUID originalId;
public AncientHellkite(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}{R}");
this.subtype.add(SubType.DRAGON);
@ -62,12 +60,29 @@ public class AncientHellkite extends CardImpl {
this.power = new MageInt(6);
this.toughness = new MageInt(6);
//TODO: Make ability properly copiable
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) {
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
@ -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

@ -69,6 +69,7 @@ public class AngrathsMarauders extends CardImpl {
return new AngrathsMarauders(this);
}
}
class AngrathsMaraudersEffect extends ReplacementEffectImpl {
public AngrathsMaraudersEffect() {
@ -100,7 +101,7 @@ class AngrathsMaraudersEffect extends ReplacementEffectImpl {
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return event.getSourceId().equals(source.getControllerId());
return game.getControllerId(event.getSourceId()).equals(source.getControllerId());
}
@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);
// Menace
this.addAbility(MenaceAbility.getInstance());
this.addAbility(new MenaceAbility());
// Crew 4
this.addAbility(new CrewAbility(4));
}

View file

@ -52,7 +52,7 @@ public class AshesOfTheAbhorrent extends CardImpl {
public AshesOfTheAbhorrent(UUID ownerId, CardSetInfo setInfo) {
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()));
// Whenever a creature dies, you gain 1 life.
@ -73,7 +73,7 @@ class AshesOfTheAbhorrentEffect extends ContinuousRuleModifyingEffectImpl {
public AshesOfTheAbhorrentEffect() {
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) {

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.)
Ability ability = new ActivateIfConditionActivatedAbility(
Zone.BATTLEFIELD,
new GainAbilityAllEffect(MenaceAbility.getInstance(), Duration.EndOfTurn, filter),
new GainAbilityAllEffect(new MenaceAbility(), Duration.EndOfTurn, filter),
new ManaCostsImpl("{3}{R}{R}"),
FormidableCondition.instance);
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.FlyingAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.abilities.text.TextPartSubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.TextPartSubtypePredicate;
import mage.filter.predicate.permanent.TappedPredicate;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
@ -68,11 +65,7 @@ public class AtarkaWorldRender extends CardImpl {
this.addAbility(TrampleAbility.getInstance());
// Whenever a Dragon you control attacks, it gains double strike until end of turn.
TextPartSubType textPart1 = (TextPartSubType) addTextPart(new TextPartSubType(SubType.DRAGON));
FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Dragon you control");
filter.add(new TextPartSubtypePredicate(textPart1));
filter.add(Predicates.not(new TappedPredicate()));
this.addAbility(new AtarkaWorldRenderEffect(filter));
this.addAbility(new AtarkaWorldRenderEffect());
}
@ -88,16 +81,18 @@ public class AtarkaWorldRender extends CardImpl {
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));
this.filter = filter;
}
public AtarkaWorldRenderEffect(final AtarkaWorldRenderEffect ability) {
super(ability);
this.filter = ability.filter.copy();
}
@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.
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.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD,

View file

@ -37,7 +37,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerPredicate;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game;
/**
@ -54,7 +54,7 @@ public class BelligerentBrontodon extends CardImpl {
this.toughness = new MageInt(6);
// 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) {
@ -67,31 +67,27 @@ public class BelligerentBrontodon extends CardImpl {
}
}
class MilitantDinosaurCombatDamageRuleEffect extends ContinuousEffectImpl {
class BelligerentBrontodonCombatDamageRuleEffect extends ContinuousEffectImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
static {
filter.add(new ControllerPredicate(TargetController.YOU));
}
public MilitantDinosaurCombatDamageRuleEffect() {
public BelligerentBrontodonCombatDamageRuleEffect() {
super(Duration.WhileOnBattlefield, Outcome.Detriment);
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);
}
@Override
public MilitantDinosaurCombatDamageRuleEffect copy() {
return new MilitantDinosaurCombatDamageRuleEffect(this);
public BelligerentBrontodonCombatDamageRuleEffect copy() {
return new BelligerentBrontodonCombatDamageRuleEffect(this);
}
@Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
// Change the rule
FilterCreaturePermanent filter = new FilterCreaturePermanent();
filter.add(new ControllerIdPredicate(source.getControllerId()));
game.getCombat().setUseToughnessForDamage(true);
game.getCombat().addUseToughnessForDamageFilter(filter);
return true;

View file

@ -62,7 +62,7 @@ public class BelligerentSliver extends CardImpl {
this.toughness = new MageInt(2);
// 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.)");
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.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.ComparisonType;
import mage.constants.SubType;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.CardTypePredicate;
@ -69,7 +69,8 @@ public class BishopOfRebirth extends CardImpl {
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.
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));
this.addAbility(ability);
}

View file

@ -33,15 +33,15 @@ import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.common.LoseLifeTargetEffect;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.SubtypePredicate;
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.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.addTarget(new TargetPlayer());
ability.addTarget(new TargetOpponent());
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);
// 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) {

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;
}
Spell spell = (Spell) game.getStack().getStackObject(targetPointer.getFirst(game, source));
Spell spell = game.getSpellOrLKIStack(this.getTargetPointer().getFirst(game, source));
if (spell == null) {
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);
// Menace
this.addAbility(MenaceAbility.getInstance());
this.addAbility(new MenaceAbility());
// Deathtouch
this.addAbility(DeathtouchAbility.getInstance());

View file

@ -48,7 +48,7 @@ public class BoggartBrute extends CardImpl {
this.toughness = new MageInt(2);
// Menace
this.addAbility(MenaceAbility.getInstance());
this.addAbility(new MenaceAbility());
}
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);
//Menace
this.addAbility(MenaceAbility.getInstance());
this.addAbility(new MenaceAbility());
//Indestructible
this.addAbility(IndestructibleAbility.getInstance());

View file

@ -89,7 +89,7 @@ class BounteousKirinEffect extends OneShotEffect {
@Override
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) {
Player controller = game.getPlayer(source.getControllerId());
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 mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.LimitedTimesPerTurnActivatedAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.mana.GenericManaCost;
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.continuous.BoostSourceEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
@ -43,6 +43,7 @@ import mage.abilities.keyword.FirstStrikeAbility;
import mage.cards.*;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
@ -65,9 +66,7 @@ public class BrutalDeceiver extends CardImpl {
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.
Ability ability = new BrutalDeceiverAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{2}"));
ability.addEffect(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn));
this.addAbility(ability);
this.addAbility(new LimitedTimesPerTurnActivatedAbility(Zone.BATTLEFIELD, new BrutalDeceiverEffect(), new ManaCostsImpl("{2}")));
}
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) {
super(zone, effect, cost);
public BrutalDeceiverEffect() {
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) {
super(ability);
public BrutalDeceiverEffect(final BrutalDeceiverEffect effect) {
super(effect);
}
@Override
public BrutalDeceiverAbility copy() {
return new BrutalDeceiverAbility(this);
public BrutalDeceiverEffect copy() {
return new BrutalDeceiverEffect(this);
}
@Override
public boolean checkIfClause(Game game) {
Player player = game.getPlayer(this.getControllerId());
if (player != null) {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
if (controller != null && sourceObject != null) {
Cards cards = new CardsImpl();
Card card = player.getLibrary().getFromTop(game);
Card card = controller.getLibrary().getFromTop(game);
if (card != null) {
cards.add(card);
player.revealCards("Brutal Deceiver", cards, game);
if (card != null && card.isLand()) {
return true;
controller.revealCards(sourceObject.getIdName(), cards, game);
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;
}
@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.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(
new GainAbilityControlledEffect(MenaceAbility.getInstance(), Duration.WhileOnBattlefield),
new GainAbilityControlledEffect(new MenaceAbility(), Duration.WhileOnBattlefield),
MonstrousCondition.instance,
"As long as Bull Rancor is monstrous, creatures you control have menace")
));

View file

@ -52,7 +52,6 @@ import mage.target.common.TargetCardInYourGraveyard;
*
* @author cbt33
*/
public class CabalInquisitor extends CardImpl {
public CabalInquisitor(UUID ownerId, CardSetInfo setInfo) {
@ -82,11 +81,8 @@ public class CabalInquisitor extends CardImpl {
}
}
class ActivateAsSorceryConditionalActivatedAbility extends ActivatedAbilityImpl {
private Condition condition;
private static final Effects emptyEffects = new Effects();
public ActivateAsSorceryConditionalActivatedAbility(Zone zone, Effect effect, ManaCosts cost, Condition condition) {
@ -95,10 +91,8 @@ class ActivateAsSorceryConditionalActivatedAbility extends ActivatedAbilityImpl
timing = TimingRule.SORCERY;
}
public ActivateAsSorceryConditionalActivatedAbility(final ActivateAsSorceryConditionalActivatedAbility ability) {
super(ability);
this.condition = ability.condition;
}
@Override
@ -109,14 +103,6 @@ class ActivateAsSorceryConditionalActivatedAbility extends ActivatedAbilityImpl
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
public ActivateAsSorceryConditionalActivatedAbility copy() {
return new ActivateAsSorceryConditionalActivatedAbility(this);

View file

@ -53,6 +53,7 @@ public class CandelabraOfTawnos extends CardImpl {
public CandelabraOfTawnos(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}");
//TODO: Make ability properly copiable
// {X}, {T}: Untap X target lands.
Effect effect = new UntapTargetEffect();
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.)
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(
MenaceAbility.getInstance(),
new MenaceAbility(),
Duration.WhileOnBattlefield, filterGoblin,
"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.)
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(
MenaceAbility.getInstance(),
new MenaceAbility(),
Duration.WhileOnBattlefield, filterElemental,
"Each Elemental you control has menace. (They can't be blocked except by two or more creatures.)")));
}

View file

@ -92,7 +92,7 @@ class CelestialKirinEffect extends OneShotEffect {
@Override
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) {
int cmc = spell.getConvertedManaCost();
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
* 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.
Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect();
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 {
ChandraTheFirebrandAbility() {
super(new CopyTargetSpellEffect(), Duration.EndOfTurn);
super(new CopyTargetSpellEffect(true), Duration.EndOfTurn);
}
ChandraTheFirebrandAbility(final ChandraTheFirebrandAbility ability) {

View file

@ -56,7 +56,7 @@ public class ChitinousCloak extends CardImpl {
// Equipped creature gets +2/+2 and has menace.
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");
ability.addEffect(effect);
this.addAbility(ability);

View file

@ -61,13 +61,13 @@ public class ChitteringHost extends MeldCard {
this.addAbility(HasteAbility.getInstance());
// 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.
Effect effect = new BoostControlledEffect(1, 0, Duration.EndOfTurn, true);
effect.setText("other creatures you control get +1/+0");
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");
ability.addEffect(effect);
this.addAbility(ability);

View file

@ -91,8 +91,6 @@ class ChronatogTotemAbility extends LimitedTimesPerTurnActivatedAbility {
private static final Effects emptyEffects = new Effects();
private final Condition condition;
public ChronatogTotemAbility(Zone zone, Effect effect, Cost cost, Condition condition) {
super(zone, effect, cost);
this.condition = condition;
@ -100,7 +98,6 @@ class ChronatogTotemAbility extends LimitedTimesPerTurnActivatedAbility {
public ChronatogTotemAbility(ChronatogTotemAbility ability) {
super(ability);
this.condition = ability.condition;
}
@Override
@ -111,14 +108,6 @@ class ChronatogTotemAbility extends LimitedTimesPerTurnActivatedAbility {
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
public ChronatogTotemAbility copy() {
return new ChronatogTotemAbility(this);

View file

@ -98,7 +98,7 @@ class CloudhoofKirinEffect extends OneShotEffect {
@Override
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) {
Player targetPlayer = null;
for (Target target : source.getTargets()) {

View file

@ -60,7 +60,7 @@ public class ClovenCasting extends CardImpl {
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.
Effect effect = new CopyTargetSpellEffect();
Effect effect = new CopyTargetSpellEffect(true);
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));
}

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.effects.common.DrawCardSourceControllerEffect;
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.cards.CardImpl;
import mage.cards.CardSetInfo;
@ -71,7 +71,7 @@ public class ConquerorsFoothold extends CardImpl {
// {6}, {T}: Return target card from your graveyard to your hand.
SimpleActivatedAbility ability3 = new SimpleActivatedAbility(Zone.BATTLEFIELD,
new ReturnToHandTargetEffect(),
new ReturnFromGraveyardToHandTargetEffect(),
new ManaCostsImpl("{6}"));
ability3.addCost(new TapSourceCost());
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");
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.
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")));

View file

@ -29,16 +29,17 @@ package mage.cards.c;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.DealsDamageToACreatureTriggeredAbility;
import mage.abilities.common.LimitedTimesPerTurnActivatedAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.abilities.effects.common.LookLibraryControllerEffect;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.*;
import mage.constants.CardType;
import mage.constants.SubType;
@ -82,7 +83,7 @@ class CruelDeceiverEffect extends OneShotEffect {
public CruelDeceiverEffect() {
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) {
@ -96,15 +97,17 @@ class CruelDeceiverEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
if (controller != null && sourceObject != null) {
Cards cards = new CardsImpl();
Card card = player.getLibrary().getFromTop(game);
Card card = controller.getLibrary().getFromTop(game);
if (card != null) {
cards.add(card);
player.revealCards("Cruel Deceiver", cards, game);
if (card != null && card.isLand()) {
game.addEffect(new BoostSourceEffect(2,2,Duration.EndOfTurn), source);
game.addEffect(new GainAbilitySourceEffect(TrampleAbility.getInstance(),Duration.EndOfTurn), source);
controller.revealCards(sourceObject.getIdName(), cards, game);
if (card.isLand()) {
game.addEffect(new GainAbilitySourceEffect(new DealsDamageToACreatureTriggeredAbility(new DestroyTargetEffect(true), false, false, true), Duration.EndOfTurn), source);
}
}
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.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.
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");

View file

@ -143,7 +143,7 @@ class CurseOfEchoesEffect extends OneShotEffect {
@Override
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) {
String chooseMessage = "Copy target spell? You may choose new targets for the copy.";
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);
// Menace
this.addAbility(MenaceAbility.getInstance());
this.addAbility(new MenaceAbility());
}

View file

@ -28,21 +28,17 @@
package mage.cards.c;
import java.util.UUID;
import mage.abilities.Ability;
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.keyword.OverloadAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.common.FilterNonlandPermanent;
import mage.filter.predicate.permanent.ControllerPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.common.TargetNonlandPermanent;
/**
@ -65,7 +61,9 @@ public class CyclonicRift extends CardImpl {
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.")
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) {
@ -77,33 +75,3 @@ public class CyclonicRift extends CardImpl {
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;
// Menace
this.addAbility(MenaceAbility.getInstance());
this.addAbility(new MenaceAbility());
// Lifelink
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");
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) {

View file

@ -40,12 +40,9 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.other.OwnerPredicate;
import mage.target.common.TargetCardInASingleGraveyard;
import mage.target.common.TargetCardInOpponentsGraveyard;
/**
*
@ -53,12 +50,6 @@ import mage.target.common.TargetCardInASingleGraveyard;
*/
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) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}");
@ -73,7 +64,7 @@ public class DeadeyeTracker extends CardImpl {
Effect effect = new ExploreSourceEffect();
effect.setText("{this} explores");
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);
}

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.abilities.common.SimpleActivatedAbility;
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.cards.CardImpl;
import mage.cards.CardSetInfo;
@ -69,7 +69,9 @@ public class DeathlessAncient extends CardImpl {
this.addAbility(FlyingAbility.getInstance());
// 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.
this.addAbility(new AsEntersBattlefieldAbility(new NameACardEffect(NameACardEffect.TypeOfName.ALL)));
//TODO: Make ability properly copiable
// {U}: Counter target spell with the chosen name.
SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterTargetEffect(), new ManaCostsImpl("{U}"));
ability.addTarget(new TargetSpell(filter));

View file

@ -68,6 +68,7 @@ public class DeepfireElemental extends CardImpl {
this.power = 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.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{X}{X}{1}"));
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.)
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.
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);
// Menace
this.addAbility(MenaceAbility.getInstance());
this.addAbility(new MenaceAbility());
// Melee
this.addAbility(new MeleeAbility());
}

View file

@ -48,7 +48,7 @@ public class DerangedWhelp extends CardImpl {
this.toughness = new MageInt(1);
// Menace
this.addAbility(MenaceAbility.getInstance());
this.addAbility(new MenaceAbility());
}
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.
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) {

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);
// Menace
this.addAbility(MenaceAbility.getInstance());
this.addAbility(new MenaceAbility());
// Deathtouch
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.Outcome;
import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.FilterSpell;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
@ -92,12 +91,9 @@ class DovescapeEffect extends OneShotEffect {
@Override
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;
UUID spellControllerID = null;
if (spell == null) {
spell = (Spell) game.getLastKnownInformation(this.getTargetPointer().getFirst(game, source), Zone.STACK);
}
if (spell != null) {
spellCMC = spell.getConvertedManaCost();
spellControllerID = spell.getControllerId();

View file

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

View file

@ -63,7 +63,7 @@ public class DrivenDespair extends SplitCard {
// Aftermath
((CardImpl) (getRightHalfCard())).addAbility(new AftermathAbility().setRuleAtTheTop(true));
// Until end of turn, creatures you control gain menace and "Whenever this creature deals combat damage to a player, that player discards a card."
getRightHalfCard().getSpellAbility().addEffect(new GainAbilityControlledEffect(MenaceAbility.getInstance(), Duration.EndOfTurn));
getRightHalfCard().getSpellAbility().addEffect(new GainAbilityControlledEffect(new MenaceAbility(), Duration.EndOfTurn));
ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DiscardTargetEffect(1), false, true);
getRightHalfCard().getSpellAbility().addEffect(new GainAbilityControlledEffect(ability, Duration.EndOfTurn)
.setText("and \"Whenever this creature deals combat damage to a player, that player discards a card.\""));

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