Merge pull request #23 from magefree/master

Merge https://github.com/magefree/mage
This commit is contained in:
Zzooouhh 2017-12-18 03:08:01 +01:00 committed by GitHub
commit a4ce4e4b17
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
75 changed files with 4096 additions and 256 deletions

View file

@ -691,8 +691,10 @@ public class DeckEditorPanel extends javax.swing.JPanel {
break;
case 1:
btnImportFromClipboardActionPerformed(evt);
break;
case 2:
btnImportFromClipboardActionWAppendPerformed(evt);
break;
}
});
@ -803,6 +805,14 @@ public class DeckEditorPanel extends javax.swing.JPanel {
.addComponent(jSplitPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 615, Short.MAX_VALUE));
}
private void processAndShowImportErrors(StringBuilder errorMessages){
// show up errors list
if (errorMessages.length() > 0){
String mes = "Founded problems with deck: \n\n" + errorMessages.toString();
JOptionPane.showMessageDialog(MageFrame.getDesktop(), mes.substring(0, Math.min(1000, mes.length())), "Errors while loading deck", JOptionPane.WARNING_MESSAGE);
}
}
/**
* @param evt ActionEvent
*/
@ -814,11 +824,23 @@ public class DeckEditorPanel extends javax.swing.JPanel {
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
Deck newDeck = null;
StringBuilder errorMessages = new StringBuilder();
MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR));
try {
deck = Deck.load(DeckImporterUtil.importDeck(dialog.getTmpPath()), true, true);
refreshDeck();
newDeck = Deck.load(DeckImporterUtil.importDeck(dialog.getTmpPath(), errorMessages), true, true);
processAndShowImportErrors(errorMessages);
if (newDeck != null) {
deck = newDeck;
refreshDeck();
}
} catch (GameException e1) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), e1.getMessage(), "Error loading deck", JOptionPane.ERROR_MESSAGE);
}finally {
MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
}
});
@ -836,16 +858,22 @@ public class DeckEditorPanel extends javax.swing.JPanel {
@Override
public void windowClosed(WindowEvent e) {
Deck deckToAppend = null;
StringBuilder errorMessages = new StringBuilder();
MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR));
try {
deckToAppend = Deck.load(DeckImporterUtil.importDeck(dialog.getTmpPath()), true, true);
deckToAppend = Deck.load(DeckImporterUtil.importDeck(dialog.getTmpPath(), errorMessages), true, true);
processAndShowImportErrors(errorMessages);
if (deckToAppend != null) {
deck = Deck.append(deckToAppend, deck);
refreshDeck();
}
} catch (GameException e1) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), e1.getMessage(), "Error loading deck", JOptionPane.ERROR_MESSAGE);
}finally {
MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
}
});
}
@ -875,20 +903,31 @@ public class DeckEditorPanel extends javax.swing.JPanel {
}
}
}
MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR));
try {
setCursor(new Cursor(Cursor.WAIT_CURSOR));
deck = Deck.load(DeckImporterUtil.importDeck(file.getPath()), true, true);
Deck newDeck = null;
StringBuilder errorMessages = new StringBuilder();
newDeck = Deck.load(DeckImporterUtil.importDeck(file.getPath(), errorMessages), true, true);
processAndShowImportErrors(errorMessages);
if (newDeck != null) {
deck = newDeck;
refreshDeck(true);
}
// save last deck history
try {
MageFrame.getPreferences().put("lastDeckFolder", file.getCanonicalPath());
} catch (IOException ex) {
logger.error("Error on save last load deck folder: " + ex.getMessage());
}
} catch (GameException ex) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage(), "Error loading deck", JOptionPane.ERROR_MESSAGE);
} finally {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
refreshDeck(true);
try {
if (file != null) {
MageFrame.getPreferences().put("lastDeckFolder", file.getCanonicalPath());
}
} catch (IOException ex) {
MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
}
fcSelectDeck.setSelectedFile(null);
@ -924,7 +963,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
if (!fileName.endsWith(".dck")) {
fileName += ".dck";
}
setCursor(new Cursor(Cursor.WAIT_CURSOR));
MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR));
DeckCardLists cardLists = deck.getDeckCardLists();
cardLists.setCardLayout(deckArea.getCardLayout());
cardLists.setSideboardLayout(deckArea.getSideboardLayout());
@ -932,7 +971,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
} catch (FileNotFoundException ex) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage() + "\nTry ensuring that the selected directory is writable.", "Error saving deck", JOptionPane.ERROR_MESSAGE);
} finally {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
try {
MageFrame.getPreferences().put("lastDeckFolder", file.getCanonicalPath());
@ -967,29 +1006,36 @@ public class DeckEditorPanel extends javax.swing.JPanel {
int ret = fcImportDeck.showOpenDialog(this);
if (ret == JFileChooser.APPROVE_OPTION) {
File file = fcImportDeck.getSelectedFile();
MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR));
try {
setCursor(new Cursor(Cursor.WAIT_CURSOR));
DeckImporter importer = DeckImporterUtil.getDeckImporter(file.getPath());
if (importer != null) {
deck = Deck.load(importer.importDeck(file.getPath()));
String errors = importer.getErrors();
if (!errors.isEmpty()) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), errors, "Error importing deck", JOptionPane.ERROR_MESSAGE);
StringBuilder errorMessages = new StringBuilder();
Deck newDeck = null;
newDeck = Deck.load(importer.importDeck(file.getPath(), errorMessages));
processAndShowImportErrors(errorMessages);
if (newDeck != null) {
deck = newDeck;
refreshDeck();
}
// save last deck import folder
try {
MageFrame.getPreferences().put("lastImportFolder", file.getCanonicalPath());
} catch (IOException ex) {
logger.error("Error on save last used import folder: " + ex.getMessage());
}
} else {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), "Unknown deck format", "Error importing deck", JOptionPane.ERROR_MESSAGE);
}
} catch (Exception ex) {
logger.fatal(ex);
} finally {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
refreshDeck();
try {
if (file != null) {
MageFrame.getPreferences().put("lastImportFolder", file.getCanonicalPath());
}
} catch (IOException ex) {
MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
}
fcImportDeck.setSelectedFile(null);
@ -1031,7 +1077,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
private void btnGenDeckActionPerformed(ActionEvent evt) {
try {
setCursor(new Cursor(Cursor.WAIT_CURSOR));
MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR));
String path = DeckGenerator.generateDeck();
deck = Deck.load(DeckImporterUtil.importDeck(path), true, true);
} catch (GameException ex) {
@ -1039,7 +1085,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
} catch (DeckGeneratorException ex) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage(), "Generator error", JOptionPane.ERROR_MESSAGE);
} finally {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
refreshDeck();
}

View file

@ -464,7 +464,7 @@ public class MageBook extends JComponent {
// first run for numbers list
LinkedList<Integer> haveNumbers = new LinkedList<>();
for (ExpansionSet.SetCardInfo card: cards){
int cardNumber = Integer.parseInt(card.getCardNumber());
int cardNumber = card.getCardNumberAsInt();
startNumber = min(startNumber, cardNumber);
endNumber = Math.max(endNumber, cardNumber);
haveNumbers.add(cardNumber);

View file

@ -291,11 +291,8 @@ public final class ManaSymbols {
};
t.setTranscodingHints(transcoderHints);
t.transcode(input, null);
}
catch (TranscoderException ex) {
// Requires Java 6
ex.printStackTrace();
throw new IOException("Couldn't convert " + svgFile);
} catch (Exception e) {
throw new IOException("Couldn't convert svg file: " + svgFile + " , reason: " + e.getMessage());
}
finally {
cssFile.delete();
@ -348,7 +345,7 @@ public final class ManaSymbols {
//imagePointer[0];
}
private static File getSymbolFileNameAsSVG(String symbol){
public static File getSymbolFileNameAsSVG(String symbol){
return new File(getResourceSymbolsPath(ResourceSymbolSize.SVG) + symbol + ".svg");
}
@ -364,7 +361,7 @@ public final class ManaSymbols {
return loadSVG(sourceFile, resizeToWidth, resizeToHeight, true);
} catch (Exception e) {
LOGGER.error("Can't load svg symbol: " + sourceFile.getPath());
LOGGER.error("Can't load svg symbol: " + sourceFile.getPath() + " , reason: " + e.getMessage());
return null;
}
}
@ -724,14 +721,18 @@ public final class ManaSymbols {
// not need to add different images (width and height do the work)
// use best png size (generated on startup) TODO: add reload images after update
String htmlImagesPath = getResourceSymbolsPath(ResourceSymbolSize.PNG);
htmlImagesPath = htmlImagesPath
.replace("$", "@S@"); // paths with $ will rise error, need escape that
replaced = REPLACE_SYMBOLS_PATTERN.matcher(replaced).replaceAll(
"<img src='" + filePathToUrl(htmlImagesPath) + "$1$2" + ".png' alt='$1$2' width="
+ symbolSize + " height=" + symbolSize + '>');
// ignore data restore
replaced = replaced.replace("|source|", "{source}");
replaced = replaced.replace("|this|", "{this}");
replaced = replaced
.replace("|source|", "{source}")
.replace("|this|", "{this}")
.replace("@S@", "$");
return replaced;
}

View file

@ -21,6 +21,7 @@ import org.mage.plugins.card.dl.Downloader;
import org.mage.plugins.card.dl.sources.DirectLinksForDownload;
import org.mage.plugins.card.dl.sources.GathererSets;
import org.mage.plugins.card.dl.sources.GathererSymbols;
import org.mage.plugins.card.dl.sources.ScryfallSymbolsSource;
import org.mage.plugins.card.images.ImageCache;
import org.mage.plugins.card.info.CardInfoPaneImpl;
@ -529,7 +530,9 @@ public class CardPluginImpl implements CardPlugin {
public void downloadSymbols(String imagesDir) {
final DownloadGui g = new DownloadGui(new Downloader());
Iterable<DownloadJob> it = new GathererSymbols();
Iterable<DownloadJob> it;
it = new GathererSymbols();
for (DownloadJob job : it) {
g.getDownloader().add(job);
}
@ -539,6 +542,11 @@ public class CardPluginImpl implements CardPlugin {
g.getDownloader().add(job);
}
it = new ScryfallSymbolsSource();
for (DownloadJob job : it) {
g.getDownloader().add(job);
}
/*
it = new CardFrames(imagesDir); // TODO: delete frames download (not need now)
for (DownloadJob job : it) {
@ -551,7 +559,7 @@ public class CardPluginImpl implements CardPlugin {
g.getDownloader().add(job);
}
JDialog d = new JDialog((Frame) null, "Download pictures", false);
JDialog d = new JDialog((Frame) null, "Download symbols", false);
d.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
d.addWindowListener(new WindowAdapter() {
@Override

View file

@ -28,7 +28,6 @@ import org.mage.plugins.card.utils.CardImageUtils;
public class DownloadJob extends AbstractLaternaBean {
public enum State {
NEW, WORKING, FINISHED, ABORTED
}

View file

@ -220,9 +220,10 @@ public class GathererSets implements Iterable<DownloadJob> {
continue; // can't do other checks
}
// 2. missing rarity icon:
// WARNING, need too much time (60+ secs), only for debug mode
if (logger.isDebugEnabled()) {
// 2. missing rarity icon:
// WARNING, need too much time (60+ secs), only for debug mode
///*
if ((set.getCardsByRarity(Rarity.COMMON).size() > 0) && !res.haveCommon) {
logger.error(String.format("Symbols: set have common cards, but don't download icon: %s (%s)", set.getCode(), set.getName()));
}
@ -235,6 +236,29 @@ public class GathererSets implements Iterable<DownloadJob> {
if ((set.getCardsByRarity(Rarity.MYTHIC).size() > 0) && !res.haveMyth) {
logger.error(String.format("Symbols: set have mythic cards, but don't download icon: %s (%s)", set.getCode(), set.getName()));
}
//*/
// 3. info: sets with alternative numbers
for(ExpansionSet.SetCardInfo card: set.getSetCardInfo()){
if (String.valueOf(card.getCardNumberAsInt()).length() != card.getCardNumber().length()){
logger.info(String.format("Symbols: set have alternative card but do not config to it: %s (%s)", set.getCode(), set.getName()));
break;
}
}
// 4. info: sets with missing cards for boosters (todo: what about +20 number for alternative land arts?)
if (set.getMaxCardNumberInBooster() != Integer.MAX_VALUE)
{
for(ExpansionSet.SetCardInfo card: set.getSetCardInfo()){
if (card.getCardNumberAsInt() > set.getMaxCardNumberInBooster()){
if (card.getRarity() == Rarity.LAND) {
logger.info(String.format("Symbols: set's booster have land above max card number: %s (%s), %s - %s", set.getCode(), set.getName(), card.getCardNumber(), card.getName()));
}else {
logger.info(String.format("Symbols: set's booster missing nonland card:: %s (%s), %s - %s", set.getCode(), set.getName(), card.getCardNumber(), card.getName()));
}
}
}
}
}
}
@ -313,4 +337,4 @@ public class GathererSets implements Iterable<DownloadJob> {
String url = "http://gatherer.wizards.com/Handlers/Image.ashx?type=symbol&set=" + set + "&size=small&rarity=" + urlRarity;
return new DownloadJob(set + '-' + rarity, fromURL(url), toFile(dst));
}
}
}

View file

@ -253,11 +253,13 @@ public enum MythicspoilerComSource implements CardImageSource {
supportedSets.add("C17");
supportedSets.add("IMA");
supportedSets.add("XLN");
supportedSets.add("RIX");
sets = new LinkedHashMap<>();
setsAliases = new HashMap<>();
setsAliases.put("exp", "bfz");
setsAliases.put("xln", "ixa");
setsAliases.put("nem", "nms");
cardNameAliases = new HashMap<>();
// set+wrong name from web side => correct card name
cardNameAliases.put("MM2-otherwordlyjourney", "otherworldlyjourney");
@ -294,6 +296,10 @@ public enum MythicspoilerComSource implements CardImageSource {
links.put("spitfirebastion", "spitfirebastion");
manualLinks.put("XLN", links);
HashMap<String, String> linksRix = new HashMap<>();
linksRix.put("vaultofcatlacan", "vaultofcatlacan");
manualLinks.put("RIX", linksRix);
cardNameAliasesStart = new HashMap<>();
HashSet<String> names = new HashSet<>();
names.add("eldrazidevastator.jpg");

View file

@ -204,8 +204,8 @@ public enum ScryfallImageSource implements CardImageSource {
supportedSets.add("IMA");
// supportedSets.add("E02");
// supportedSets.add("V17");
// supportedSets.add("UST");
// supportedSets.add("RIX");
supportedSets.add("UST");
supportedSets.add("RIX");
// supportedSets.add("A25");
// supportedSets.add("DOM");
// supportedSets.add("M19");

View file

@ -0,0 +1,171 @@
package org.mage.plugins.card.dl.sources;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.mage.plugins.card.dl.DownloadJob;
import static org.mage.card.arcane.ManaSymbols.getSymbolFileNameAsSVG;
import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
// TODO: add force to download symbols (rewrite exist files)
/**
*
* @author jaydi85@gmail.com
*
*/
public class ScryfallSymbolsSource implements Iterable<DownloadJob> {
static final String SOURCE_URL = "https://assets.scryfall.com/assets/scryfall.css"; // search css-file on https://scryfall.com/docs/api/colors
static final String STATE_PROP_NAME = "state";
static final String DOWNLOAD_TEMP_FILE = getImagesDir() + File.separator + "temp" + File.separator + "scryfall-symbols-source.txt";
// card-symbol-(.{1,10}){background-image.+base64,(.+)("\)})
// see https://regex101.com/
static final String REGEXP_MANA_PATTERN = "card-symbol-(.{1,10})\\{background-image.+base64,(.+)(\"\\)\\})";
protected static final org.apache.log4j.Logger LOGGER = org.apache.log4j.Logger.getLogger(ScryfallSymbolsSource.class);
private static final int SYMBOLS_NUMBER_START = 0;
private static final int SYMBOLS_NUMBER_END = 20;
// copy-past symbols list from gatherer download
private static final String[] SYMBOLS_LIST = {"W", "U", "B", "R", "G",
"W/U", "U/B", "B/R", "R/G", "G/W", "W/B", "U/R", "B/G", "R/W", "G/U",
"2/W", "2/U", "2/B", "2/R", "2/G",
"WP", "UP", "BP", "RP", "GP",
"X", "S", "T", "Q", "C", "E"};
@Override
public Iterator<DownloadJob> iterator() {
ArrayList<DownloadJob> jobs = new ArrayList<>();
// all symbols on one page
jobs.add(generateDownloadJob());
return jobs.iterator();
}
private void parseData(String sourcePath){
String sourceData = "";
try {
sourceData = new String(Files.readAllBytes(Paths.get(sourcePath)));
}catch (IOException e) {
LOGGER.error("Can't open file to parse data: " + sourcePath + " , reason: " + e.getMessage());
}
// gen symbols list
ArrayList<String> allMageSymbols = new ArrayList<>();
for(int i = 0; i < SYMBOLS_LIST.length; i++){
allMageSymbols.add(SYMBOLS_LIST[i]);
}
for(Integer i = SYMBOLS_NUMBER_START; i <= SYMBOLS_NUMBER_END; i++){
allMageSymbols.add(String.valueOf(SYMBOLS_NUMBER_START + i));
}
Map<String, String> foundedData = new HashMap<>();
// search raw data
sourceData = sourceData.replaceAll(".card-symbol", "\n.card-symbol"); // css as one line, but need multiline
Pattern regex = Pattern.compile(REGEXP_MANA_PATTERN);
Matcher regexMatcher = regex.matcher(sourceData);
while (regexMatcher.find()) {
String symbolCode = regexMatcher.group(1).trim();
String symbolData = regexMatcher.group(2).trim().replace(" ", "").replaceAll("\n", ""); // decoder need only wrapped text as one line
foundedData.put(symbolCode, symbolData);
}
// dirs maker
File dir = getSymbolFileNameAsSVG("W").getParentFile();
if(!dir.exists()){
dir.mkdirs();
}
// decode and save data (only if not exist)
for(String needCode: allMageSymbols){
String searchCode = needCode.replace("/", "");
if(!foundedData.containsKey(searchCode))
{
LOGGER.warn("Can't found symbol code from scryfall: " + searchCode);
continue;
}
File destFile = getSymbolFileNameAsSVG(searchCode);
if (destFile.exists() && (destFile.length() > 0)){
continue;
}
try {
// base64 transform
String data64 = foundedData.get(searchCode);
Base64.Decoder dec = Base64.getDecoder();
byte[] fileData = dec.decode(data64);
FileOutputStream stream = new FileOutputStream(destFile);
stream.write(fileData);
stream.close();
LOGGER.info("New svg symbol downloaded: " + needCode);
} catch (Exception e) {
LOGGER.error("Can't decode svg icon and save to file: " + destFile.getPath() + ", reason: " + e.getMessage());
}
}
}
private class ScryfallSymbolsDownloadJob extends DownloadJob{
// listener for data parse after download complete
private class ScryDownloadOnFinishedListener implements PropertyChangeListener {
private String downloadedFile;
public ScryDownloadOnFinishedListener(String ADestFile){
this.downloadedFile = ADestFile;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (!evt.getPropertyName().equals(STATE_PROP_NAME)){
throw new IllegalArgumentException("Unknown download property " + evt.getPropertyName());
}
if (evt.getNewValue() != State.FINISHED){
return;
}
// parse data and save to dest
parseData(this.downloadedFile);
}
}
private String destFile = "";
public ScryfallSymbolsDownloadJob() {
super("Scryfall symbols source", fromURL(SOURCE_URL), toFile(DOWNLOAD_TEMP_FILE));
this.destFile = DOWNLOAD_TEMP_FILE;
this.addPropertyChangeListener(STATE_PROP_NAME, new ScryDownloadOnFinishedListener(this.destFile));
// clear dest file (always download new data)
File file = new File(this.destFile);
if (file.exists()){
file.delete();
}
}
}
private DownloadJob generateDownloadJob() {
return new ScryfallSymbolsDownloadJob();
}
}

View file

@ -43,6 +43,8 @@ import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
public class DownloadPictures extends DefaultBoundedRangeModel implements Runnable {
// don't forget to remove new sets from ignore.urls to download (propeties file in resources)
private static DownloadPictures instance;
private static final Logger logger = Logger.getLogger(DownloadPictures.class);
@ -772,6 +774,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
// START to download
cardImageSource.doPause(url.getPath());
URLConnection httpConn = url.openConnection(p);
httpConn.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2");
httpConn.connect();
int responseCode = ((HttpURLConnection) httpConn).getResponseCode();

View file

@ -237,7 +237,7 @@ public final class CardImageUtils {
}
public static String generateFaceImagePath(String cardname, String set) {
return getImagesDir() + File.separator + "FACE" + File.separator + set + File.separator + prepareCardNameForFile(cardname) + File.separator + ".jpg";
return getImagesDir() + File.separator + "FACE" + File.separator + set + File.separator + prepareCardNameForFile(cardname) + ".jpg";
}
public static String generateTokenDescriptorImagePath(CardDownloadData card) {

View file

@ -1062,6 +1062,7 @@
|Generate|TOK:USG|Minion|||MinionToken|
|Generate|TOK:USG|Saproling|||SaprolingToken|
|Generate|TOK:UST|Dragon|||DragonTokenGold|
|Generate|TOK:UST|StormCrow|||StormCrowToken|
|Generate|TOK:V10|Wolf|||WolfToken|
|Generate|TOK:V11|Faerie Rogue|||OonaQueenFaerieToken|
|Generate|TOK:V12|Spirit|||SpiritToken|

View file

@ -29,7 +29,6 @@ ulg=ul
6ed=6e
btd=bd
sth=sh
nem=ne
por=po
s99=st
lgn=le
@ -74,6 +73,6 @@ dd3evg=ddaevg
dd3gvl=ddagvl
dd3jvc=ddajvc
# Remove setname as soon as the images can be downloaded
ignore.urls=TOK,DDT,V17,RIX,E02,M19,M25,DOM,UST,H17
ignore.urls=TOK,DDT,V17,E02,M19,M25,DOM,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

@ -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 = "V9b";
public final static String MAGE_VERSION_MINOR_PATCH = "V10b";
public final static String MAGE_VERSION_INFO = "";
private final int major;

View file

@ -114,7 +114,39 @@ public class AusHighlander extends Constructed {
int totalPoints = 0;
for (Map.Entry<String, Integer> entry : counts.entrySet()) {
String cn = entry.getKey();
if (cn.equals("Ancestral Recall")
|| cn.equals("Black Lotus")
|| cn.equals("Time Vault")) {
totalPoints += 4;
invalid.put(cn, "4 points");
}
if (cn.equals("Demonic Tutor")
|| cn.equals("Imperial Seal")
|| cn.equals("Mox Emerald")
|| cn.equals("Mox Jet")
|| cn.equals("Mox Pearl")
|| cn.equals("Mox Ruby")
|| cn.equals("Sol Ring")
|| cn.equals("Time Walk")
|| cn.equals("Tinker")
|| cn.equals("Vampiric Tutor")
|| cn.equals("Yawgmoth's Will")
|| cn.equals("Mox Sapphire.")) {
totalPoints += 3;
invalid.put(cn, "3 points");
}
if (cn.equals("Channel")
|| cn.equals("Dig Through Time")
|| cn.equals("Library of Alexandria")
|| cn.equals("Mana Crypt")
|| cn.equals("Mystical Tutor")
|| cn.equals("Protean Hulk")
|| cn.equals("Skullclamp")
|| cn.equals("Strip Mine")
|| cn.equals("Tolarian Academy.")) {
totalPoints += 2;
invalid.put(cn, "2 points");
}
if (cn.equals("Back to Basics")
|| cn.equals("Balance")
|| cn.equals("Birthing Pod")
@ -139,8 +171,8 @@ public class AusHighlander extends Constructed {
|| cn.equals("Natural Order")
|| cn.equals("Oath of Druids")
|| cn.equals("Personal Tutor")
|| cn.equals("Sensei's Divining Top")
|| cn.equals("Snapcaster Mage")
|| cn.equals("Steelshaper's Gift")
|| cn.equals("Stoneforge Mystic")
|| cn.equals("Survival of the Fittest")
|| cn.equals("Tainted Pact")
@ -151,50 +183,15 @@ public class AusHighlander extends Constructed {
|| cn.equals("Umezawa's Jitte")
|| cn.equals("Wasteland")
|| cn.equals("Wheel of Fortune")
|| cn.equals("Worldly Tutor")
|| cn.equals("Yawgmoth's Bargain")) {
|| cn.equals("Yawgmoth's Bargain")
|| cn.equals("Worldly Tutor")) {
totalPoints += 1;
invalid.put(cn, "1 point");
}
if (cn.equals("Channel")
|| cn.equals("Dig Through Time")
|| cn.equals("Library of Alexandria")
|| cn.equals("Mana Crypt")
|| cn.equals("Mox Emerald")
|| cn.equals("Mox Jet")
|| cn.equals("Mox Pearl")
|| cn.equals("Mox Ruby")
|| cn.equals("Mox Sapphire")
|| cn.equals("Mystical Tutor")
|| cn.equals("Protean Hulk")
|| cn.equals("Skullclamp")
|| cn.equals("Strip Mine")
|| cn.equals("Tolarian Academy")) {
totalPoints += 2;
invalid.put(cn, "2 points");
}
if (cn.equals("Demonic Tutor")
|| cn.equals("Imperial Seal")
|| cn.equals("Sol Ring")
|| cn.equals("Time Walk")
|| cn.equals("Tinker")
|| cn.equals("Vampiric Tutor")
|| cn.equals("Yawgmoth's Will")) {
totalPoints += 3;
invalid.put(cn, "3 points");
}
if (cn.equals("Ancestral Recall")
|| cn.equals("Black Lotus")
|| cn.equals("Time Vault")) {
totalPoints += 4;
invalid.put(cn, "4 points");
}
}
if (totalPoints > 7) {
invalid.put("Total points too high", "Your calculated point total was " + totalPoints);
invalid.put("Only you can see this!", "Your opponents will not be able to see this message or what cards are in your deck!");
valid = false;
}
return valid;

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.a;
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.effects.common.DestroyTargetEffect;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Zone;
import mage.target.common.TargetEnchantmentPermanent;
/**
*
* @author TheElk801
*/
public class AmateurAuteur extends CardImpl {
public AmateurAuteur(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
this.subtype.add(SubType.HUMAN);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// Sacrifice Amateur Auteur: Destroy target enchantment.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new SacrificeSourceCost());
ability.addTarget(new TargetEnchantmentPermanent());
this.addAbility(ability);
}
public AmateurAuteur(final AmateurAuteur card) {
super(card);
}
@Override
public AmateurAuteur copy() {
return new AmateurAuteur(this);
}
}

View file

@ -39,7 +39,6 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.filter.predicate.permanent.AnotherPredicate;
import mage.target.common.TargetControlledPermanent;
@ -52,7 +51,6 @@ public class ApocalypseDemon extends CardImpl {
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another creature");
static {
filter.add(new CardTypePredicate(CardType.CREATURE));
filter.add(new AnotherPredicate());
}
@ -76,6 +74,7 @@ public class ApocalypseDemon extends CardImpl {
super(card);
}
@Override
public ApocalypseDemon copy() {
return new ApocalypseDemon(this);
}

View file

@ -0,0 +1,150 @@
/*
* 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.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.HexproofAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.counters.Counter;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author spjspj
*/
public class AsLuckWouldHaveIt extends CardImpl {
static final String rule = "put a number of luck counters on {this} equal to the result. Then if there are 100 or more luck counters on {this}, you win the game.";
public AsLuckWouldHaveIt(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}");
// Hexproof
this.addAbility(HexproofAbility.getInstance());
// Whenever you roll a die, put a number of luck counters on As Luck Would Have It equal to the result. Then if there are 100 or more luck counters on As Luck Would Have It, you win the game.
this.addAbility(new AsLuckWouldHaveItTriggeredAbility());
}
public AsLuckWouldHaveIt(final AsLuckWouldHaveIt card) {
super(card);
}
@Override
public AsLuckWouldHaveIt copy() {
return new AsLuckWouldHaveIt(this);
}
}
class AsLuckWouldHaveItTriggeredAbility extends TriggeredAbilityImpl {
public AsLuckWouldHaveItTriggeredAbility() {
super(Zone.BATTLEFIELD, new AsLuckWouldHaveItEffect(), false);
}
public AsLuckWouldHaveItTriggeredAbility(final AsLuckWouldHaveItTriggeredAbility ability) {
super(ability);
}
@Override
public AsLuckWouldHaveItTriggeredAbility copy() {
return new AsLuckWouldHaveItTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DICE_ROLLED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (this.getControllerId().equals(event.getPlayerId()) && event.getFlag()) {
for (Effect effect : this.getEffects()) {
effect.setValue("rolled", event.getAmount());
}
return true;
}
return false;
}
@Override
public String getRule() {
return "Whenever you roll a die, " + super.getRule();
}
}
class AsLuckWouldHaveItEffect extends OneShotEffect {
public AsLuckWouldHaveItEffect() {
super(Outcome.Benefit);
this.staticText = "put a number of luck counters on {this} equal to the result. Then if there are 100 or more luck counters on {this}, you win the game.";
}
public AsLuckWouldHaveItEffect(final AsLuckWouldHaveItEffect effect) {
super(effect);
}
@Override
public AsLuckWouldHaveItEffect copy() {
return new AsLuckWouldHaveItEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent permanent = game.getPermanent(source.getSourceId());
if (controller != null && permanent != null) {
if (getValue("rolled") != null) {
int amount = (Integer) getValue("rolled");
permanent.addCounters(new Counter("luck", amount), source, game);
if (permanent.getCounters(game).getCount("luck") >= 100) {
Player player = game.getPlayer(permanent.getControllerId());
if (player != null) {
player.won(game);
}
}
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,92 @@
/*
* 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.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.token.GoblinToken;
import mage.players.Player;
/**
*
* @author spjspj
*/
public class BoxOfFreerangeGoblins extends CardImpl {
public BoxOfFreerangeGoblins(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{R}{R}");
// Roll a six-sided die. Create a number of 1/1 red Goblin creature tokens equal to the result.
this.getSpellAbility().addEffect(new BoxOfFreerangeGoblinsEffect());
}
public BoxOfFreerangeGoblins(final BoxOfFreerangeGoblins card) {
super(card);
}
@Override
public BoxOfFreerangeGoblins copy() {
return new BoxOfFreerangeGoblins(this);
}
}
class BoxOfFreerangeGoblinsEffect extends OneShotEffect {
public BoxOfFreerangeGoblinsEffect() {
super(Outcome.PutCreatureInPlay);
this.staticText = "Roll a six-sided die. Create a number of 1/1 red Goblin creature tokens equal to the result";
}
public BoxOfFreerangeGoblinsEffect(final BoxOfFreerangeGoblinsEffect effect) {
super(effect);
}
@Override
public BoxOfFreerangeGoblinsEffect copy() {
return new BoxOfFreerangeGoblinsEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
int amount = controller.rollDice(game, 6);
CreateTokenEffect effect = new CreateTokenEffect(new GoblinToken(), amount);
effect.apply(game, source);
return true;
}
return false;
}
}

View file

@ -0,0 +1,217 @@
/*
* 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.Set;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.IntCompareCondition;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.decorator.ConditionalActivatedAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.LoseLifeTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.ComparisonType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import mage.target.Target;
import mage.target.TargetPlayer;
import mage.target.common.TargetOpponent;
/**
*
* @author spjspj
*/
public class BuzzingWhackADoodle extends CardImpl {
public BuzzingWhackADoodle(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}");
// As Buzzing Whack-a-Doodle enters the battlefield, you and an opponent each secretly choose Whack or Doodle. Then those choices are revealed. If the choices match, Buzzing Whack-a-Doodle has that ability. Otherwise it has Buzz.
this.addAbility(new EntersBattlefieldTriggeredAbility(new BuzzingWhackADoodleEffect(), false));
// *Whack - T: Target player loses 2 life.
Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new LoseLifeTargetEffect(2), new TapSourceCost(), new WhackCondition());
ability.addTarget(new TargetPlayer());
this.addAbility(ability);
// *Doodle - T: You gain 3 life.
Ability ability2 = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new GainLifeEffect(3), new TapSourceCost(), new DoodleCondition());
this.addAbility(ability2);
// *Buzz - 2, T: Draw a card.
Ability ability3 = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{2}"), new BuzzCondition());
ability3.addCost(new TapSourceCost());
this.addAbility(ability3);
}
public BuzzingWhackADoodle(final BuzzingWhackADoodle card) {
super(card);
}
@Override
public BuzzingWhackADoodle copy() {
return new BuzzingWhackADoodle(this);
}
}
class BuzzingWhackADoodleEffect extends OneShotEffect {
BuzzingWhackADoodleEffect() {
super(Outcome.Benefit);
this.staticText = "You and an opponent each secretly choose Whack or Doodle. Then those choices are revealed. If the choices match, {this} has that ability. Otherwise it has Buzz";
}
BuzzingWhackADoodleEffect(final BuzzingWhackADoodleEffect effect) {
super(effect);
}
@Override
public BuzzingWhackADoodleEffect copy() {
return new BuzzingWhackADoodleEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
int whackCount = 0;
int doodleCount = 0;
if (controller.chooseUse(Outcome.Benefit, "Choose Whack (yes) or Doodle (no)?", source, game)) {
whackCount++;
} else {
doodleCount++;
}
Set<UUID> opponents = game.getOpponents(source.getControllerId());
if (!opponents.isEmpty()) {
Player opponent = game.getPlayer(opponents.iterator().next());
if (opponents.size() > 1) {
Target targetOpponent = new TargetOpponent(true);
if (controller.chooseTarget(Outcome.Neutral, targetOpponent, source, game)) {
opponent = game.getPlayer(targetOpponent.getFirstTarget());
game.informPlayers(controller.getLogName() + " chose " + opponent.getLogName() + " to choose Whack or Doodle");
}
}
if (opponent != null) {
if (opponent.chooseUse(Outcome.Benefit, "Choose Whack (yes) or Doodle (no)?", source, game)) {
whackCount++;
} else {
doodleCount++;
}
}
}
if (whackCount == 2) {
game.informPlayers("Whack was chosen");
game.getState().setValue("whack" + source.getSourceId(), Boolean.TRUE);
} else if (doodleCount == 2) {
game.informPlayers("Doodle was chosen");
game.getState().setValue("doodle" + source.getSourceId(), Boolean.TRUE);
} else {
game.informPlayers("Buzz was chosen");
game.getState().setValue("buzz" + source.getSourceId(), Boolean.TRUE);
}
return true;
}
return false;
}
}
class WhackCondition extends IntCompareCondition {
WhackCondition() {
super(ComparisonType.MORE_THAN, 0);
}
@Override
protected int getInputValue(Game game, Ability source) {
Object object = game.getState().getValue("whack" + source.getSourceId());
if (object != null && object instanceof Boolean && (Boolean) object) {
return 1;
}
return 0;
}
@Override
public String toString() {
return "if both players picked 'Whack'";
}
}
class DoodleCondition extends IntCompareCondition {
DoodleCondition() {
super(ComparisonType.MORE_THAN, 0);
}
@Override
protected int getInputValue(Game game, Ability source) {
Object object = game.getState().getValue("doodle" + source.getSourceId());
if (object != null && object instanceof Boolean && (Boolean) object) {
return 1;
}
return 0;
}
@Override
public String toString() {
return "if both players picked 'Doodle'";
}
}
class BuzzCondition extends IntCompareCondition {
BuzzCondition() {
super(ComparisonType.MORE_THAN, 0);
}
@Override
protected int getInputValue(Game game, Ability source) {
Object object = game.getState().getValue("buzz" + source.getSourceId());
if (object != null && object instanceof Boolean && (Boolean) object) {
return 1;
}
return 0;
}
@Override
public String toString() {
return "if both players picked differently";
}
}

View file

@ -0,0 +1,98 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.c;
import java.util.UUID;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.token.SquirrelToken;
/**
*
* @author spjspj
*/
public class ChitteringDoom extends CardImpl {
public ChitteringDoom(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}");
// Whenever you roll a 4 or higher on a die, create a 1/1 green Squirrel creature token.
this.addAbility(new ChitteringDoomTriggeredAbility());
}
public ChitteringDoom(final ChitteringDoom card) {
super(card);
}
@Override
public ChitteringDoom copy() {
return new ChitteringDoom(this);
}
}
class ChitteringDoomTriggeredAbility extends TriggeredAbilityImpl {
public ChitteringDoomTriggeredAbility() {
super(Zone.BATTLEFIELD, new CreateTokenEffect(new SquirrelToken()), false);
}
public ChitteringDoomTriggeredAbility(final ChitteringDoomTriggeredAbility ability) {
super(ability);
}
@Override
public ChitteringDoomTriggeredAbility copy() {
return new ChitteringDoomTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DICE_ROLLED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (this.getControllerId().equals(event.getPlayerId()) && event.getFlag()) {
if (event.getAmount() >= 4) {
return true;
}
}
return false;
}
@Override
public String getRule() {
return "Whenever you roll a 4 or higher on a die, create a 1/1 green Squirrel creature token";
}
}

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.keyword.StormAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.game.permanent.token.StormCrowToken;
/**
*
* @author spjspj
*/
public class CrowStorm extends CardImpl {
public CrowStorm(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}");
// Create a 1/2 blue Bird creature token with flying named Storm Crow.
this.getSpellAbility().addEffect(new CreateTokenEffect(new StormCrowToken(), 1));
// Storm (When you cast this spell, copy it for each spell cast before it this turn.)
this.addAbility(new StormAbility());
}
public CrowStorm(final CrowStorm card) {
super(card);
}
@Override
public CrowStorm copy() {
return new CrowStorm(this);
}
}

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.c;
import java.util.UUID;
import mage.MageInt;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
/**
*
* @author spjspj
*/
public class CuriousKillbot extends CardImpl {
public CuriousKillbot(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}");
this.subtype.add(SubType.KILLBOT);
this.power = new MageInt(2);
this.toughness = new MageInt(1);
}
public CuriousKillbot(final CuriousKillbot card) {
super(card);
}
@Override
public CuriousKillbot copy() {
return new CuriousKillbot(this);
}
}

View file

@ -32,7 +32,6 @@ import mage.abilities.common.ControllerAssignCombatDamageToBlockersAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.common.FilterCreaturePermanent;
/**
*

View file

@ -64,8 +64,8 @@ public class DrakestownForgotten extends CardImpl {
new AddCountersSourceEffect(
CounterType.P1P1.createInstance(),
new CardsInAllGraveyardsCount(new FilterCreatureCard()),
false),
"with X +1/+1 counters on it, where X is the number of other creatures on the battlefield"));
false),
"with X +1/+1 counters on it, where X is the number of creature cards in all graveyards"));
// {2}{B}, Remove a +1/+1 counter from Drakestown Forgotten: Target creature gets -1/-1 until end of turn.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(-1, -1, Duration.EndOfTurn), new ManaCostsImpl<>("{2}{B}"));

View file

@ -0,0 +1,97 @@
/*
* 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.e;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.continuous.BecomesSubtypeAllEffect;
import mage.abilities.effects.common.continuous.BoostControlledEffect;
import mage.abilities.keyword.SquirrellinkAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.filter.predicate.permanent.AnotherPredicate;
import mage.filter.predicate.permanent.ControllerPredicate;
import mage.filter.predicate.permanent.TokenPredicate;
import mage.util.SubTypeList;
/**
*
* @author spjspj
*/
public class EarlOfSquirrel extends CardImpl {
private final static FilterCreaturePermanent filter = new FilterCreaturePermanent("Creature tokens you control");
private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("Other squirrels you control");
static {
filter.add(new TokenPredicate());
filter.add(new ControllerPredicate(TargetController.YOU));
filter2.add(new SubtypePredicate(SubType.SQUIRREL));
}
public EarlOfSquirrel(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}");
this.subtype.add(SubType.SQUIRREL);
this.subtype.add(SubType.ADVISOR);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Squirrellink (Damage dealt by this creature also causes you to create that many 1/1 green Squirrel creature tokens.)
this.addAbility(SquirrellinkAbility.getInstance());
// Creature tokens you control are Squirrels in addition to their other creature types.
SubTypeList subTypes = new SubTypeList();
subTypes.add(SubType.SQUIRREL);
Effect effect = new BecomesSubtypeAllEffect(Duration.WhileOnBattlefield, subTypes, filter, false);
effect.setText("Creature tokens you control are Squirrels in addition to their other creature types");
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect));
// Other Squirrels you control get +1/+1.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter2, true)));
}
public EarlOfSquirrel(final EarlOfSquirrel card) {
super(card);
}
@Override
public EarlOfSquirrel copy() {
return new EarlOfSquirrel(this);
}
}

View file

@ -0,0 +1,193 @@
/*
* 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.g;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.delayed.OnLeaveReturnExiledToBattlefieldAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ChooseOpponentEffect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.ExileTargetEffect;
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.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetCreaturePermanent;
import mage.util.CardUtil;
/**
*
* @author spjspj
*/
public class GOTOJAIL extends CardImpl {
private final static FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls");
static {
filter.add(new ControllerPredicate(TargetController.OPPONENT));
}
public GOTOJAIL(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}");
// When GO TO JAIL enters the battlefield, exile target creature an opponent controls until GO TO JAIL leaves the battlefield.
Ability ability = new EntersBattlefieldTriggeredAbility(new GoToJailExileEffect());
ability.addTarget(new TargetCreaturePermanent(filter));
ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnLeaveReturnExiledToBattlefieldAbility()));
this.addAbility(ability);
// At the beginning of the upkeep of the exiled card's owner, that player rolls two six-sided dice. If he or she rolls doubles, sacrifice GO TO JAIL.
this.addAbility(new GoToJailTriggeredAbility());
}
public GOTOJAIL(final GOTOJAIL card) {
super(card);
}
@Override
public GOTOJAIL copy() {
return new GOTOJAIL(this);
}
}
class GoToJailExileEffect extends OneShotEffect {
public GoToJailExileEffect() {
super(Outcome.Benefit);
this.staticText = "exile target creature an opponent controls until {this} leaves the battlefield.";
}
public GoToJailExileEffect(final GoToJailExileEffect effect) {
super(effect);
}
@Override
public GoToJailExileEffect copy() {
return new GoToJailExileEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = (Permanent) source.getSourceObjectIfItStillExists(game);
Permanent targetPermanent = game.getPermanent(targetPointer.getFirst(game, source));
// If GO TO JAIL leaves the battlefield before its triggered ability resolves,
// the target creature won't be exiled.
if (permanent != null && targetPermanent != null) {
Player controller = game.getPlayer(targetPermanent.getControllerId());
if (controller != null) {
game.getState().setValue(permanent.getId() + ChooseOpponentEffect.VALUE_KEY, controller.getId());
return new ExileTargetEffect(CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), permanent.getIdName()).apply(game, source);
}
}
return false;
}
}
class GoToJailTriggeredAbility extends TriggeredAbilityImpl {
public GoToJailTriggeredAbility() {
super(Zone.BATTLEFIELD, new GoToJailUpkeepEffect(), false);
}
public GoToJailTriggeredAbility(final GoToJailTriggeredAbility ability) {
super(ability);
}
@Override
public GoToJailTriggeredAbility copy() {
return new GoToJailTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.UPKEEP_STEP_PRE;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
return event.getPlayerId().equals((UUID) game.getState().getValue(this.getSourceId().toString() + ChooseOpponentEffect.VALUE_KEY));
}
@Override
public String getRule() {
return "At the beginning of the chosen player's upkeep, " + super.getRule();
}
}
class GoToJailUpkeepEffect extends OneShotEffect {
public GoToJailUpkeepEffect() {
super(Outcome.Sacrifice);
this.staticText = "that player rolls two six-sided dice. If he or she rolls doubles, sacrifice {this}";
}
public GoToJailUpkeepEffect(final GoToJailUpkeepEffect effect) {
super(effect);
}
@Override
public GoToJailUpkeepEffect copy() {
return new GoToJailUpkeepEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObjectIfItStillExists(game);
Permanent permanent = (Permanent) source.getSourceObjectIfItStillExists(game);
if (sourceObject != null && sourceObject instanceof Permanent && permanent != null) {
UUID opponentId = (UUID) game.getState().getValue(sourceObject.getId().toString() + ChooseOpponentEffect.VALUE_KEY);
Player opponent = game.getPlayer(opponentId);
if (opponent != null) {
int thisRoll = opponent.rollDice(game, 6);
int thatRoll = opponent.rollDice(game, 6);
if (thisRoll == thatRoll) {
return permanent.sacrifice(source.getSourceId(), game);
}
}
}
return false;
}
}

View file

@ -0,0 +1,115 @@
/*
* 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.g;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.BattleCryAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.filter.FilterPermanent;
import mage.filter.predicate.permanent.CounterPredicate;
import mage.game.Game;
import mage.game.permanent.token.GoblinToken;
import mage.game.permanent.token.Token;
import mage.players.Player;
/**
*
* @author spjspj
*/
public class GarbageElemental extends CardImpl {
public GarbageElemental(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}");
this.subtype.add("Elemental");
this.power = new MageInt(3);
this.toughness = new MageInt(2);
// Battle cry
this.addAbility(new BattleCryAbility());
// When Garbage Elemental enters the battlefield, roll two six-sided dice. Create a number of 1/1 red Goblin creature tokens equal to the difference between those results.
this.addAbility(new EntersBattlefieldAbility(new GarbageElementalEffect(),
null,
"When {this} enters the battlefield, roll two six-sided dice. Create a number of 1/1 red Goblin creature tokens equal to the difference between those results",
null));
}
public GarbageElemental(final GarbageElemental card) {
super(card);
}
@Override
public GarbageElemental copy() {
return new GarbageElemental(this);
}
}
class GarbageElementalEffect extends OneShotEffect {
private static final FilterPermanent filter = new FilterPermanent("permanent with a counter");
static {
filter.add(new CounterPredicate(null));
}
GarbageElementalEffect() {
super(Outcome.PutCreatureInPlay);
this.staticText = "roll two six-sided dice. Create a number of 1/1 red Goblin creature tokens equal to the difference between those results";
}
GarbageElementalEffect(final GarbageElementalEffect effect) {
super(effect);
}
@Override
public GarbageElementalEffect copy() {
return new GarbageElementalEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
int thisRoll = controller.rollDice(game, 6);
int thatRoll = controller.rollDice(game, 6);
Token token = new GoblinToken();
return token.putOntoBattlefield(Math.abs(thatRoll - thisRoll), game, source.getSourceId(), source.getControllerId());
}
return false;
}
}

View file

@ -35,6 +35,7 @@ import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
import mage.abilities.effects.common.combat.CantAttackUnlessDefenderControllsPermanent;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
@ -69,6 +70,9 @@ public class GoblinRockSled extends CardImpl {
this.power = new MageInt(3);
this.toughness = new MageInt(1);
// Trample
this.addAbility(TrampleAbility.getInstance());
// Goblin Rock Sled doesn't untap during your untap step if it attacked during your last turn.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapIfAttackedLastTurnSourceEffect()), new AttackedLastTurnWatcher());

View file

@ -0,0 +1,147 @@
/*
* 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.g;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.abilities.keyword.TrampleAbility;
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.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author spjspj
*/
public class GroundPounder extends CardImpl {
public GroundPounder(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}");
this.subtype.add(SubType.GOBLIN);
this.subtype.add(SubType.WARRIOR);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// 3G: Roll a six-sided die. Ground Pounder gets +X/+X until end of turn, where X is the result.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GroundPounderEffect(), new ManaCostsImpl("{3}{G}")));
// Whenever you roll a 5 or higher on a die, Ground Pounder gains trample until end of turn.
this.addAbility(new GroundPounderTriggeredAbility());
}
public GroundPounder(final GroundPounder card) {
super(card);
}
@Override
public GroundPounder copy() {
return new GroundPounder(this);
}
}
class GroundPounderEffect extends OneShotEffect {
public GroundPounderEffect() {
super(Outcome.Benefit);
this.staticText = "Roll a six-sided die. {this} gets +X/+X until end of turn, where X is the result";
}
public GroundPounderEffect(final GroundPounderEffect effect) {
super(effect);
}
@Override
public GroundPounderEffect copy() {
return new GroundPounderEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent permanent = game.getPermanent(source.getSourceId());
if (controller != null && permanent != null) {
int amount = controller.rollDice(game, 6);
game.addEffect(new BoostSourceEffect(amount, amount, Duration.EndOfTurn), source);
return true;
}
return false;
}
}
class GroundPounderTriggeredAbility extends TriggeredAbilityImpl {
public GroundPounderTriggeredAbility() {
super(Zone.BATTLEFIELD, new GainAbilitySourceEffect(TrampleAbility.getInstance(), Duration.EndOfTurn), false);
}
public GroundPounderTriggeredAbility(final GroundPounderTriggeredAbility ability) {
super(ability);
}
@Override
public GroundPounderTriggeredAbility copy() {
return new GroundPounderTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DICE_ROLLED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (this.getControllerId().equals(event.getPlayerId()) && event.getFlag()) {
if (event.getAmount() >= 5) {
return true;
}
}
return false;
}
@Override
public String getRule() {
return "Whenever you roll a 5 or higher on a die, {this} gains trample until end of turn";
}
}

View file

@ -0,0 +1,103 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.h;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.effects.common.continuous.GainControlTargetEffect;
import mage.abilities.keyword.HasteAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetCreaturePermanent;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author spjspj
*/
public class HammerHelper extends CardImpl {
public HammerHelper(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}");
// Gain control of target creature until end of turn. Untap that creature and roll a six-sided die. Until end of turn, it gains haste and gets +X/+0, where X is the result.
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
this.getSpellAbility().addEffect(new HammerHelperEffect());
}
public HammerHelper(final HammerHelper card) {
super(card);
}
@Override
public HammerHelper copy() {
return new HammerHelper(this);
}
}
class HammerHelperEffect extends OneShotEffect {
HammerHelperEffect() {
super(Outcome.Benefit);
staticText = "Gain control of target creature until end of turn. Untap that creature and roll a six-sided die. Until end of turn, it gains haste and gets +X/+0, where X is the result";
}
HammerHelperEffect(HammerHelperEffect effect) {
super(effect);
}
@Override
public HammerHelperEffect copy() {
return new HammerHelperEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent targetCreature = game.getPermanent(source.getFirstTarget());
if (controller != null && targetCreature != null) {
source.getEffects().get(0).setTargetPointer(new FixedTarget(targetCreature.getId()));
game.addEffect(new GainControlTargetEffect(Duration.EndOfTurn), source);
targetCreature.untap(game);
int amount = controller.rollDice(game, 6);
game.addEffect(new BoostTargetEffect(amount, 0, Duration.EndOfTurn), source);
game.addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn), source);
return true;
}
return false;
}
}

View file

@ -29,8 +29,6 @@ package mage.cards.h;
import java.util.UUID;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.costs.Cost;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.cards.CardImpl;
@ -98,14 +96,8 @@ class HauntingWindTriggeredAbility extends TriggeredAbilityImpl {
if (stackAbility == null) {
return false;
}
boolean triggerable = true;
for (Cost cost : stackAbility.getCosts()) {
if (cost instanceof TapSourceCost) {
triggerable = false;
break;
}
}
if (!triggerable) {
String abilityText = stackAbility.getRule(true);
if (abilityText.contains("{T}:") || abilityText.contains("{T},") || abilityText.contains("{T} or")) {
return false;
}
for (Effect effect : this.getEffects()) {

View file

@ -0,0 +1,133 @@
/*
* 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.h;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.effects.EntersBattlefieldEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.ReachAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.counters.CounterType;
import mage.filter.FilterPermanent;
import mage.filter.predicate.permanent.CounterPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author spjspj
*/
public class Hydradoodle extends CardImpl {
public Hydradoodle(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{X}{X}{G}{G}");
this.subtype.add(SubType.HYDRA);
this.subtype.add(SubType.HOUND);
this.power = new MageInt(0);
this.toughness = new MageInt(0);
// As Hydradoodle enters the battlefield, roll X six-sided dice. Hydradoodle enters the battlefield with a number of +1/+1 counters on it equal to the total of those results.
this.addAbility(new EntersBattlefieldAbility(new HydradoodleEffect(),
null,
"As {this} enters the battlefield, roll X six-sided dice. {this} enters the battlefield with a number of +1/+1 counters on it equal to the total of those results",
null));
// Reach
this.addAbility(ReachAbility.getInstance());
// Trample
this.addAbility(TrampleAbility.getInstance());
}
public Hydradoodle(final Hydradoodle card) {
super(card);
}
@Override
public Hydradoodle copy() {
return new Hydradoodle(this);
}
}
class HydradoodleEffect extends OneShotEffect {
private static final FilterPermanent filter = new FilterPermanent("permanent with a counter");
static {
filter.add(new CounterPredicate(null));
}
HydradoodleEffect() {
super(Outcome.BoostCreature);
this.staticText = "roll X six-sided dice. {this} enters the battlefield with a number of +1/+1 counters on it equal to the total of those results";
}
HydradoodleEffect(final HydradoodleEffect effect) {
super(effect);
}
@Override
public HydradoodleEffect copy() {
return new HydradoodleEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanentEntering(source.getSourceId());
Player controller = game.getPlayer(source.getControllerId());
if (permanent != null) {
SpellAbility spellAbility = (SpellAbility) getValue(EntersBattlefieldEffect.SOURCE_CAST_SPELL_ABILITY);
if (spellAbility != null
&& spellAbility.getSourceId().equals(source.getSourceId())
&& permanent.getZoneChangeCounter(game) == spellAbility.getSourceObjectZoneChangeCounter()) {
int amount = spellAbility.getManaCostsToPay().getX();
if (amount > 0) {
int total = 0;
for (int roll = 0; roll < amount; roll++) {
int thisRoll = controller.rollDice(game, 6);
total += thisRoll;
}
permanent.addCounters(CounterType.P1P1.createInstance(total), source, game);
}
}
return true;
}
return false;
}
}

View file

@ -0,0 +1,106 @@
/*
* 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.i;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author spjspj
*/
public class Inhumaniac extends CardImpl {
public Inhumaniac(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}");
this.subtype.add(SubType.BRAINIAC);
this.power = new MageInt(1);
this.toughness = new MageInt(1);
// At the beginning of your upkeep, roll a six-sided die. On a 3 or 4, put a +1/+1 counter on Inhumaniac. On a 5 or higher, put two +1/+1 counters on it. On a 1, remove all +1/+1 counters from Inhumaniac.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new InhumaniacEffect(), TargetController.YOU, false));
}
public Inhumaniac(final Inhumaniac card) {
super(card);
}
@Override
public Inhumaniac copy() {
return new Inhumaniac(this);
}
}
class InhumaniacEffect extends OneShotEffect {
public InhumaniacEffect() {
super(Outcome.Benefit);
this.staticText = "roll a six-sided die. On a 3 or 4, put a +1/+1 counter on {this}. On a 5 or higher, put two +1/+1 counters on it. On a 1, remove all +1/+1 counters from {this}";
}
public InhumaniacEffect(final InhumaniacEffect effect) {
super(effect);
}
@Override
public InhumaniacEffect copy() {
return new InhumaniacEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent permanent = game.getPermanent(source.getSourceId());
if (controller != null && permanent != null) {
int amount = controller.rollDice(game, 6);
if (amount >= 3 && amount <= 4) {
permanent.addCounters(CounterType.P1P1.createInstance(1), source, game);
} else if (amount >= 5) {
permanent.addCounters(CounterType.P1P1.createInstance(2), source, game);
} else if (amount == 1) {
permanent.getCounters(game).removeAllCounters(CounterType.P1P1);
}
return true;
}
return false;
}
}

View file

@ -0,0 +1,123 @@
/*
* 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.k;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SuperType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.players.Player;
import mage.util.RandomUtil;
/**
*
* @author spjspj
*/
public class KrarksOtherThumb extends CardImpl {
public KrarksOtherThumb(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
addSuperType(SuperType.LEGENDARY);
// If you would roll a die, instead roll two of those dice and ignore one of those results.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KrarksOtherThumbEffect()));
}
public KrarksOtherThumb(final KrarksOtherThumb card) {
super(card);
}
@Override
public KrarksOtherThumb copy() {
return new KrarksOtherThumb(this);
}
}
class KrarksOtherThumbEffect extends ReplacementEffectImpl {
KrarksOtherThumbEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit);
staticText = "If you would roll a die, instead roll two die and ignore one";
}
KrarksOtherThumbEffect(final KrarksOtherThumbEffect effect) {
super(effect);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Player player = game.getPlayer(event.getPlayerId());
if (player != null) {
// event.getData holds the num of sides of the die to roll
String data = event.getData();
int numSides = Integer.parseInt(data);
int secondDieRoll = RandomUtil.nextInt(numSides) + 1;
if (!game.isSimulation()) {
game.informPlayers("[Roll a die] " + player.getLogName() + " rolled a " + secondDieRoll);
}
if (player.chooseUse(outcome, "Ignore the first die roll?", source, game)) {
event.setAmount(secondDieRoll);
game.informPlayers(player.getLogName() + " ignores the first die roll.");
} else {
game.informPlayers(player.getLogName() + " ignores the second die roll.");
}
}
return false;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ROLL_DICE;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return source.getControllerId().equals(event.getPlayerId());
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public KrarksOtherThumbEffect copy() {
return new KrarksOtherThumbEffect(this);
}
}

View file

@ -0,0 +1,116 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.l;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.UntapSourceEffect;
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
import mage.abilities.keyword.EquipAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.AttachmentType;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPlayer;
/**
*
* @author spjspj
*/
public class LobeLobber extends CardImpl {
public LobeLobber(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
this.subtype.add(SubType.EQUIPMENT);
// Equipped creature has "T: This creature deals 1 damage to target player. Roll a six-sided die. On a 5 or higher, untap it."
Effect effect = new LobeLobberEffect();
SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost());
ability.addTarget(new TargetPlayer());
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(ability, AttachmentType.EQUIPMENT)));
// Equip 2
this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(2)));
}
public LobeLobber(final LobeLobber card) {
super(card);
}
@Override
public LobeLobber copy() {
return new LobeLobber(this);
}
}
class LobeLobberEffect extends OneShotEffect {
public LobeLobberEffect() {
super(Outcome.Benefit);
this.staticText = "This creature deals 1 damage to target player. Roll a six-sided die. On a 5 or higher, untap it";
}
public LobeLobberEffect(final LobeLobberEffect effect) {
super(effect);
}
@Override
public LobeLobberEffect copy() {
return new LobeLobberEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent equipment = game.getPermanent(source.getSourceId());
Player player = game.getPlayer(source.getFirstTarget());
if (controller != null && equipment != null && player != null) {
player.damage(1, source.getSourceId(), game, false, true);
int amount = controller.rollDice(game, 6);
if (amount >= 5) {
new UntapSourceEffect().apply(game, source);
}
return true;
}
return false;
}
}

View file

@ -0,0 +1,132 @@
/*
* 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.m;
import java.util.List;
import java.util.UUID;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.common.ManaEffect;
import mage.abilities.mana.ActivatedManaAbilityImpl;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.choices.ChoiceColor;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author spjspj
*/
public class MadScienceFairProject extends CardImpl {
public MadScienceFairProject(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
// {tap}: Roll a six-sided die. On a 3 or lower, target player adds {C} to his or her mana pool. Otherwise, that player adds one mana of any color he or she chooses to his or her mana pool.
this.addAbility(new MadScienceFairProjectManaAbility());
}
public MadScienceFairProject(final MadScienceFairProject card) {
super(card);
}
@Override
public MadScienceFairProject copy() {
return new MadScienceFairProject(this);
}
}
class MadScienceFairProjectManaAbility extends ActivatedManaAbilityImpl {
public MadScienceFairProjectManaAbility() {
super(Zone.BATTLEFIELD, new MadScienceFairManaEffect(), new TapSourceCost());
}
public MadScienceFairProjectManaAbility(final MadScienceFairProjectManaAbility ability) {
super(ability);
}
@Override
public MadScienceFairProjectManaAbility copy() {
return new MadScienceFairProjectManaAbility(this);
}
}
class MadScienceFairManaEffect extends ManaEffect {
public MadScienceFairManaEffect() {
super();
this.staticText = "Roll a six-sided die. On a 3 or lower, target player adds {C} to his or her mana pool. Otherwise, that player adds one mana of any color he or she chooses to his or her mana pool";
}
public MadScienceFairManaEffect(final MadScienceFairManaEffect effect) {
super(effect);
}
@Override
public MadScienceFairManaEffect copy() {
return new MadScienceFairManaEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent permanent = game.getPermanent(source.getSourceId());
if (controller != null && permanent != null) {
int amount = controller.rollDice(game, 6);
if (amount <= 3) {
controller.getManaPool().addMana(Mana.ColorlessMana(1), game, source);
} else {
ChoiceColor choice = new ChoiceColor();
controller.choose(Outcome.PutManaInPool, choice, game);
if (choice.getColor() == null) {
return false;
}
Mana chosen = choice.getMana(1);
if (chosen != null) {
checkToFirePossibleEvents(chosen, game, source);
controller.getManaPool().addMana(chosen, game, source);
return true;
}
}
return true;
}
return false;
}
@Override
public Mana getMana(Game game, Ability source) {
return null;
}
}

View file

@ -0,0 +1,101 @@
/*
* 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.p;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
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.Outcome;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author spjspj
*/
public class Painiac extends CardImpl {
public Painiac(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}");
this.subtype.add(SubType.BRAINIAC);
this.power = new MageInt(0);
this.toughness = new MageInt(3);
// At the beginning of your upkeep, roll a six-sided die. Painiac gets +X/+0 until end of turn, where X is the result.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new PainiacEffect(), TargetController.YOU, false));
}
public Painiac(final Painiac card) {
super(card);
}
@Override
public Painiac copy() {
return new Painiac(this);
}
}
class PainiacEffect extends OneShotEffect {
public PainiacEffect() {
super(Outcome.Benefit);
this.staticText = "Roll a six-sided die. {this} gets +X/+0 until end of turn, where X is the result";
}
public PainiacEffect(final PainiacEffect effect) {
super(effect);
}
@Override
public PainiacEffect copy() {
return new PainiacEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent permanent = game.getPermanent(source.getSourceId());
if (controller != null && permanent != null) {
int amount = controller.rollDice(game, 6);
game.addEffect(new BoostSourceEffect(amount, 0, Duration.EndOfTurn), source);
return true;
}
return false;
}
}

View file

@ -70,7 +70,7 @@ public class RootSliver extends CardImpl {
// Root Sliver can't be countered.
this.addAbility(new SimpleStaticAbility(Zone.STACK, new CantBeCounteredSourceEffect()));
// Sliver spells can't be countered by spells or abilities.
this.addAbility(new SimpleStaticAbility(Zone.STACK, new RootSliverEffect()));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new RootSliverEffect()));
}
@ -88,7 +88,7 @@ class RootSliverEffect extends ContinuousRuleModifyingEffectImpl {
public RootSliverEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit);
staticText = "Sliver spells can't be countered by spells or abilities.";
staticText = "Sliver spells can't be countered by spells or abilities";
}
public RootSliverEffect(final RootSliverEffect effect) {

View file

@ -129,7 +129,8 @@ class ScytheSpecterEffect extends OneShotEffect {
}
for (UUID playerId : game.getOpponents(controller.getId())) {//lose life equal to CMC
if (cardDiscarded.get(playerId).getConvertedManaCost() == highestCMC) {
Card card = cardDiscarded.get(playerId);
if ((card != null) && (card.getConvertedManaCost() == highestCMC)) {
Player opponent = game.getPlayer(playerId);
if (opponent != null
&& discardedCheck.get(playerId) == 1) {//check that card was discarded

View file

@ -0,0 +1,120 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.s;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author spjspj
*/
public class SnickeringSquirrel extends CardImpl {
public SnickeringSquirrel(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}");
this.subtype.add("Squirrel");
this.subtype.add("Advisor");
this.power = new MageInt(1);
this.toughness = new MageInt(1);
// You may tap Snickering Squirrel to increase the result of a die any player rolled by 1.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SnickeringSquirrelEffect()));
}
public SnickeringSquirrel(final SnickeringSquirrel card) {
super(card);
}
@Override
public SnickeringSquirrel copy() {
return new SnickeringSquirrel(this);
}
}
class SnickeringSquirrelEffect extends ReplacementEffectImpl {
SnickeringSquirrelEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit);
staticText = "You may tap {this} to increase the result of a die any player rolled by 1";
}
SnickeringSquirrelEffect(final SnickeringSquirrelEffect effect) {
super(effect);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null && permanent.canTap() && !permanent.isTapped()) {
if (controller.chooseUse(Outcome.AIDontUseIt, "Do you want to tap this to increase the result of a die any player rolled by 1?", null, "Yes", "No", source, game)) {
permanent.tap(game);
event.setAmount(event.getAmount() + 1);
}
}
}
return false;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ROLL_DICE;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return true;
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public SnickeringSquirrelEffect copy() {
return new SnickeringSquirrelEffect(this);
}
}

View file

@ -0,0 +1,102 @@
/*
* 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.s;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
/**
*
* @author spjspj
*/
public class SquirrelPoweredScheme extends CardImpl {
public SquirrelPoweredScheme(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}");
// Increase the result of each die you roll by 2.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SquirrelPoweredSchemeEffect()));
}
public SquirrelPoweredScheme(final SquirrelPoweredScheme card) {
super(card);
}
@Override
public SquirrelPoweredScheme copy() {
return new SquirrelPoweredScheme(this);
}
}
class SquirrelPoweredSchemeEffect extends ReplacementEffectImpl {
SquirrelPoweredSchemeEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit);
staticText = "Increase the result of each die you roll by 2";
}
SquirrelPoweredSchemeEffect(final SquirrelPoweredSchemeEffect effect) {
super(effect);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
event.setAmount(event.getAmount() + 2);
return false;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ROLL_DICE;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return source.getControllerId().equals(event.getPlayerId());
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public SquirrelPoweredSchemeEffect copy() {
return new SquirrelPoweredSchemeEffect(this);
}
}

View file

@ -0,0 +1,151 @@
/*
* 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.s;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.RollDiceEffect;
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.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author spjspj
*/
public class SteelSquirrel extends CardImpl {
public SteelSquirrel(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}");
this.subtype.add(SubType.SQUIRREL);
this.power = new MageInt(1);
this.toughness = new MageInt(1);
// Whenever you roll a 5 or higher on a die, Steel Squirrel gets +X/+X until end of turn, where X is the result.
this.addAbility(new SteelSquirrelTriggeredAbility());
// 6: Roll a six-sided die.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RollDiceEffect(null, 6), new GenericManaCost(6));
this.addAbility(ability);
}
public SteelSquirrel(final SteelSquirrel card) {
super(card);
}
@Override
public SteelSquirrel copy() {
return new SteelSquirrel(this);
}
}
class SteelSquirrelTriggeredAbility extends TriggeredAbilityImpl {
public SteelSquirrelTriggeredAbility() {
super(Zone.BATTLEFIELD, new SteelSquirrelEffect());
}
public SteelSquirrelTriggeredAbility(final SteelSquirrelTriggeredAbility ability) {
super(ability);
}
@Override
public SteelSquirrelTriggeredAbility copy() {
return new SteelSquirrelTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DICE_ROLLED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (this.getControllerId().equals(event.getPlayerId()) && event.getFlag()) {
if (event.getAmount() >= 5) {
for (Effect effect : this.getEffects()) {
effect.setValue("rolled", event.getAmount());
}
return true;
}
}
return false;
}
@Override
public String getRule() {
return "Whenever you roll a 5 or higher on a die, " + super.getRule();
}
}
class SteelSquirrelEffect extends OneShotEffect {
public SteelSquirrelEffect() {
super(Outcome.Benefit);
this.staticText = "{this} gets +X/+X until end of turn, where X is the result";
}
public SteelSquirrelEffect(final SteelSquirrelEffect effect) {
super(effect);
}
@Override
public SteelSquirrelEffect copy() {
return new SteelSquirrelEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent permanent = game.getPermanent(source.getSourceId());
if (controller != null && permanent != null) {
if (this.getValue("rolled") != null) {
int rolled = (Integer) this.getValue("rolled");
game.addEffect(new BoostSourceEffect(rolled, rolled, Duration.EndOfTurn), source);
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,139 @@
/*
* 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.s;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ChooseExpansionSetEffect;
import mage.abilities.effects.common.continuous.BecomesBlackZombieAdditionEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.ExpansionSet;
import mage.cards.Sets;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author spjspj
*/
public class SummonThePack extends CardImpl {
public SummonThePack(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{7}{B}");
// Open a sealed Magic booster pack, reveal the cards, and put all creature cards revealed this way onto the battlefield under your control. They're Zombies in addition to their other types. (Remove those cards from your deck before beginning a new game)
this.getSpellAbility().addEffect(new SummonThePackEffect());
}
public SummonThePack(final SummonThePack card) {
super(card);
}
@Override
public SummonThePack copy() {
return new SummonThePack(this);
}
}
class SummonThePackEffect extends OneShotEffect {
public SummonThePackEffect() {
super(Outcome.DestroyPermanent);
this.staticText = "Open a sealed Magic booster pack, reveal the cards, and put all creature cards revealed this way onto the battlefield under your control. They're Zombies in addition to their other types";
}
public SummonThePackEffect(final SummonThePackEffect effect) {
super(effect);
}
@Override
public SummonThePackEffect copy() {
return new SummonThePackEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
ChooseExpansionSetEffect effect = new ChooseExpansionSetEffect(Outcome.UnboostCreature);
effect.apply(game, source);
Player controller = game.getPlayer(source.getControllerId());
String setChosen = null;
if (effect.getValue("setchosen") != null) {
setChosen = (String) effect.getValue("setchosen");
} else if (game.getState().getValue(this.getId() + "_set") != null) {
setChosen = (String) game.getState().getValue(this.getId() + "_set");
}
if (setChosen != null && controller != null) {
//ExpansionInfo set = ExpansionRepository.instance.getSetByName(setChosen);
ExpansionSet expansionSet = Sets.findSet(setChosen);
if (expansionSet != null) {
List<Card> boosterPack = expansionSet.create15CardBooster();
List<Card> creatureCards = new ArrayList<>();
if (boosterPack != null) {
StringBuilder message = new StringBuilder(controller.getLogName()).append(" opened: ");
for (Card c : boosterPack) {
message.append(c.getName()).append(" ");
if (c != null && c.isCreature()) {
message.append(" (creature card) ");
ContinuousEffect effect2 = new BecomesBlackZombieAdditionEffect();
effect2.setTargetPointer(new FixedTarget(c.getId()));
game.addEffect(effect2, source);
creatureCards.add(c);
c.setZone(Zone.OUTSIDE, game);
}
}
if (creatureCards.size() > 0) {
Set<Card> ccs = new HashSet<Card>(creatureCards);
game.loadCards(ccs, controller.getId());
controller.moveCards(ccs, Zone.BATTLEFIELD, source, game);
}
game.informPlayers(message.toString());
}
}
}
return false;
}
}

View file

@ -101,10 +101,8 @@ class SurveyorsScopeEffect extends OneShotEffect {
}
}
game.informPlayers(new StringBuilder("Surveyor's Scope: X = ").append(numberOfLands).toString());
if (numberOfLands > 0) {
return new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, numberOfLands, StaticFilters.FILTER_BASIC_LAND_CARD)).apply(game, source);
}
return true;
// 10/17/2013 If no players control at least two more lands than you when the ability resolves, youll still search and shuffle your library.
return new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, numberOfLands, StaticFilters.FILTER_BASIC_LAND_CARD)).apply(game, source);
}
return false;
}

View file

@ -159,13 +159,14 @@ class SwordOfDungeonsAndDragonsEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
int count = 1;
int dice = (int)(Math.random()*20+1);
while (dice == 20) {
int amount = controller.rollDice(game, 20);
while (amount == 20) {
count += 1;
dice = (int)(Math.random()*20+1);
amount = controller.rollDice(game, 20);
}
return new CreateTokenEffect(new DragonTokenGold(), count).apply(game, source);
}
return false;
}
}
}

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.t;
import java.util.UUID;
import mage.MageInt;
import mage.constants.SubType;
import mage.abilities.keyword.ProwessAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
/**
*
* @author TheElk801
*/
public class TargetMinotaur extends CardImpl {
public TargetMinotaur(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}");
this.subtype.add(SubType.MINOTAUR);
this.subtype.add(SubType.WARRIOR);
this.power = new MageInt(2);
this.toughness = new MageInt(1);
// Prowess
this.addAbility(new ProwessAbility());
}
public TargetMinotaur(final TargetMinotaur card) {
super(card);
}
@Override
public TargetMinotaur copy() {
return new TargetMinotaur(this);
}
}

View file

@ -0,0 +1,134 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.t;
import java.util.Deque;
import java.util.LinkedList;
import java.util.UUID;
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.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPermanent;
/**
*
* @author spjspj
*/
public class TimeOut extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent("nonland permanent");
static {
filter.add(Predicates.not(new CardTypePredicate(CardType.LAND)));
}
public TimeOut(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{U}");
// Roll a six-sided die. Put target nonland permanent into its owner's library just beneath the top X cards of that library, where X is the result.
this.getSpellAbility().addEffect(new TimeOutEffect());
this.getSpellAbility().addTarget(new TargetPermanent(filter));
}
public TimeOut(final TimeOut card) {
super(card);
}
@Override
public TimeOut copy() {
return new TimeOut(this);
}
}
class TimeOutEffect extends OneShotEffect {
public TimeOutEffect() {
super(Outcome.Benefit);
this.staticText = "Roll a six-sided die. Put target nonland permanent into its owner's library just beneath the top X cards of that library, where X is the result";
}
public TimeOutEffect(final TimeOutEffect effect) {
super(effect);
}
@Override
public TimeOutEffect copy() {
return new TimeOutEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source));
if (permanent != null) {
Player owner = game.getPlayer(permanent.getOwnerId());
if (owner != null) {
int amount = controller.rollDice(game, 6);
Cards cards = new CardsImpl();
Deque<UUID> cardIds = new LinkedList<>();
for (int i = 0; i < amount; i++) {
Card card = owner.getLibrary().removeFromTop(game);
cards.add(card);
cardIds.push(card.getId());
}
// return cards back to library
game.informPlayers(new StringBuilder(controller.getLogName())
.append(" puts ").append(permanent.getName())
.append(" beneath the top ").append(amount)
.append(" cards of ").append(owner.getLogName()).append("'s library").toString());
permanent.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
while (!cardIds.isEmpty()) {
UUID cardId = cardIds.poll();
Card card = cards.get(cardId, game);
if (card != null) {
card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
}
}
return true;
}
}
}
return false;
}
}

View file

@ -0,0 +1,120 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.w;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.common.RollDiceEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.abilities.keyword.ReachAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.events.GameEvent;
/**
*
* @author spjspj
*/
public class WillingTestSubject extends CardImpl {
public WillingTestSubject(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
this.subtype.add(SubType.SPIDER);
this.subtype.add(SubType.MONKEY);
this.subtype.add(SubType.SCIENTIST);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// Reach
this.addAbility(ReachAbility.getInstance());
// Whenever you roll a 4 or higher on a die, put a +1/+1 counter on Willing Test Subject.
this.addAbility(new WillingTestSubjectTriggeredAbility());
// 6: Roll a six-sided die.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RollDiceEffect(null, 6), new GenericManaCost(6));
this.addAbility(ability);
}
public WillingTestSubject(final WillingTestSubject card) {
super(card);
}
@Override
public WillingTestSubject copy() {
return new WillingTestSubject(this);
}
}
class WillingTestSubjectTriggeredAbility extends TriggeredAbilityImpl {
public WillingTestSubjectTriggeredAbility() {
super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()));
}
public WillingTestSubjectTriggeredAbility(final WillingTestSubjectTriggeredAbility ability) {
super(ability);
}
@Override
public WillingTestSubjectTriggeredAbility copy() {
return new WillingTestSubjectTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DICE_ROLLED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (this.getControllerId().equals(event.getPlayerId()) && event.getFlag()) {
if (event.getAmount() >= 4) {
return true;
}
}
return false;
}
@Override
public String getRule() {
return "Whenever you roll a 4 or higher on a die, put a +1/+1 counter on {this}";
}
}

View file

@ -27,7 +27,9 @@
*/
package mage.sets;
import mage.cards.CardGraphicInfo;
import mage.cards.ExpansionSet;
import mage.cards.FrameStyle;
import mage.constants.Rarity;
import mage.constants.SetType;
@ -45,6 +47,37 @@ public class Unstable extends ExpansionSet {
private Unstable() {
super("Unstable", "UST", ExpansionSet.buildDate(2017, 12, 8), SetType.JOKESET);
cards.add(new SetCardInfo("Sword of Dungeons and Dragons", 1, Rarity.MYTHIC, mage.cards.s.SwordOfDungeonsAndDragons.class));
cards.add(new SetCardInfo("Amateur Auteur", 3, Rarity.COMMON, mage.cards.a.AmateurAuteur.class));
cards.add(new SetCardInfo("As Luck Would Have It", 102, Rarity.RARE, mage.cards.a.AsLuckWouldHaveIt.class));
cards.add(new SetCardInfo("Box of Free-Range Goblins", 77, Rarity.COMMON, mage.cards.b.BoxOfFreerangeGoblins.class));
cards.add(new SetCardInfo("Buzzing Whack-a-Doodle", 141, Rarity.UNCOMMON, mage.cards.b.BuzzingWhackADoodle.class));
cards.add(new SetCardInfo("Chittering Doom", 104, Rarity.UNCOMMON, mage.cards.c.ChitteringDoom.class));
cards.add(new SetCardInfo("Crow Storm", 31, Rarity.UNCOMMON, mage.cards.c.CrowStorm.class));
cards.add(new SetCardInfo("Curious Killbot", 145, Rarity.COMMON, mage.cards.c.CuriousKillbot.class));
cards.add(new SetCardInfo("Earl of Squirrel", 108, Rarity.RARE, mage.cards.e.EarlOfSquirrel.class));
cards.add(new SetCardInfo("Forest", 216, Rarity.LAND, mage.cards.basiclands.Forest.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false)));
cards.add(new SetCardInfo("GO TO JAIL", 8, Rarity.COMMON, mage.cards.g.GOTOJAIL.class));
cards.add(new SetCardInfo("Garbage Elemental", 82, Rarity.UNCOMMON, mage.cards.g.GarbageElemental.class));
cards.add(new SetCardInfo("Ground Pounder", 110, Rarity.COMMON, mage.cards.g.GroundPounder.class));
cards.add(new SetCardInfo("Hammer Helper", 85, Rarity.COMMON, mage.cards.h.HammerHelper.class));
cards.add(new SetCardInfo("Hydradoodle", 112, Rarity.RARE, mage.cards.h.Hydradoodle.class));
cards.add(new SetCardInfo("Inhumaniac", 59, Rarity.UNCOMMON, mage.cards.i.Inhumaniac.class));
cards.add(new SetCardInfo("Island", 213, Rarity.LAND, mage.cards.basiclands.Island.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false)));
cards.add(new SetCardInfo("Krark's Other Thumb", 151, Rarity.UNCOMMON, mage.cards.k.KrarksOtherThumb.class));
cards.add(new SetCardInfo("Lobe Lobber", 153, Rarity.UNCOMMON, mage.cards.l.LobeLobber.class));
cards.add(new SetCardInfo("Mad Science Fair Project", 154, Rarity.COMMON, mage.cards.m.MadScienceFairProject.class));
cards.add(new SetCardInfo("Mountain", 215, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false)));
cards.add(new SetCardInfo("Painiac", 91, Rarity.COMMON, mage.cards.p.Painiac.class));
cards.add(new SetCardInfo("Plains", 212, Rarity.LAND, mage.cards.basiclands.Plains.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false)));
cards.add(new SetCardInfo("Snickering Squirrel", 68, Rarity.COMMON, mage.cards.s.SnickeringSquirrel.class));
cards.add(new SetCardInfo("Squirrel-Powered Scheme", 70, Rarity.UNCOMMON, mage.cards.s.SquirrelPoweredScheme.class));
cards.add(new SetCardInfo("Steel Squirrel", 162, Rarity.UNCOMMON, mage.cards.s.SteelSquirrel.class));
cards.add(new SetCardInfo("Summon the Pack", 74, Rarity.MYTHIC, mage.cards.s.SummonThePack.class));
cards.add(new SetCardInfo("Swamp", 214, Rarity.LAND, mage.cards.basiclands.Swamp.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false)));
cards.add(new SetCardInfo("Sword of Dungeons & Dragons", 1, Rarity.MYTHIC, mage.cards.s.SwordOfDungeonsAndDragons.class));
cards.add(new SetCardInfo("Target Minotaur", 98, Rarity.COMMON, mage.cards.t.TargetMinotaur.class));
cards.add(new SetCardInfo("Time Out", 48, Rarity.COMMON, mage.cards.t.TimeOut.class));
cards.add(new SetCardInfo("Willing Test Subject", 126, Rarity.COMMON, mage.cards.w.WillingTestSubject.class));
}
}

View file

@ -69,7 +69,7 @@ public class ZendikarVsEldrazi extends ExpansionSet {
cards.add(new SetCardInfo("Forest", 38, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Forest", 39, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Forest", 40, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Forked Bolt", 60, Rarity.UNCOMMON, mage.cards.f.ForkedBolt.class));
cards.add(new SetCardInfo("Forked Bolt", 60, Rarity.COMMON, mage.cards.f.ForkedBolt.class));
cards.add(new SetCardInfo("Frontier Guide", 12, Rarity.UNCOMMON, mage.cards.f.FrontierGuide.class));
cards.add(new SetCardInfo("Graypelt Hunter", 13, Rarity.COMMON, mage.cards.g.GraypeltHunter.class));
cards.add(new SetCardInfo("Graypelt Refuge", 32, Rarity.UNCOMMON, mage.cards.g.GraypeltRefuge.class));

View file

@ -1,107 +1,107 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package org.mage.test.cards.modal;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class OneOrBothTest extends CardTestPlayerBase {
@Test
public void testSubtleStrikeFirstMode() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
// Choose one or both
// Target creature gets -1/-1 until end of turn.
// Put a +1/+1 counter on target creature.
addCard(Zone.HAND, playerA, "Subtle Strike"); // Instant {1}{B}
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Subtle Strike", "Pillarfield Ox");
setModeChoice(playerA, "1");
setModeChoice(playerA, null);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPowerToughness(playerA, "Silvercoat Lion", 2, 2);
assertPowerToughness(playerB, "Pillarfield Ox", 1, 3);
}
@Test
public void testSubtleStrikeSecondMode() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
// Choose one or both
// Target creature gets -1/-1 until end of turn.
// Put a +1/+1 counter on target creature.
addCard(Zone.HAND, playerA, "Subtle Strike"); // Instant {1}{B}
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Subtle Strike", "Pillarfield Ox");
setModeChoice(playerA, "2");
setModeChoice(playerA, null);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPowerToughness(playerA, "Silvercoat Lion", 2, 2);
assertPowerToughness(playerB, "Pillarfield Ox", 3, 5);
}
@Test
public void testSubtleStrikeBothModes() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
// Choose one or both
// Target creature gets -1/-1 until end of turn.
// Put a +1/+1 counter on target creature.
addCard(Zone.HAND, playerA, "Subtle Strike"); // Instant {1}{B}
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Subtle Strike", "Pillarfield Ox");
addTarget(playerA, "Silvercoat Lion");
setModeChoice(playerA, "1");
setModeChoice(playerA, "2");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPowerToughness(playerA, "Silvercoat Lion", 3, 3);
assertPowerToughness(playerB, "Pillarfield Ox", 1, 3);
}
}
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package org.mage.test.cards.modal;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class OneOrBothTest extends CardTestPlayerBase {
@Test
public void testSubtleStrikeFirstMode() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
// Choose one or both
// Target creature gets -1/-1 until end of turn.
// Put a +1/+1 counter on target creature.
addCard(Zone.HAND, playerA, "Subtle Strike"); // Instant {1}{B}
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Subtle Strike", "Pillarfield Ox");
setModeChoice(playerA, "1");
setModeChoice(playerA, null);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPowerToughness(playerA, "Silvercoat Lion", 2, 2);
assertPowerToughness(playerB, "Pillarfield Ox", 1, 3);
}
@Test
public void testSubtleStrikeSecondMode() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
// Choose one or both
// Target creature gets -1/-1 until end of turn.
// Put a +1/+1 counter on target creature.
addCard(Zone.HAND, playerA, "Subtle Strike"); // Instant {1}{B}
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Subtle Strike", "Pillarfield Ox");
setModeChoice(playerA, "2");
setModeChoice(playerA, null);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPowerToughness(playerA, "Silvercoat Lion", 2, 2);
assertPowerToughness(playerB, "Pillarfield Ox", 3, 5);
}
@Test
public void testSubtleStrikeBothModes() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
// Choose one or both
// Target creature gets -1/-1 until end of turn.
// Put a +1/+1 counter on target creature.
addCard(Zone.HAND, playerA, "Subtle Strike"); // Instant {1}{B}
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Subtle Strike", "Pillarfield Ox");
addTarget(playerA, "Silvercoat Lion");
setModeChoice(playerA, "1");
setModeChoice(playerA, "2");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPowerToughness(playerB, "Pillarfield Ox", 1, 3);
assertPowerToughness(playerA, "Silvercoat Lion", 3, 3);
}
}

View file

@ -12,7 +12,7 @@ public class TxtDeckImporterTest {
@Test
public void testImportWithBlankLineAboveSideboard() {
TxtDeckImporter importer = new TxtDeckImporter();
TxtDeckImporter importer = new TxtDeckImporter(false);
CardInfo card;
DeckCardLists deck = new DeckCardLists();

View file

@ -1825,6 +1825,17 @@ public class TestPlayer implements Player {
return computerPlayer.flipCoin(game, appliedEffects);
}
@Override
public int rollDice(Game game, int numSides) {
return computerPlayer.rollDice(game, numSides);
}
@Override
public int rollDice(Game game, ArrayList<UUID> appliedEffects, int numSides) {
return computerPlayer.rollDice(game, appliedEffects, numSides);
}
@Override
public List<Permanent> getAvailableAttackers(Game game) {
return computerPlayer.getAvailableAttackers(game);

View file

@ -646,6 +646,17 @@ public class PlayerStub implements Player {
return false;
}
@Override
public int rollDice(Game game, int numSides) {
return 1;
}
@Override
public int rollDice(Game game, ArrayList<UUID> appliedEffects, int numSides) {
return 1;
}
@Override
public void discard(int amount, Ability source, Game game) {

View file

@ -22,6 +22,35 @@ public class VerifyCardDataTest {
// right now this is very noisy, and not useful enough to make any assertions on
private static final boolean CHECK_SOURCE_TOKENS = false;
private static final HashMap<String, Set<String>> skipCheckLists = new HashMap<>();
private static void skipListCreate(String listName){ skipCheckLists.put(listName, new LinkedHashSet<>()); }
private static void skipListAddName(String listName, String name){ skipCheckLists.get(listName).add(name); }
private static boolean skipListHaveName(String listName, String name){ return skipCheckLists.get(listName).contains(name); }
static {
// skip lists for checks (example: ustable cards with same name may have different stats)
// power-toughness
skipListCreate("PT");
skipListAddName("PT", "Garbage Elemental"); // UST
// color
skipListCreate("COLOR");
//skipListAddName("COLOR", "Ulrich, Uncontested Alpha"); // gatherer is missing the color indicator on one card and json has wrong data (16.12.2017: not actual)
// cost
skipListCreate("COST");
// supertype
skipListCreate("SUPERTYPE");
// type
skipListCreate("TYPE");
// subtype
skipListCreate("SUBTYPE");
}
public static List<Card> allCards() {
Collection<ExpansionSet> sets = Sets.getInstance().values();
List<Card> cards = new ArrayList<>();
@ -38,7 +67,7 @@ public class VerifyCardDataTest {
}
private void warn(Card card, String message) {
System.out.println("Warning: " + message + " for " + card.getName());
System.out.println("Warning: " + message + " for " + card.getName() + " (" + card.getExpansionSetCode() + ")");
}
private void fail(Card card, String category, String message) {
@ -129,10 +158,8 @@ public class VerifyCardDataTest {
}
private void checkColors(Card card, JsonCard ref) {
// gatherer is missing the color indicator on one card:
if ("Ulrich, Uncontested Alpha".equals(ref.name)) {
return;
}
if (skipListHaveName("COLOR", card.getName())){ return; }
Collection<String> expected = ref.colors;
ObjectColor color = card.getColor(null);
if (expected == null) {
@ -149,7 +176,11 @@ public class VerifyCardDataTest {
}
private void checkSubtypes(Card card, JsonCard ref) {
if (skipListHaveName("SUBTYPE", card.getName())){ return; }
Collection<String> expected = ref.subtypes;
// fix names (e.g. Urzas to Urza's)
if (expected != null && expected.contains("Urzas")) {
expected = new ArrayList<>(expected);
for (ListIterator<String> it = ((List<String>) expected).listIterator(); it.hasNext();) {
@ -158,12 +189,15 @@ public class VerifyCardDataTest {
}
}
}
if (!eqSet(card.getSubtype(null).stream().map(p -> p.toString()).collect(Collectors.toSet()), expected)) {
fail(card, "subtypes", card.getSubtype(null) + " != " + expected);
}
}
private void checkSupertypes(Card card, JsonCard ref) {
if (skipListHaveName("SUPERTYPE", card.getName())){ return; }
Collection<String> expected = ref.supertypes;
if (!eqSet(card.getSuperType().stream().map(s -> s.toString()).collect(Collectors.toList()), expected)) {
fail(card, "supertypes", card.getSuperType() + " != " + expected);
@ -171,6 +205,8 @@ public class VerifyCardDataTest {
}
private void checkTypes(Card card, JsonCard ref) {
if (skipListHaveName("TYPE", card.getName())){ return; }
Collection<String> expected = ref.types;
List<String> type = new ArrayList<>();
for (CardType cardType : card.getCardType()) {
@ -189,6 +225,8 @@ public class VerifyCardDataTest {
}
private void checkPT(Card card, JsonCard ref) {
if (skipListHaveName("PT", card.getName())){ return; }
if (!eqPT(card.getPower().toString(), ref.power) || !eqPT(card.getToughness().toString(), ref.toughness)) {
String pt = card.getPower() + "/" + card.getToughness();
String expected = ref.power + '/' + ref.toughness;
@ -205,6 +243,8 @@ public class VerifyCardDataTest {
}
private void checkCost(Card card, JsonCard ref) {
if (skipListHaveName("COST", card.getName())){ return; }
String expected = ref.manaCost;
String cost = join(card.getManaCost().getSymbols());
if (cost != null && cost.isEmpty()) {

View file

@ -0,0 +1,102 @@
/*
* 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.abilities.effects.common;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.repository.ExpansionRepository;
import mage.choices.Choice;
import mage.choices.ChoiceImpl;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.util.CardUtil;
/**
* @author LevelX2 (spjspj)
*/
public class ChooseExpansionSetEffect extends OneShotEffect {
public ChooseExpansionSetEffect(Outcome outcome) {
super(outcome);
staticText = "choose an expansion set";
}
public ChooseExpansionSetEffect(final ChooseExpansionSetEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject mageObject = game.getPermanentEntering(source.getSourceId());
if (mageObject == null) {
mageObject = game.getObject(source.getSourceId());
}
if (controller != null) {
Choice setChoice = new ChoiceImpl(true);
setChoice.setMessage("Choose expansion set");
List<String> setCodes = ExpansionRepository.instance.getSetCodes();
Set<String> sets = new HashSet<String>(setCodes);
setChoice.setChoices(sets);
while (!controller.choose(outcome, setChoice, game)) {
if (!controller.canRespond()) {
return false;
}
}
if (setChoice.getChoice() == null) {
return false;
}
if (!game.isSimulation()) {
game.informPlayers(controller.getLogName() + " has chosen set " + setChoice.getChoice());
}
game.getState().setValue(mageObject.getId() + "_set", setChoice.getChoice());
this.setValue("setchosen", setChoice.getChoice());
if (mageObject instanceof Permanent) {
((Permanent) mageObject).addInfo("chosen set", CardUtil.addToolTipMarkTags("Chosen set: " + setChoice.getChoice()), game);
}
}
return false;
}
@Override
public ChooseExpansionSetEffect copy() {
return new ChooseExpansionSetEffect(this);
}
}

View file

@ -0,0 +1,96 @@
/*
* 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.abilities.effects.common;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.Effects;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.game.Game;
import mage.players.Player;
/**
*
* @author spjspj
*/
public class RollDiceEffect extends OneShotEffect {
protected Effects executingEffects = new Effects();
protected int numSides;
public RollDiceEffect(Effect effect, int numSides) {
this(effect, Outcome.Neutral, numSides);
}
public RollDiceEffect(Effect effect, Outcome outcome, int numSides) {
super(outcome);
addEffect(effect);
this.numSides = numSides;
}
public RollDiceEffect(final RollDiceEffect effect) {
super(effect);
this.executingEffects = effect.executingEffects.copy();
this.numSides = effect.numSides;
}
public void addEffect(Effect effect) {
if (effect != null) {
executingEffects.add(effect);
}
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject mageObject = game.getObject(source.getSourceId());
if (controller != null && mageObject != null) {
controller.rollDice(game, numSides);
return true;
}
return false;
}
@Override
public String getText(Mode mode) {
if (!staticText.isEmpty()) {
return staticText;
}
StringBuilder sb = new StringBuilder("Roll a " + numSides + " sided dice");
return sb.toString();
}
@Override
public RollDiceEffect copy() {
return new RollDiceEffect(this);
}
}

View file

@ -0,0 +1,66 @@
/*
* 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.abilities.keyword;
import mage.constants.Zone;
import mage.abilities.MageSingleton;
import mage.abilities.StaticAbility;
import java.io.ObjectStreamException;
/**
*
* @author BetaSteward_at_googlemail.com (spjspj)
*/
public class SquirrellinkAbility extends StaticAbility implements MageSingleton {
private static final SquirrellinkAbility instance = new SquirrellinkAbility();
private Object readResolve() throws ObjectStreamException {
return instance;
}
public static SquirrellinkAbility getInstance() {
return instance;
}
private SquirrellinkAbility() {
super(Zone.ALL, null);
}
@Override
public String getRule() {
return "Squirrellink <i>(Damage dealt by this creature also causes you to create that many 1/1/ green Squirrel creature tokens.)</i>";
}
@Override
public SquirrellinkAbility copy() {
return instance;
}
}

View file

@ -594,6 +594,9 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
} else if (game.getPhase() == null) {
// E.g. Commander of commander game
removed = true;
} else {
// Unstable - Summon the Pack
removed = true;
}
break;
case BATTLEFIELD: // for sacrificing permanents or putting to library

View file

@ -32,6 +32,7 @@ import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.constants.Rarity;
import mage.constants.SetType;
import mage.util.CardUtil;
import mage.util.RandomUtil;
import java.io.Serializable;
@ -82,6 +83,10 @@ public abstract class ExpansionSet implements Serializable {
return this.cardNumber;
}
public int getCardNumberAsInt(){
return CardUtil.parseCardNumberAsInt(this.cardNumber);
}
public Rarity getRarity() {
return this.rarity;
}
@ -390,7 +395,7 @@ public abstract class ExpansionSet implements Serializable {
savedCardsInfos = CardRepository.instance.findCards(criteria);
// Workaround after card number is numeric
if (maxCardNumberInBooster != Integer.MAX_VALUE) {
savedCardsInfos.removeIf(next -> Integer.valueOf(next.getCardNumber()) > maxCardNumberInBooster && rarity != Rarity.LAND);
savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster && rarity != Rarity.LAND);
}
savedCards.put(rarity, savedCardsInfos);
@ -431,4 +436,6 @@ public abstract class ExpansionSet implements Serializable {
savedCards.clear();
}
public int getMaxCardNumberInBooster() { return maxCardNumberInBooster; }
}

View file

@ -45,7 +45,14 @@ public abstract class DeckImporter {
protected StringBuilder sbMessage = new StringBuilder(); //TODO we should stop using this not garbage collectable StringBuilder. It just bloats
protected int lineCount;
public DeckCardLists importDeck(String file) {
/**
*
* @param file file to import
* @param errorMessages you can setup output messages to showup to user (set null for fatal exception on messages.count > 0)
* @return decks list
*/
public DeckCardLists importDeck(String file, StringBuilder errorMessages) {
File f = new File(file);
DeckCardLists deckList = new DeckCardLists();
if (!f.exists()) {
@ -62,8 +69,15 @@ public abstract class DeckImporter {
lineCount++;
readLine(line, deckList);
}
if (sbMessage.length() > 0) {
logger.fatal(sbMessage);
if(errorMessages != null) {
// normal output for user
errorMessages.append(sbMessage);
}else{
// fatal error
logger.fatal(sbMessage);
}
}
} catch (Exception ex) {
logger.fatal(null, ex);
@ -74,6 +88,10 @@ public abstract class DeckImporter {
return deckList;
}
public DeckCardLists importDeck(String file) {
return importDeck(file, null);
}
public String getErrors(){
return sbMessage.toString();
}

View file

@ -29,19 +29,48 @@ package mage.cards.decks.importer;
import mage.cards.decks.DeckCardLists;
import java.io.File;
import java.util.Scanner;
/**
*
* @author North
*/
public final class DeckImporterUtil {
public static final String[] SIDEBOARD_MARKS = new String[]{"//sideboard", "sb: "};
public static boolean haveSideboardSection(String file){
// search for sideboard section:
// or //sideboard
// or SB: 1 card name -- special deckstats.net
File f = new File(file);
try (Scanner scanner = new Scanner(f)) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim().toLowerCase();
for(String mark: SIDEBOARD_MARKS){
if (line.startsWith(mark)){
return true;
}
}
}
} catch (Exception e) {
// ignore error, deckimporter will process it
}
// not found
return false;
}
public static DeckImporter getDeckImporter(String file) {
if (file.toLowerCase().endsWith("dec")) {
return new DecDeckImporter();
} else if (file.toLowerCase().endsWith("mwdeck")) {
return new MWSDeckImporter();
} else if (file.toLowerCase().endsWith("txt")) {
return new TxtDeckImporter();
return new TxtDeckImporter(haveSideboardSection(file));
} else if (file.toLowerCase().endsWith("dck")) {
return new DckDeckImporter();
} else if (file.toLowerCase().endsWith("dek")) {
@ -51,12 +80,16 @@ public final class DeckImporterUtil {
}
}
public static DeckCardLists importDeck(String file) {
public static DeckCardLists importDeck(String file, StringBuilder errorMessages) {
DeckImporter deckImporter = getDeckImporter(file);
if (deckImporter != null) {
return deckImporter.importDeck(file);
return deckImporter.importDeck(file, errorMessages);
} else {
return new DeckCardLists();
}
}
public static DeckCardLists importDeck(String file) {
return importDeck(file, null);
}
}

View file

@ -47,25 +47,60 @@ public class TxtDeckImporter extends DeckImporter {
public static final Set<String> IGNORE_NAMES = new HashSet<>(Arrays.asList(SET_VALUES));
private boolean sideboard = false;
private boolean switchSideboardByEmptyLine = true; // all cards after first empty line will be sideboard (like mtgo format)
private int nonEmptyLinesTotal = 0;
public TxtDeckImporter(boolean haveSideboardSection){
if(haveSideboardSection){
switchSideboardByEmptyLine = false;
}
}
@Override
protected void readLine(String line, DeckCardLists deckList) {
if (line.toLowerCase().contains("sideboard")) {
sideboard = true;
return;
}
if (line.startsWith("//")) {
line = line.trim();
// process comment:
// skip or force to sideboard
String commentString = line.toLowerCase();
if (commentString.startsWith("//")){
// use start, not contains (card names may contain commands like "Legerdemain")
if (commentString.startsWith("//sideboard")) {
sideboard = true;
}
// skip comment line
return;
}
// Start the sideboard on empty line that follows
// at least 1 non-empty line
if (line.isEmpty() && nonEmptyLinesTotal > 0) {
sideboard = true;
// remove inner card comments from text line: 2 Blinding Fog #some text (like deckstats format)
int commentDelim = line.indexOf('#');
if(commentDelim >= 0){
line = line.substring(0, commentDelim).trim();
}
// switch sideboard by empty line
if (switchSideboardByEmptyLine && line.isEmpty() && nonEmptyLinesTotal > 0) {
if(!sideboard){
sideboard = true;
}else{
sbMessage.append("Found empty line at ").append(lineCount).append(", but sideboard already used. Use //sideboard switcher OR one empty line to devide your cards.").append('\n');
}
// skip empty line
return;
} else {
nonEmptyLinesTotal++;
}
nonEmptyLinesTotal++;
// single line sideboard card from deckstats.net
// SB: 3 Carnage Tyrant
boolean singleLineSideBoard = false;
if (line.startsWith("SB:")){
line = line.replace("SB:", "").trim();
singleLineSideBoard = true;
}
line = line.replace("\t", " "); // changing tabs to blanks as delimiter
@ -87,12 +122,17 @@ public class TxtDeckImporter extends DeckImporter {
}
try {
int num = Integer.parseInt(lineNum.replaceAll("\\D+", ""));
if ((num < 0) || (num > 100)){
sbMessage.append("Invalid number (too small or too big): ").append(lineNum).append(" at line ").append(lineCount).append('\n');
return;
}
CardInfo cardInfo = CardRepository.instance.findPreferedCoreExpansionCard(lineName, true);
if (cardInfo == null) {
sbMessage.append("Could not find card: '").append(lineName).append("' at line ").append(lineCount).append('\n');
} else {
for (int i = 0; i < num; i++) {
if (!sideboard) {
if (!sideboard && !singleLineSideBoard) {
deckList.getCards().add(new DeckCardInfo(cardInfo.getName(), cardInfo.getCardNumber(), cardInfo.getSetCode()));
} else {
deckList.getSideboard().add(new DeckCardInfo(cardInfo.getName(), cardInfo.getCardNumber(), cardInfo.getSetCode()));
@ -103,5 +143,4 @@ public class TxtDeckImporter extends DeckImporter {
sbMessage.append("Invalid number: ").append(lineNum).append(" at line ").append(lineCount).append('\n');
}
}
}

View file

@ -40,6 +40,7 @@ import mage.cards.*;
import mage.cards.mock.MockCard;
import mage.cards.mock.MockSplitCard;
import mage.constants.*;
import mage.util.CardUtil;
import mage.util.SubTypeList;
import org.apache.log4j.Logger;
@ -374,6 +375,10 @@ public class CardInfo {
return cardNumber;
}
public int getCardNumberAsInt() {
return CardUtil.parseCardNumberAsInt(cardNumber);
}
public boolean isSplitCard() {
return splitCard;
}

View file

@ -58,7 +58,7 @@ public enum CardRepository {
// raise this if db structure was changed
private static final long CARD_DB_VERSION = 51;
// raise this if new cards were added to the server
private static final long CARD_CONTENT_VERSION = 95;
private static final long CARD_CONTENT_VERSION = 96;
private Dao<CardInfo, Object> cardDao;
private Set<String> classNames;

View file

@ -72,6 +72,7 @@ public enum SubType {
BITH("Bith", SubTypeSet.CreatureType, true), // Star Wars
BLINKMOTH("Blinkmoth", SubTypeSet.CreatureType),
BOAR("Boar", SubTypeSet.CreatureType),
BRAINIAC("Brainiac", SubTypeSet.CreatureType, true), // Unstable
BRINGER("Bringer", SubTypeSet.CreatureType),
BRUSHWAGG("Brushwagg", SubTypeSet.CreatureType),
// C
@ -177,7 +178,8 @@ public enum SubType {
KALEESH("Kaleesh", SubTypeSet.CreatureType, true), // Star Wars
KAVU("Kavu", SubTypeSet.CreatureType),
KELDOR("KelDor", SubTypeSet.CreatureType, true),
KIRIN("Kirin", SubTypeSet.CreatureType),
KILLBOT("Killbot", SubTypeSet.CreatureType, true), // Unstable
KIRIN("Kirin", SubTypeSet.CreatureType),
KITHKIN("Kithkin", SubTypeSet.CreatureType),
KNIGHT("Knight", SubTypeSet.CreatureType),
KOBOLD("Kobold", SubTypeSet.CreatureType),
@ -268,6 +270,7 @@ public enum SubType {
SAPROLING("Saproling", SubTypeSet.CreatureType),
SATYR("Satyr", SubTypeSet.CreatureType),
SCARECROW("Scarecrow", SubTypeSet.CreatureType),
SCIENTIST("Scientist", SubTypeSet.CreatureType, true), // Unstable
SCION("Scion", SubTypeSet.CreatureType),
SCORPION("Scorpion", SubTypeSet.CreatureType),
SCOUT("Scout", SubTypeSet.CreatureType),

View file

@ -578,6 +578,9 @@ public class Combat implements Serializable, Copyable<Combat> {
* @param game
*/
private void retrieveMustBlockAttackerRequirements(Player attackingPlayer, Game game) {
if (attackingPlayer == null) {
return;
}
if (!game.getContinuousEffects().existRequirementEffects()) {
return;
}

View file

@ -229,6 +229,7 @@ public class GameEvent implements Serializable {
ENCHANT_PLAYER, ENCHANTED_PLAYER,
CAN_TAKE_MULLIGAN,
FLIP_COIN, COIN_FLIPPED, SCRY, FATESEAL,
ROLL_DICE, DICE_ROLLED,
PAID_CUMULATIVE_UPKEEP,
DIDNT_PAY_CUMULATIVE_UPKEEP,
//permanent events

View file

@ -52,6 +52,7 @@ import mage.game.combat.CombatGroup;
import mage.game.command.CommandObject;
import mage.game.events.*;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.token.SquirrelToken;
import mage.game.stack.Spell;
import mage.game.stack.StackObject;
import mage.players.Player;
@ -802,6 +803,11 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
if (dealtDamageByThisTurn == null) {
dealtDamageByThisTurn = new HashSet<>();
}
// Unstable ability - Earl of Squirrel
if (sourceAbilities.containsKey(SquirrellinkAbility.getInstance().getId())) {
Player player = game.getPlayer(sourceControllerId);
new SquirrelToken().putOntoBattlefield(damageDone, game, sourceId, player.getId());
}
dealtDamageByThisTurn.add(new MageObjectReference(source, game));
}
if (source == null) {

View file

@ -0,0 +1,50 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.game.permanent.token;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.MageInt;
import mage.abilities.keyword.FlyingAbility;
/**
*
* @author spjspj
*/
public class StormCrowToken extends Token {
public StormCrowToken() {
super("Storm Crow", "1/2 blue Bird creature token with flying named Storm Crow");
cardType.add(CardType.CREATURE);
color.setBlue(true);
subtype.add(SubType.BIRD);
power = new MageInt(1);
toughness = new MageInt(2);
this.addAbility(FlyingAbility.getInstance());
}
}

View file

@ -417,6 +417,10 @@ public interface Player extends MageItem, Copyable<Player> {
boolean flipCoin(Game game, ArrayList<UUID> appliedEffects);
int rollDice(Game game, int numSides);
int rollDice(Game game, ArrayList<UUID> appliedEffects, int numSides);
@Deprecated
void discard(int amount, Ability source, Game game);

View file

@ -80,6 +80,7 @@ import mage.game.events.ZoneChangeEvent;
import mage.game.match.MatchPlayer;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard;
import mage.game.permanent.token.SquirrelToken;
import mage.game.stack.Spell;
import mage.game.stack.StackAbility;
import mage.game.stack.StackObject;
@ -1841,6 +1842,11 @@ public abstract class PlayerImpl implements Player, Serializable {
Player player = game.getPlayer(sourceControllerId);
player.gainLife(actualDamage, game);
}
// Unstable ability - Earl of Squirrel
if (sourceAbilities.containsKey(SquirrellinkAbility.getInstance().getId())) {
Player player = game.getPlayer(sourceControllerId);
new SquirrelToken().putOntoBattlefield(actualDamage, game, sourceId, player.getId());
}
game.fireEvent(new DamagedPlayerEvent(playerId, sourceId, playerId, actualDamage, combatDamage));
return actualDamage;
}
@ -2332,6 +2338,34 @@ public abstract class PlayerImpl implements Player, Serializable {
return event.getFlag();
}
@Override
public int rollDice(Game game, int numSides) {
return this.rollDice(game, null, numSides);
}
/**
* @param game
* @param appliedEffects
* @return the number that the player rolled
*/
@Override
public int rollDice(Game game, ArrayList<UUID> appliedEffects, int numSides) {
int result = RandomUtil.nextInt(numSides) + 1;
if (!game.isSimulation()) {
game.informPlayers("[Roll a die] " + getLogName() + " rolled a " + result + " on a " + numSides + " sided dice");
}
GameEvent event = new GameEvent(GameEvent.EventType.ROLL_DICE, playerId, null, playerId, result, true);
event.setAppliedEffects(appliedEffects);
event.setAmount(result);
event.setData(numSides + "");
if (!game.replaceEvent(event)) {
GameEvent ge = new GameEvent(GameEvent.EventType.DICE_ROLLED, playerId, null, playerId, event.getAmount(), event.getFlag());
ge.setData(numSides + "");
game.fireEvent(ge);
}
return event.getAmount();
}
@Override
public List<Permanent> getAvailableAttackers(Game game) {
// TODO: get available opponents and their planeswalkers, check for each if permanent can attack one

View file

@ -382,6 +382,25 @@ public final class CardUtil {
return true;
}
/**
* Parse card number as int (support base [123] and alternative numbers [123b]).
*
* @param cardNumber origin card number
* @return int
*/
public static int parseCardNumberAsInt(String cardNumber){
if (cardNumber.isEmpty()){ throw new IllegalArgumentException("Card number is empty.");}
if(Character.isDigit(cardNumber.charAt(cardNumber.length() - 1)))
{
return Integer.parseInt(cardNumber);
}else{
return Integer.parseInt(cardNumber.substring(0, cardNumber.length() - 1));
}
}
/**
* Creates and saves a (card + zoneChangeCounter) specific exileId.
*

View file

@ -32701,4 +32701,66 @@ Tetzimoc, Primal Death|Rivals of Ixalan|86|R|{4}{B}{B}|Legendary Creature - Elde
Ghalta, Primal Hunger|Rivals of Ixalan|130|R|{10}{G}{G}|Legendary Creature - Elder Dinosaur|12|12|Ghalta, Primal Hunger costs {X} less to cast, where X is the total power of creatures you control.$Trample|
Storm the Vault|Rivals of Ixalan|173|R|{2}{U}{R}|Legendary Enchantment|||Whenever one or more creatures you control deal combat damage to a player, create a colorless Treasure artifact token with "{T}, Sacrifice this artifact: Add one mana of any color to your mana pool."$At the beginning of your end step, if you control five or more artifacts, transform Storm the Vault.|
Vault of Catlacan|Rivals of Ixalan|173|R||Legendary Land|||<i>(Transforms from Storm the Vault.)</i>${T}: Add one mana of any color to your mana pool.${T}: Add {U} to your mana pool for each artifact you control.|
The Immortal Sun|Rivals of Ixalan|180|M|{6}|Legendary Artifact|||Players can't activate loyalty abilities of planeswalkers.$At the beginning of your draw step, draw an additional card.$Spells you cast cost {1} less to cast.$Creatures you control get +1/+1.|
The Immortal Sun|Rivals of Ixalan|180|M|{6}|Legendary Artifact|||Players can't activate loyalty abilities of planeswalkers.$At the beginning of your draw step, draw an additional card.$Spells you cast cost {1} less to cast.$Creatures you control get +1/+1.|
Amateur Auteur|Unstable|3|C|{1}{W}|Creature - Human|2|2|Sacrifice Amateur Auteur: Destroy target enchantment.|
Angelic Rocket|Unstable|139|R|{8}|Host Creature - Angel|4|4|Flying$When this creature enters the battlefield, you may destroy target nonland permanent.|
As Luck Would Have It|Unstable|102|R|{G}|Enchantment|||Hexproof$Whenever you roll a die, put a number of luck counters on As Luck Would Have It equal to the result. Then is there are 100 or more luck counters on As Luck Would Have It, you win the game. (Count both rolls if you reroll a die.)|
Baron Von Count|Unstable|127|M|{1}{B}{R}|Legendary Creature - Human Villain|3|3|Baron Von Count enters the battlefield with a doom counter on "5."$Whenever you cast a spell with the indicated numeral in its mana cost, text box, power, or toughness, move the doom counter one numeral to the left.$When the doom counter moves from "1," destroy target player and put that doom counter on "5."|
Big Boa Constrictor|Unstable|51|C|{3}{B}|Host Creature - Snake|1|2|When this creature enters the battlefield, roll a six-sided die. Target opponent loses life equal to the result.|
Bumbling Pangolin|Unstable|78|C|{3}{R}|Host Creature - Beast|2|2|When this creature enters the battlefield, you may destroy target artifact.|
Buzzing Whack-a-Doodle|Unstable|141|U|{4}|Artifact|||As Buzzing Whack-a-Doodle enters the battlefield, you and an opponent each secretly choose Whack or Doodle. Then those choices are revealed. If the choices match, Buzzing Whack-a-Doodle has that ability. Otherwise it has Buzz.$*Whack - T: Target player loses 2 life. $*Doodle - T: You gain 3 life.$*Buzz - 2, T: Draw a card.|
Chittering Doom|Unstable|104|U|{3}{G}|Enchantment|||Whenever you roll a 4 or higher on a die, create a 1/1 green Squirrel creature token.|
Common Iguana|Unstable|79|C|{1}{R}|Host Creature - Lizard|1|3|When this creature enters the battlefield, you may discard a card. If you do, draw a card.|
Contraption Cannon|Unstable|144|U|{4}|Artifact|||2, Sacrifice Contraption Cannon: It deals damage to target creature or player equal to the number of Contraptions you control.|
Crow Storm|Unstable|31|U|{2}{U}|Sorcery|||Create a 1/2 blue Bird creature token with flying named Storm Crow.$Storm (When you cast this spell, copy it for each spell cast before it this turn.)|
Curious Killbot|Unstable|145|C|{2}|Artifact Creature - Killbot|2|1||
Dirty Rat|Unstable|53|C|{1}{B}|Host Creature - Rat|1|1|When this creature enters the battlefield, target opponent discards a card.|
Eager Beaver|Unstable|107|C|{2}{G}|Host Creature - Beaver|3|2|When this creature enters the battlefield, you may untap target permanent.|
Earl of Squirrel|Unstable|108|R|{4}{G}{G}|Creature - Squirrel Advisor|4|4|Squirrellink (Damage dealt by this creature also causes you to create that many 1/1 green Squirrel creature tokens.)$Creature tokens you control are Squirrels in addition to their other creature types.$Other Squirrels you control get +1/+1.|
Feisty Stegosaurus|Unstable|81|C|{4}{R}|Host Creature - Dinosaur|2|1|When this creature enters the battlefield, roll a six-sided die. This creature deals damage equal to the result to target creature an opponent controls.|
Forest|Unstable|216|C||Basic Land - Forest|||G|
GO TO JAIL|Unstable|8|C|{W}|Enchantment|||When GO TO JAIL enters the battlefield, exile target creature an opponent controls until GO TO JAIL leaves the battlefield.$At the beginning of the upkeep of the exiled card's owner, that player rolls two six-sided dice. If he or she rolls doubles, sacrifice GO TO JAIL.|
Gnome-Made Engine|Unstable|148|C|{4}|Host Creature - Construct|2|2|When this creature enters the battlefield, create a 1/1 colorless Gnome artifact creature token.|
Ground Pounder|Unstable|110|C|{1}{G}|Creature - Goblin Warrior|2|2|3G: Roll a six-sided die. Ground Pounder gets +X/+X until end of turn, where X is the result.$Whenever you roll a 5 or higher on a die, Ground Pounder gains trample until end of turn.|
Hammer Helper|Unstable|85|C|{3}{R}|Sorcery|||Gain control of target creature until end of turn. Untap that creature and roll a six-sided die. Until end of turn, it gains haste and gets +X/+0, where X is the result.|
Hammer Jammer|Unstable|86|U|{3}{R}|Creature - Goblin Warrior|0|0|As Hammer Jammer enters the battlefield, roll a six-sided die. Hammer Jammer enters the battlefield with a number of +1/+1 counters on it equal to the result.$Whenever you roll a die, remove all +1/+1 counters from Hammer Jammer, then put a number of +1/+1 counters on it equal to the result.|
Hangman|Unstable|56|R|{B}|Creature - Human Villain|1|1|As Hangman enters the battlefield, secretly note a word with six to eight letters.$1: Target player who doesn't control Hangman guesses he noted word or an unguessed letter in that word. If he or she guesses wrong, put a +1/+1 counter on Hangman. Any player may activate this ability.$When a player guesses the noted word of all of its letters, sacrifice Hangman.|
Hazmat Suit (Used)|Unstable|57|C|{3}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+1 and has menace.$Whenever a player's skin or fingernail touches enchanted creature, that player loses 2 life.|
Hot Fix|Unstable|133|R|{4}{W}{U}|Sorcery||| You have ten seconds to look at and rearrange the cards in your library. At the end of those ten seconds, if you're touching one or more of those cards, shuffle your library.|
Hydradoodle|Unstable|112|R|{X}{X}{G}{G}|Creature - Hydra Hound|0|0|As Hydradoodle enters the battlefield, roll X six-sided dice. Hydradoodle enters the battlefield with s number of +1/+1 counters on it equal to the total of those results.$Reach, trample|
Incite Insight|Unstable|36|R|{X}{U}{U}|Sorcery|||Assemble X Contraptions.|
Inhumaniac|Unstable|59|U|{1}{B}|Creature - Brainiac|1|1|At the beginning of your upkeep, roll a six-sided die. On a 3 or 4, put a +1/+1 counter on Inhumaniac. On a 5 or higher, put two +1/+1 counters on it. On a 1, remove all +1/+1 counters from Inhumaniac.|
Island|Unstable|213|C||Basic Land - Island|||U|
Jackknight|Unstable|11|R|{1}{W}|Artifact Creature - Cyborg Knight|1|1|Whenever another artifact enters the battlefield under your control, put a +1/+1 counter on Jackknight. If that artifact is a Contraption, Jackknight gains lifelink until end of turn.|
Krark's Other Thumb|Unstable|151|U|{2}|Legendary Artifact|||If you would roll a die, instead roll two of those dice and ignore one of those results.|
Lobe Lobber|Unstable|153|U|{2}|Artifact - Equipment|||Equipped creature has "T: This creature deals 1 damage to target player. Roll a six-sided die. On a 5 or higher, untap it."$Equip 2|
Mad Science Fair Project|Unstable|154|C|{3}|Artifact|||T: Roll a six-sided die. On a 3 or lower, target player adds {C} to his or her mana pool. Otherwise, that player adds one mana of any color he or she chooses to his or her mana pool.|
Mer Man|Unstable|39|C|{4}{U}|Host Creature - Human Fish|3|3|When this creature enters the battlefield, you may draw a card.|
Mountain|Unstable|215|C||Basic Land - Mountain|||R|
Numbing Jellyfish|Unstable|42|C|{3}{U}|Host Creature - Jellyfish|2|3|When this creature enters the battlefield, roll a six-sided die. Target player puts the top X cards of his or her library into his or her graveyard, where X is the result.|
Ordinary Pony|Unstable|17|C|{2}{W}|Host Creature - Horse|2|3|When this creature enters the battlefield, you may exile target non-Horse creature you control, then return it to the battlefield under its owner's control.|
Painiac|Unstable|91|C|{2}{R}|Creature - Brainiac|0|3|At the beginning of your upkeep, roll a six-sided die. Painiac gets +X/+0 until end of turn, where X is the result.|
Plains|Unstable|212|C||Basic Land - Plains|||W|
Shaggy Camel|Unstable|22|C|{3}{W}|Host Creature - Camel|3|3|When this creature enters the battlefield, creatures you control get +1/+1 until end of turn.|
Skull Saucer|Unstable|66|U|{4}{B}{B}|Creature - Zombie Head|4|1|Flying$When Skull Saucer enters the battlefield, destroy target creature and put your head on the table. Sacrifice Skull Saucer when your head stops touching the table.|
Snickering Squirrel|Unstable|68|C|{B}|Creature - Squirrel Advisor|1|1|You may tap Snickering Squirrel to increase the result of a die any player rolled by 1.|
Split Screen|Unstable|158|R|{4}|Artifact|||When Split Screen enters the battlefield, shuffle your library and deal it into four libraries. If anything refers to your library, choose one of your libraries for it.$Play with your libraries' top cards revealed.$When Split Screen leaves the battlefield, shuffle your libraries together.|
Spy Eye|Unstable|46|U|{2}{U}{U}|Creature - Eye Spy|1|3|Flying$Whenever Spy Eye deals combat damage to a player, you may draw a card from that player's library.|
Squirrel-Powered Scheme|Unstable|70|U|{2}{B}|Enchantment|||Increase the result of each die you roll by 2.|
Box of Freerange Goblins|Unstable|77|C|{4}{R}{R}|Sorcery |||Roll a six-sided die. Create a number of 1/1 red Goblin creature tokens equal to the result.|
Staff of the Letter Magus|Unstable|159|U|{3}|Artifact|||As Staff of the Letter Magus enters the battlefield, choose a consonant other than N, R, S, or T.$Whenever a player casts a spell, you gain 1 life for each time the chosen letter appears in that spell's name.|
Steamflogger Boss|Unstable|93|R|{3}{R}|Creature - Goblin Rigger|3|3|Other Riggers you control get +1/+0 and have haste.$If a Rigger you control would assemble a Contraption, it assembles two Contraptions instead.|
Steamflogger Service Rep|Unstable|124|U|{2}{G}|Creature - Goblin Rigger|1|1|Whenever another Goblin enters the battlefield under your control, you may pay 1. If you do, Steamflogger Service Rep assembles a Contraption.|
Steel Squirrel|Unstable|162|U|{2}|Artifact Creature - Squirrel|1|1|Whenever you roll a 5 or higher on a die, Steel Squirrel gets +X/+X until end of turn, where X is the result.$6: Roll a six-sided die.|
Stinging Scorpion|Unstable|72|C|{4}{B}|Host Creature - Scorpion|3|2|When this creature enters the battlefield, target creature an opponent controls gets -1/-1 until end of turn.|
Summon the Pack|Unstable|74|M|{7}{B}|Sorcery|||Open a sealed Magic booster pack, reveal the cards, and put all creature cards revealed this way onto the battlefield under your control. They're Zombies in addition to their other types. (Remove those cards from your deck before beginning a new game)|
Swamp|Unstable|214|C||Basic Land - Swamp|||B|
Sword of Dungeons and Dragons|Unstable|1|M|{3}|Artifact - Equipment|||Equipped creature gets +2/+2 and has protection from Rogues and from Clerics.$Whenever equipped creature deals combat damage to a player, create a 4/4 gold Dragon creature token with flying and roll a d20. If you roll a 20, repeat this process.$Equip {2}|
Target Minotaur|Unstable|98|C|{1}{R}|Creature - Minotaur Warrior|2|1|Prowess|
The Big Idea|Unstable|76|R|{4}{R}{R}|Legendary Creature - Brainiac Villain|4|4|2{BR}{BR}, T: Roll a six-sided dice. Create a number of 1/1 red Brainiac creature tokens equal to the result. $Tap three untapped Brainiacs you control: The next time you would roll a six-sided die, instead roll two six-sided dice and use the total of those results.|
Time Out|Unstable|48|C|{4}{U}|Instant|||Roll a six-sided die. Put target nonland permanent into its owner's library just beneath the top X cards of that library, where X is the result.|
Voracious Vacuum|Unstable|164|C|{3}|Host Creature - Construct|1|1|When this creature enters the battlefield, put a +1/+1 counter on target creature.|
Wall of Fortune|Unstable|50|C|{1}{U}|Artifact Creature - Wall|0|4|Defender$You may tap an untapped Wall you control to have any player reroll a die that player rolled.|
Wild Crocodile|Unstable|125|C|{1}{G}|Host Creature - Crocodile|1|1|When this creature enters the battlefield, search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.|
Willing Test Subject|Unstable|126|C|{2}{G}|Creature- Spider Monkey Scientist|2|2|Reach$Whenever you roll a 4 or higher on a die, put a +1/+1 counter on Willing Test Subject.$6: Roll a six-sided die.|
Garbage Elemental|Unstable|82|U|{4}{R}|Creature - Elemental|3|2|Battle cry <i>(Whenever this creature attacks, each other attacking creature gets +1/+0 until end of turn.)</i>$When Garbage Elemental enters the battlefield, roll two six-sided dice. Create a number of 1/1 red Goblin creature tokens equal to the difference between those results.|