Merge pull request #12 from magefree/master

update
This commit is contained in:
Li REN 2013-06-19 17:46:20 -07:00
commit 7b71f12f5d
86 changed files with 2932 additions and 338 deletions

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View file

@ -0,0 +1,28 @@
NAME:Gadin's Robots
4 [DDF:72] Darksteel Citadel
3 [MMA:220] Blinkmoth Nexus
3 [MBS:145] Inkmoth Nexus
4 [NPH:76] Vault Skirge
4 [HOP:110] Cranial Plating
4 [MMA:223] Glimmervoid
3 [LRW:261] Springleaf Drum
2 [MMA:198] Arcbound Ravager
1 [RTR:265] Mountain
3 [SOM:154] Etched Champion
4 [DDF:48] Master of Etherium
4 [M11:211] Ornithopter
3 [SOM:179] Mox Opal
4 [SOM:91] Galvanic Blast
4 [DDF:71] Thoughtcast
1 [RTR:255] Island
3 [SOM:174] Memnite
3 [MBS:131] Signal Pest
3 [DDF:44] Steel Overseer
SB: 1 [ROE:92] Unified Will
SB: 3 [NPH:159] Spellskite
SB: 1 [NPH:102] Whipflare
SB: 3 [MMA:213] Relic of Progenitus
SB: 1 [M13:62] Negate
SB: 3 [MMA:106] Blood Moon
SB: 2 [PC2:7] Ghostly Prison
SB: 1 [ISD:127] Ancient Grudge

View file

@ -37,7 +37,6 @@ package mage.client;
import de.schlichtherle.truezip.file.TArchiveDetector;
import de.schlichtherle.truezip.file.TConfig;
import de.schlichtherle.truezip.fs.FsOutputOption;
import mage.cards.Card;
import mage.cards.decks.Deck;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
@ -479,19 +478,9 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
menu.show(component, 0, component.getHeight());
}
private List<Card> getAllCards() {
List<Card> cards = new ArrayList<Card>();
List<CardInfo> allCards = CardRepository.instance.getAllCards();
for (CardInfo cardInfo : allCards) {
cards.add(cardInfo.getCard());
}
return cards;
}
private void checkForNewImages() {
long beforeCall = System.currentTimeMillis();
List<Card> cards = getAllCards();
List<CardInfo> cards = CardRepository.instance.getAllCards();
logger.info("Card pool load time: " + ((System.currentTimeMillis() - beforeCall) / 1000 + " seconds"));
String useDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true");
@ -507,7 +496,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
}
public void btnImagesActionPerformed(java.awt.event.ActionEvent evt) {
List<Card> cards = getAllCards();
List<CardInfo> cards = CardRepository.instance.getAllCards();
String useDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true");
String path = useDefault.equals("true") ? null : PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PATH, null);

View file

@ -26,8 +26,11 @@ public class HoverButton extends JPanel implements MouseListener {
private int textOffsetY = 0;
private int textOffsetButtonY = 2;
private int textOffsetX = -1;
private int topTextOffsetX = -1;
private Dimension overlayImageSize;
private String topText;
private boolean isHovered = false;
private boolean isSelected = false;
private boolean drawSet = false;
@ -36,6 +39,7 @@ public class HoverButton extends JPanel implements MouseListener {
private Command observer = null;
private Command onHover = null;
private Color textColor = Color.white;
private Color textBGColor = Color.black;
static final Font textFont = new Font("Arial", Font.PLAIN, 12);
static final Font textFontMini = new Font("Arial", Font.PLAIN, 11);
@ -100,6 +104,18 @@ public class HoverButton extends JPanel implements MouseListener {
} else {
g.drawImage(disabledImage, 0, 0, imageSize.width, imageSize.height, this);
}
if (topText != null) {
if (useMiniFont) {
g2d.setFont(textFontMini);
} else {
g2d.setFont(textFont);
}
topTextOffsetX = calculateOffsetForTop(g2d, topText);
g2d.setColor(textBGColor);
g2d.drawString(topText, topTextOffsetX+1, 13);
g2d.setColor(textColor);
g2d.drawString(topText, topTextOffsetX, 12);
}
if (overlayImage != null) {
g.drawImage(overlayImage, (imageSize.width - overlayImageSize.width) / 2, 10, this);
} else if (set != null) {
@ -136,6 +152,15 @@ public class HoverButton extends JPanel implements MouseListener {
return textOffsetX;
}
private int calculateOffsetForTop(Graphics2D g2d, String text) {
if (topTextOffsetX == -1) { // calculate once
FontRenderContext frc = g2d.getFontRenderContext();
int textWidth = (int) textFont.getStringBounds(text, frc).getWidth();
topTextOffsetX = (imageSize.width - textWidth) / 2;
}
return topTextOffsetX;
}
public void setTextColor(Color textColor) {
this.textColor = textColor;
}
@ -242,4 +267,8 @@ public class HoverButton extends JPanel implements MouseListener {
observer.execute();
}
}
public void setTopText(String topText) {
this.topText = topText;
}
}

View file

@ -49,6 +49,7 @@ import java.util.List;
import java.util.UUID;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import mage.client.util.AudioManager;
/**
*
@ -392,6 +393,7 @@ class UpdateSeatsTask extends SwingWorker<Void, TableView> {
if (count > 0) {
if (current > count) {
MageTray.getInstance().displayMessage("New player joined your game.");
AudioManager.playPlayerJoinedTable();
} else {
MageTray.getInstance().displayMessage("A player left your game.");
}

View file

@ -34,6 +34,7 @@
package mage.client.game;
import mage.MageException;
import mage.cards.MageCard;
import mage.cards.action.ActionCallback;
import mage.cards.decks.importer.DckDeckImporter;
@ -51,7 +52,9 @@ import mage.client.util.Command;
import mage.client.util.ImageHelper;
import mage.client.util.gui.BufferedImageBuilder;
import mage.components.ImagePanel;
import mage.constants.Constants;
import mage.remote.Session;
import mage.utils.timer.PriorityTimer;
import mage.view.CardView;
import mage.view.ManaPoolView;
import mage.view.PlayerView;
@ -101,6 +104,8 @@ public class PlayerPanelExt extends javax.swing.JPanel {
private int avatarId = -1;
private PriorityTimer timer;
/** Creates new form PlayerPanel */
public PlayerPanelExt() {
setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
@ -114,6 +119,25 @@ public class PlayerPanelExt extends javax.swing.JPanel {
session = MageFrame.getSession();
cheat.setVisible(session.isTestMode());
cheat.setFocusable(false);
long delay = 1000L;
timer = new PriorityTimer(Constants.PRIORITY_TIME_SEC, delay, new mage.interfaces.Action() {
@Override
public void execute() throws MageException {
// do nothing
}
});
final PriorityTimer pt = timer;
timer.setTaskOnTick(new mage.interfaces.Action() {
@Override
public void execute() throws MageException {
int priorityTimeValue = pt.getCount();
String text = getPriorityTimeLeftString(priorityTimeValue);
PlayerPanelExt.this.avatar.setTopText(text);
PlayerPanelExt.this.avatar.repaint();
}
});
timer.init();
}
public void update(PlayerView player) {
@ -182,16 +206,22 @@ public class PlayerPanelExt extends javax.swing.JPanel {
}
}
this.avatar.setText(player.getName());
String priorityTimeValue = getPriorityTimeLeftString(player);
this.timer.setCount(player.getPriorityTimeLeft());
this.avatar.setTopText(priorityTimeValue);
this.btnPlayer.setText(player.getName());
if (player.isActive()) {
this.avatar.setBorder(greenBorder);
this.btnPlayer.setBorder(greenBorder);
this.timer.resume();
} else if (player.hasLeft()) {
this.avatar.setBorder(redBorder);
this.btnPlayer.setBorder(redBorder);
this.timer.pause();
} else {
this.avatar.setBorder(emptyBorder);
this.btnPlayer.setBorder(emptyBorder);
this.timer.pause();
}
synchronized (this) {
@ -216,6 +246,18 @@ public class PlayerPanelExt extends javax.swing.JPanel {
update(player.getManaPool());
}
private String getPriorityTimeLeftString(PlayerView player) {
int priorityTimeLeft = player.getPriorityTimeLeft();
return getPriorityTimeLeftString(priorityTimeLeft);
}
private String getPriorityTimeLeftString(int priorityTimeLeft) {
int h = priorityTimeLeft / 3600;
int m = (priorityTimeLeft % 3600) / 60;
int s = priorityTimeLeft % 60;
return (h < 10 ? "0" : "") + h + ":" + (m < 10 ? "0" : "") + m + ":" + (s < 10 ? "0" : "") + s;
}
protected void update(ManaPoolView pool) {
manaLabels.get("B").setText(Integer.toString(pool.getBlack()));
manaLabels.get("R").setText(Integer.toString(pool.getRed()));

View file

@ -1,12 +1,15 @@
package mage.client.util;
import java.io.File;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import mage.client.constants.Constants;
import mage.client.dialog.PreferencesDialog;
import org.apache.log4j.Logger;
import javax.sound.sampled.*;
import java.io.File;
/**
* Manager class for playing audio files.
*
@ -42,6 +45,8 @@ public class AudioManager {
audioManager.addArtifactClip = audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnAddArtifact.wav");
audioManager.updateStackClip = audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnStackNew.wav");
audioManager.onHover = audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnHover.wav");
audioManager.playerJoinedTable = audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnPlayerJoinedTable.wav");
}
return audioManager;
}
@ -114,6 +119,10 @@ public class AudioManager {
checkAndPlayClip(getManager().onHover);
}
public static void playPlayerJoinedTable() {
checkAndPlayClip(getManager().playerJoinedTable);
}
private static void checkAndPlayClip(Clip clip) {
try {
if (clip != null) {
@ -175,4 +184,6 @@ public class AudioManager {
private Clip addArtifactClip = null;
private Clip updateStackClip = null;
private Clip onHover = null;
private Clip playerJoinedTable = null;
}

View file

@ -1,12 +1,14 @@
package org.mage.plugins.card.dl.sources;
import org.mage.plugins.card.images.CardDownloadData;
/**
*
* @author North
*/
public interface CardImageSource {
String generateURL(Integer collectorId, String cardName, String cardSet, boolean twoFacedCard, boolean secondFace, boolean isFlipCard, boolean isSplitCard, boolean flippedView) throws Exception;
String generateTokenUrl(String name, String set);
String generateURL(CardDownloadData card) throws Exception;
String generateTokenUrl(CardDownloadData card);
Float getAverageSize();
}

View file

@ -1,5 +1,6 @@
package org.mage.plugins.card.dl.sources;
import org.mage.plugins.card.images.CardDownloadData;
import org.mage.plugins.card.utils.CardImageUtils;
import java.util.HashMap;
@ -64,7 +65,9 @@ public class MagicCardsImageSource implements CardImageSource {
}
@Override
public String generateURL(Integer collectorId, String cardName, String cardSet, boolean twoFacedCard, boolean secondSide, boolean isFlipCard, boolean isSplitCard, boolean flippedView) throws Exception {
public String generateURL(CardDownloadData card) throws Exception {
Integer collectorId = card.getCollectorId();
String cardSet = card.getSet();
if (collectorId == null || cardSet == null) {
throw new Exception("Wrong parameters for image: collector id: " + collectorId + ",card set: " + cardSet);
}
@ -72,14 +75,14 @@ public class MagicCardsImageSource implements CardImageSource {
StringBuilder url = new StringBuilder("http://magiccards.info/scans/en/");
url.append(set.toLowerCase()).append("/").append(collectorId);
if (twoFacedCard) {
url.append(secondSide ? "b" : "a");
if (card.isTwoFacedCard()) {
url.append(card.isSecondSide() ? "b" : "a");
}
if (isSplitCard) {
if (card.isSplitCard()) {
url.append("a");
}
if (isFlipCard) {
if (flippedView) { // download rotated by 180 degree image
if (card.isFlipCard()) {
if (card.isFlippedSide()) { // download rotated by 180 degree image
url.append("b");
} else {
url.append("a");
@ -91,17 +94,15 @@ public class MagicCardsImageSource implements CardImageSource {
}
@Override
public String generateTokenUrl(String name, String set) {
String _name = name.replaceAll(" ", "-").replace(",", "").toLowerCase();
String _set = "not-supported-set";
if (setNameReplacement.containsKey(set)) {
_set = setNameReplacement.get(set);
public String generateTokenUrl(CardDownloadData card) {
String name = card.getName().replaceAll(" ", "-").replace(",", "").toLowerCase();
String set = "not-supported-set";
if (setNameReplacement.containsKey(card.getSet())) {
set = setNameReplacement.get(card.getSet());
} else {
_set += "-" + set;
set += "-" + card.getSet();
}
String url = "http://magiccards.info/extras/token/" + _set + "/" + _name + ".jpg";
return url;
return "http://magiccards.info/extras/token/" + set + "/" + name + ".jpg";
}
@Override

View file

@ -3,6 +3,7 @@ package org.mage.plugins.card.dl.sources;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.mage.plugins.card.images.CardDownloadData;
import java.io.IOException;
import java.util.HashMap;
@ -15,8 +16,8 @@ import java.util.Map;
public class WizardCardsImageSource implements CardImageSource {
private static CardImageSource instance;
private static Map setsAliases;
private Map sets;
private static Map<String, String> setsAliases;
private Map<String, Map<String, String>> sets;
public static CardImageSource getInstance() {
if (instance == null) {
@ -26,8 +27,8 @@ public class WizardCardsImageSource implements CardImageSource {
}
public WizardCardsImageSource() {
sets = new HashMap();
setsAliases = new HashMap();
sets = new HashMap<String, Map<String, String>>();
setsAliases = new HashMap<String, String>();
setsAliases.put("MMA", "modernmasters/cig");
setsAliases.put("DGM", "dragonsmaze/cig");
setsAliases.put("GTC", "gatecrash/cig");
@ -56,7 +57,7 @@ public class WizardCardsImageSource implements CardImageSource {
private Map<String, String> getSetLinks(String cardSet) {
Map<String, String> setLinks = new HashMap<String, String>();
try {
Document doc = Jsoup.connect("http://www.wizards.com/magic/tcg/article.aspx?x=mtg/tcg/" + (String) setsAliases.get(cardSet)).get();
Document doc = Jsoup.connect("http://www.wizards.com/magic/tcg/article.aspx?x=mtg/tcg/" + setsAliases.get(cardSet)).get();
Elements cardsImages = doc.select("img[height$=370]");
for (int i = 0; i < cardsImages.size(); i++) {
String cardName = cardsImages.get(i).attr("title").replace("\u00C6", "AE").replace("\u2019", "'");
@ -94,20 +95,22 @@ public class WizardCardsImageSource implements CardImageSource {
}
@Override
public String generateURL(Integer collectorId, String cardName, String cardSet, boolean twoFacedCard, boolean secondSide, boolean isFlipCard, boolean isSplitCard, boolean flippedView) throws Exception {
public String generateURL(CardDownloadData card) throws Exception {
Integer collectorId = card.getCollectorId();
String cardSet = card.getSet();
if (collectorId == null || cardSet == null) {
throw new Exception("Wrong parameters for image: collector id: " + collectorId + ",card set: " + cardSet);
}
if (flippedView) { //doesn't support rotated images
if (card.isFlippedSide()) { //doesn't support rotated images
return null;
}
if (setsAliases.get(cardSet) != null) {
Map<String, String> setLinks = (Map<String, String>) sets.get(cardSet);
Map<String, String> setLinks = sets.get(cardSet);
if (setLinks == null) {
setLinks = getSetLinks(cardSet);
sets.put(cardSet, setLinks);
}
String link = setLinks.get(cardName);
String link = setLinks.get(card.getDownloadName());
if (link == null) {
if (setLinks.size() >= collectorId) {
link = setLinks.get(Integer.toString(collectorId - 1));
@ -127,7 +130,7 @@ public class WizardCardsImageSource implements CardImageSource {
}
@Override
public String generateTokenUrl(String name, String set) {
public String generateTokenUrl(CardDownloadData card) {
return null;
}
@ -135,4 +138,4 @@ public class WizardCardsImageSource implements CardImageSource {
public Float getAverageSize() {
return 60.0f;
}
}
}

View file

@ -4,7 +4,7 @@ package org.mage.plugins.card.images;
*
* @author North
*/
public class CardInfo {
public class CardDownloadData {
private String name;
private String downloadName;
@ -19,15 +19,15 @@ public class CardInfo {
private boolean splitCard;
private boolean usesVariousArt;
public CardInfo(String name, String set, Integer collectorId, boolean usesVariousArt, Integer type) {
public CardDownloadData(String name, String set, Integer collectorId, boolean usesVariousArt, Integer type) {
this(name, set, collectorId, usesVariousArt, type, false);
}
public CardInfo(String name, String set, Integer collectorId, boolean usesVariousArt, Integer type, boolean token) {
public CardDownloadData(String name, String set, Integer collectorId, boolean usesVariousArt, Integer type, boolean token) {
this(name, set, collectorId, usesVariousArt, type, token, false, false);
}
public CardInfo(String name, String set, Integer collectorId, boolean usesVariousArt, Integer type, boolean token, boolean twoFacedCard, boolean secondSide) {
public CardDownloadData(String name, String set, Integer collectorId, boolean usesVariousArt, Integer type, boolean token, boolean twoFacedCard, boolean secondSide) {
this.name = name;
this.set = set;
this.collectorId = collectorId;
@ -38,7 +38,7 @@ public class CardInfo {
this.secondSide = secondSide;
}
public CardInfo(final CardInfo card) {
public CardDownloadData(final CardDownloadData card) {
this.name = card.name;
this.set = card.set;
this.collectorId = card.collectorId;
@ -57,7 +57,7 @@ public class CardInfo {
if (getClass() != obj.getClass()) {
return false;
}
final CardInfo other = (CardInfo) obj;
final CardDownloadData other = (CardDownloadData) obj;
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
@ -96,10 +96,6 @@ public class CardInfo {
return collectorId;
}
public void setCollectorId(Integer collectorId) {
this.collectorId = collectorId;
}
public String getName() {
return name;
}
@ -160,10 +156,6 @@ public class CardInfo {
return type;
}
public void setType(Integer type) {
this.type = type;
}
public boolean getUsesVariousArt() {
return usesVariousArt;
}

View file

@ -4,7 +4,7 @@ import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.file.TFileOutputStream;
import de.schlichtherle.truezip.file.TVFS;
import de.schlichtherle.truezip.fs.FsSyncException;
import mage.cards.Card;
import mage.cards.repository.CardInfo;
import mage.client.dialog.PreferencesDialog;
import mage.remote.Connection;
import org.apache.log4j.Logger;
@ -46,7 +46,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
private JButton closeButton;
private JButton startDownloadButton;
private int cardIndex;
private ArrayList<CardInfo> cards;
private ArrayList<CardDownloadData> cards;
private JComboBox jComboBox1;
private JLabel jLabel1;
private static boolean offlineMode = false;
@ -60,14 +60,12 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
private ExecutorService executor = Executors.newFixedThreadPool(10);
public static final Proxy.Type[] types = Proxy.Type.values();
public static void main(String[] args) {
startDownload(null, null, null);
}
public static void startDownload(JFrame frame, List<Card> allCards, String imagesPath) {
ArrayList<CardInfo> cards = getNeededCards(allCards, imagesPath);
public static void startDownload(JFrame frame, List<CardInfo> allCards, String imagesPath) {
ArrayList<CardDownloadData> cards = getNeededCards(allCards, imagesPath);
/*
* if (cards == null || cards.size() == 0) {
@ -99,33 +97,15 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
this.cancel = cancel;
}
public DownloadPictures(ArrayList<CardInfo> cards, String imagesPath) {
public DownloadPictures(ArrayList<CardDownloadData> cards, String imagesPath) {
this.cards = cards;
this.imagesPath = imagesPath;
//addr = new JTextField("Proxy Address");
//port = new JTextField("Proxy Port");
bar = new JProgressBar(this);
JPanel p0 = new JPanel();
p0.setLayout(new BoxLayout(p0, BoxLayout.Y_AXIS));
// Proxy Choice
/*ButtonGroup bg = new ButtonGroup();
String[] labels = { "No Proxy", "HTTP Proxy", "SOCKS Proxy" };
for (int i = 0; i < types.length; i++) {
JRadioButton rb = new JRadioButton(labels[i]);
rb.addChangeListener(new ProxyHandler(i));
bg.add(rb);
p0.add(rb);
if (i == 0)
rb.setSelected(true);
}*/
// Proxy config
//p0.add(addr);
//p0.add(port);
p0.add(Box.createVerticalStrut(5));
jLabel1 = new JLabel();
jLabel1.setText("Please select server:");
@ -206,11 +186,11 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
dlg = new JOptionPane(p0, JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[1]);
}
public static boolean checkForNewCards(List<Card> allCards, String imagesPath) {
public static boolean checkForNewCards(List<CardInfo> allCards, String imagesPath) {
TFile file;
for (Card card : allCards) {
if (card.getCardNumber() > 0 && !card.getExpansionSetCode().isEmpty()) {
CardInfo url = new CardInfo(card.getName(), card.getExpansionSetCode(), card.getCardNumber(),card.getUsesVariousArt(),0 , false, card.canTransform(), card.isNightCard());
for (CardInfo card : allCards) {
if (card.getCardNumber() > 0 && !card.getSetCode().isEmpty()) {
CardDownloadData url = new CardDownloadData(card.getName(), card.getSetCode(), card.getCardNumber(), usesVariousArt(card), 0, false, card.isDoubleFaced(), card.isNightCard());
file = new TFile(CardImageUtils.getImagePath(url, imagesPath));
if (!file.exists()) {
return true;
@ -220,47 +200,54 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
return false;
}
private static ArrayList<CardInfo> getNeededCards(List<Card> allCards, String imagesPath) {
private static boolean usesVariousArt(CardInfo card) {
String className = card.getClassName();
return Character.isDigit(className.charAt(className.length() - 1));
}
ArrayList<CardInfo> cardsToDownload = new ArrayList<CardInfo>();
private static String createDownloadName(CardInfo card) {
String className = card.getClassName();
return className.substring(className.lastIndexOf('.') + 1);
}
private static ArrayList<CardDownloadData> getNeededCards(List<CardInfo> allCards, String imagesPath) {
ArrayList<CardDownloadData> cardsToDownload = new ArrayList<CardDownloadData>();
/**
* read all card names and urls
*/
ArrayList<CardInfo> allCardsUrls = new ArrayList<CardInfo>();
ArrayList<CardDownloadData> allCardsUrls = new ArrayList<CardDownloadData>();
HashSet<String> ignoreUrls = SettingsManager.getIntance().getIgnoreUrls();
try {
offlineMode = true;
for (Card card : allCards) {
if (card.getCardNumber() > 0 && !card.getExpansionSetCode().isEmpty()
&& !ignoreUrls.contains(card.getExpansionSetCode())) {
for (CardInfo card : allCards) {
if (card.getCardNumber() > 0 && !card.getSetCode().isEmpty()
&& !ignoreUrls.contains(card.getSetCode())) {
String cardName = card.getName();
CardInfo url = new CardInfo(cardName, card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), 0, false, card.canTransform(), card.isNightCard());
CardDownloadData url = new CardDownloadData(cardName, card.getSetCode(), card.getCardNumber(), usesVariousArt(card), 0, false, card.isDoubleFaced(), card.isNightCard());
if (url.getUsesVariousArt()) {
url.setDownloadName(card.getClass().getName().replace(card.getClass().getPackage().getName() + ".", ""));
}
if (card.isFlipCard()) {
url.setFlipCard(true);
}
if (card.isSplitCard()) {
url.setSplitCard(true);
url.setDownloadName(createDownloadName(card));
}
url.setFlipCard(card.isFlipCard());
url.setSplitCard(card.isSplitCard());
allCardsUrls.add(url);
if (card.canTransform()) {
if (card.isDoubleFaced()) {
// add second side for downloading
// it has the same expansion set code and card number as original one
// second side = true;
Card secondSide = card.getSecondCardFace();
url = new CardInfo(secondSide.getName(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), 0, false, card.canTransform(), true);
url = new CardDownloadData(card.getSecondSideName(), card.getSetCode(), card.getCardNumber(), usesVariousArt(card), 0, false, card.isDoubleFaced(), true);
allCardsUrls.add(url);
}
if (card.isFlipCard()) {
if (card.getFlipCardName() == null || card.getFlipCardName().trim().isEmpty()) {
throw new IllegalStateException("Flipped card can't have empty name.");
}
url = new CardInfo(card.getFlipCardName(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), 0, false, card.canTransform(), card.isNightCard());
url = new CardDownloadData(card.getFlipCardName(), card.getSetCode(), card.getCardNumber(), usesVariousArt(card), 0, false, card.isDoubleFaced(), card.isNightCard());
url.setFlipCard(true);
url.setFlippedSide(true);
allCardsUrls.add(url);
@ -269,7 +256,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
if (card.getCardNumber() < 1) {
System.err.println("There was a critical error!");
log.error("Card has no collector ID and won't be sent to client: " + card);
} else if (card.getExpansionSetCode().isEmpty()) {
} else if (card.getSetCode().isEmpty()) {
System.err.println("There was a critical error!");
log.error("Card has no set name and won't be sent to client:" + card);
}
@ -286,14 +273,14 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
/**
* check to see which cards we already have
*/
for (CardInfo card : allCardsUrls) {
for (CardDownloadData card : allCardsUrls) {
file = new TFile(CardImageUtils.getImagePath(card, imagesPath));
if (!file.exists()) {
cardsToDownload.add(card);
}
}
for (CardInfo card : cardsToDownload) {
for (CardDownloadData card : cardsToDownload) {
if (card.isToken()) {
log.info("Card to download: " + card.getName() + " (Token) ");
} else {
@ -308,8 +295,8 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
return cardsToDownload;
}
private static ArrayList<CardInfo> getTokenCardUrls() throws RuntimeException {
ArrayList<CardInfo> list = new ArrayList<CardInfo>();
private static ArrayList<CardDownloadData> getTokenCardUrls() throws RuntimeException {
ArrayList<CardDownloadData> list = new ArrayList<CardDownloadData>();
InputStream in = DownloadPictures.class.getClassLoader().getResourceAsStream("card-pictures-tok.txt");
if (in == null) {
@ -332,15 +319,15 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
if (params.length >= 4) {
if (params[1].toLowerCase().equals("generate") && params[2].startsWith("TOK:")) {
String set = params[2].substring(4);
CardInfo card = new CardInfo(params[3], set, 0, false, 0, true);
CardDownloadData card = new CardDownloadData(params[3], set, 0, false, 0, true);
list.add(card);
} else if (params[1].toLowerCase().equals("generate") && params[2].startsWith("EMBLEM:")) {
String set = params[2].substring(7);
CardInfo card = new CardInfo("Emblem " + params[3], set, 0, false,0, true);
CardDownloadData card = new CardDownloadData("Emblem " + params[3], set, 0, false,0, true);
list.add(card);
} else if (params[1].toLowerCase().equals("generate") && params[2].startsWith("EMBLEM-:")) {
String set = params[2].substring(8);
CardInfo card = new CardInfo(params[3] + " Emblem", set, 0, false, 0, true);
CardDownloadData card = new CardDownloadData(params[3] + " Emblem", set, 0, false, 0, true);
list.add(card);
}
} else {
@ -408,7 +395,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
for (int i = 0; i < cards.size() && !cancel; i++) {
try {
CardInfo card = cards.get(i);
CardDownloadData card = cards.get(i);
log.info("Downloading card: " + card.getName() + " (" + card.getSet() + ")");
@ -417,10 +404,9 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
if (card.getCollectorId() != 0) {
continue;
}
url = cardImageSource.generateTokenUrl(card.getName(), card.getSet());
url = cardImageSource.generateTokenUrl(card);
} else {
url = cardImageSource.generateURL(card.getCollectorId(), card.getDownloadName(), card.getSet(),
card.isTwoFacedCard(), card.isSecondSide(), card.isFlipCard(), card.isSplitCard(), card.isFlippedSide());
url = cardImageSource.generateURL(card);
}
if (url != null) {
@ -457,10 +443,10 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
private final class DownloadTask implements Runnable {
private CardInfo card;
private CardDownloadData card;
private URL url;
public DownloadTask(CardInfo card, URL url) {
public DownloadTask(CardDownloadData card, URL url) {
this.card = card;
this.url = url;
}
@ -573,10 +559,10 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
bar.setString(String.format("%d of %d cards finished! Please wait! [%.1f Mb]",
card, count, mb));
} else {
Iterator<CardInfo> cardsIterator = DownloadPictures.this.cards.iterator();
Iterator<CardDownloadData> cardsIterator = DownloadPictures.this.cards.iterator();
while (cardsIterator.hasNext()) {
CardInfo cardInfo = cardsIterator.next();
TFile file = new TFile(CardImageUtils.getImagePath(cardInfo, imagesPath));
CardDownloadData cardDownloadData = cardsIterator.next();
TFile file = new TFile(CardImageUtils.getImagePath(cardDownloadData, imagesPath));
if (file.exists()) {
cardsIterator.remove();
}
@ -584,7 +570,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
count = DownloadPictures.this.cards.size();
if (count == 0) {
bar.setString(String.format("0 cards remaining! Please close!", count));
bar.setString("0 cards remaining! Please close!");
} else {
bar.setString(String.format("%d cards remaining! Please choose another source!", count));
//executor = Executors.newFixedThreadPool(10);

View file

@ -71,7 +71,7 @@ public class ImageCache {
Integer type = Integer.parseInt(m.group(3));
Integer collectorId = Integer.parseInt(m.group(4));
CardInfo info = new CardInfo(name, set, collectorId, usesVariousArt, type);
CardDownloadData info = new CardDownloadData(name, set, collectorId, usesVariousArt, type);
if (collectorId == 0) {
info.setToken(true);

View file

@ -4,12 +4,12 @@ import de.schlichtherle.truezip.file.TFile;
import java.util.HashMap;
import mage.client.constants.Constants;
import mage.client.dialog.PreferencesDialog;
import org.mage.plugins.card.images.CardInfo;
import org.mage.plugins.card.images.CardDownloadData;
import org.mage.plugins.card.properties.SettingsManager;
public class CardImageUtils {
private static HashMap<CardInfo, String> pathCache = new HashMap<CardInfo, String>();
private static HashMap<CardDownloadData, String> pathCache = new HashMap<CardDownloadData, String>();
/**
* Get path to image for specific card.
@ -18,7 +18,7 @@ public class CardImageUtils {
* card to get path for
* @return String if image exists, else null
*/
public static String getImagePath(CardInfo card) {
public static String getImagePath(CardDownloadData card) {
String filePath;
TFile file;
@ -51,19 +51,19 @@ public class CardImageUtils {
}
}
private static String getTokenImagePath(CardInfo card) {
private static String getTokenImagePath(CardDownloadData card) {
String useDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true");
String path = useDefault.equals("true") ? null : PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PATH, null);
String filename = getImagePath(card, path);
TFile file = new TFile(filename);
if (!file.exists()) {
CardInfo updated = new CardInfo(card);
CardDownloadData updated = new CardDownloadData(card);
updated.setName(card.getName() + " 1");
filename = getImagePath(updated, path);
file = new TFile(filename);
if (!file.exists()) {
updated = new CardInfo(card);
updated = new CardDownloadData(card);
updated.setName(card.getName() + " 2");
filename = getImagePath(updated, path);
}
@ -72,10 +72,10 @@ public class CardImageUtils {
return filename;
}
private static String searchForCardImage(CardInfo card) {
private static String searchForCardImage(CardDownloadData card) {
TFile file;
String path;
CardInfo c = new CardInfo(card);
CardDownloadData c = new CardDownloadData(card);
for (String set : SettingsManager.getIntance().getTokenLookupOrder()) {
c.setSet(set);
@ -100,7 +100,7 @@ public class CardImageUtils {
return set;
}
private static String getImageDir(CardInfo card, String imagesPath) {
private static String getImageDir(CardDownloadData card, String imagesPath) {
if (card.getSet() == null) {
return "";
}
@ -129,7 +129,7 @@ public class CardImageUtils {
}
}
public static String getImagePath(CardInfo card, String imagesPath) {
public static String getImagePath(CardDownloadData card, String imagesPath) {
String imageDir = getImageDir(card, imagesPath);
String imageName;

View file

@ -68,6 +68,11 @@ public final class Constants {
public static final double SCALE_FACTOR = 0.5;
/**
* Time each player has during the game to play using his\her priority.
*/
public static final int PRIORITY_TIME_SEC = 1200;
public enum SessionState {
DISCONNECTED, CONNECTED, CONNECTING, DISCONNECTING, SERVER_UNAVAILABLE, SERVER_STARTING;
}

View file

@ -0,0 +1,127 @@
package mage.utils.timer;
import mage.MageException;
import mage.interfaces.Action;
import org.apache.log4j.Logger;
import java.util.Timer;
import java.util.TimerTask;
/**
* @author noxx
*/
public class PriorityTimer extends TimerTask {
private static final Logger logger = Logger.getLogger(PriorityTimer.class);
private int count;
private long delay;
private Action taskOnTimeout;
private Action taskOnTick;
private States state = States.NONE;
enum States {
NONE,
INIT,
RUNNING,
PAUSED,
FINISHED
}
public PriorityTimer(int count, long delay, Action taskOnTimeout) {
this.count = count;
this.delay = delay;
this.taskOnTimeout = taskOnTimeout;
}
public void init() {
state = States.INIT;
Timer timer = new Timer("Priority Timer", false);
long delayMs = delay * (int) (1000L / delay);
timer.scheduleAtFixedRate(this, delayMs, delayMs);
}
public void start() {
if (state == States.NONE) {
throw new IllegalStateException("Timer should have been initialized first");
}
if (state == States.FINISHED) {
throw new IllegalStateException("Timer has already finished its work");
}
state = States.RUNNING;
}
public void pause() {
state = States.PAUSED;
}
public void stop() {
state = States.FINISHED;
count = 0;
}
public void resume() {
if (state == States.FINISHED) {
throw new IllegalStateException("Timer has already finished its work");
}
state = States.RUNNING;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public void setTaskOnTick(Action taskOnTick) {
this.taskOnTick = taskOnTick;
}
@Override
public void run() {
if (state == States.RUNNING) {
count--;
if (taskOnTick != null) {
try {
taskOnTick.execute();
} catch (MageException e) {
throw new RuntimeException(e);
}
}
}
if (logger.isDebugEnabled()) logger.debug("Count is: " + count);
//System.out.println("Count is: " + count);
if (count <= 0) {
cancel();
try {
taskOnTimeout.execute();
} catch (MageException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) throws Exception {
long delay = 250L;
int count = 5;
PriorityTimer timer = new PriorityTimer(count, delay, new Action() {
@Override
public void execute() throws MageException {
System.out.println("Exit");
System.exit(0);
}
});
timer.init();
timer.start();
Thread.sleep(2000);
timer.pause();
Thread.sleep(3000);
timer.resume();
}
}

View file

@ -63,6 +63,7 @@ public class PlayerView implements Serializable {
private List<EmblemView> emblemList = new ArrayList<EmblemView>();
private List<UUID> attachments = new ArrayList<UUID>();
private int statesSavedSize;
private int priorityTimeLeft;
public PlayerView(Player player, GameState state, Game game) {
this.playerId = player.getId();
@ -108,6 +109,7 @@ public class PlayerView implements Serializable {
}
this.statesSavedSize = player.getStoredBookmark();
this.priorityTimeLeft = player.getPriorityTimeLeft();
}
private boolean showInBattlefield(Permanent permanent, GameState state) {
@ -191,4 +193,8 @@ public class PlayerView implements Serializable {
public int getStatesSavedSize() {
return statesSavedSize;
}
public int getPriorityTimeLeft() {
return priorityTimeLeft;
}
}

View file

@ -28,11 +28,6 @@
package mage.player.human;
import java.io.Serializable;
import java.util.*;
import mage.constants.Outcome;
import mage.constants.RangeOfInfluence;
import mage.constants.Zone;
import mage.MageObject;
import mage.abilities.*;
import mage.abilities.costs.common.SacrificeSourceCost;
@ -46,6 +41,9 @@ import mage.cards.Cards;
import mage.cards.decks.Deck;
import mage.choices.Choice;
import mage.choices.ChoiceImpl;
import mage.constants.Outcome;
import mage.constants.RangeOfInfluence;
import mage.constants.Zone;
import mage.filter.common.FilterAttackingCreature;
import mage.filter.common.FilterBlockingCreature;
import mage.filter.common.FilterCreatureForCombat;
@ -67,6 +65,9 @@ import mage.target.common.TargetDefender;
import mage.util.ManaUtil;
import org.apache.log4j.Logger;
import java.io.Serializable;
import java.util.*;
/**
*
@ -98,40 +99,43 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
super(player);
}
protected void waitForResponse() {
protected void waitForResponse(Game game) {
response.clear();
log.debug("Waiting response from player: " + getId());
game.resumeTimer(playerId);
synchronized(response) {
try {
response.wait();
log.debug("Got response from player: " + getId());
} catch (InterruptedException ex) {
ex.printStackTrace();
} finally {
game.pauseTimer(playerId);
}
}
}
protected void waitForBooleanResponse() {
protected void waitForBooleanResponse(Game game) {
do {
waitForResponse();
waitForResponse(game);
} while (response.getBoolean() == null && !abort);
}
protected void waitForUUIDResponse() {
protected void waitForUUIDResponse(Game game) {
do {
waitForResponse();
waitForResponse(game);
} while (response.getUUID() == null && !abort);
}
protected void waitForStringResponse() {
protected void waitForStringResponse(Game game) {
do {
waitForResponse();
waitForResponse(game);
} while (response.getString() == null && !abort);
}
protected void waitForIntegerResponse() {
protected void waitForIntegerResponse(Game game) {
do {
waitForResponse();
waitForResponse(game);
} while (response.getInteger() == null && !abort);
}
@ -143,7 +147,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
.append(getHand().size() > nextHandSize?"down to ":"for free, draw ")
.append(nextHandSize)
.append(nextHandSize == 1?" card?":" cards?").toString());
waitForBooleanResponse();
waitForBooleanResponse(game);
if (!abort) {
return response.getBoolean();
}
@ -154,7 +158,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
public boolean chooseUse(Outcome outcome, String message, Game game) {
updateGameStatePriority("chooseUse", game);
game.fireAskPlayerEvent(playerId, message);
waitForBooleanResponse();
waitForBooleanResponse(game);
if (!abort) {
return response.getBoolean();
}
@ -175,7 +179,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
}
while (!abort) {
game.fireChooseEvent(playerId, replacementEffectChoice);
waitForResponse();
waitForResponse(game);
log.debug("Choose effect: " + response.getString());
if (response.getString() != null) {
replacementEffectChoice.setChoice(response.getString());
@ -196,7 +200,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
updateGameStatePriority("choose(3)", game);
while (!abort) {
game.fireChooseEvent(playerId, choice);
waitForResponse();
waitForResponse(game);
if (response.getString() != null) {
choice.setChoice(response.getString());
return true;
@ -218,7 +222,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
while (!abort) {
Set<UUID> cards = target.possibleTargets(null, playerId, game);
game.fireSelectTargetEvent(playerId, target.getMessage(), cards, target.isRequired(), options);
waitForResponse();
waitForResponse(game);
if (response.getUUID() != null) {
if (target instanceof TargetPermanent) {
if (((TargetPermanent)target).canTarget(playerId, response.getUUID(), sourceId, game, false)) {
@ -267,7 +271,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
Set<UUID> possibleTargets = target.possibleTargets(source==null?null:source.getSourceId(), playerId, game);
boolean required = possibleTargets.isEmpty() ? false : target.isRequired();
game.fireSelectTargetEvent(playerId, target.getMessage(), possibleTargets, required, getOptions(target));
waitForResponse();
waitForResponse(game);
if (response.getUUID() != null) {
if (possibleTargets.contains(response.getUUID())) {
if (target instanceof TargetPermanent) {
@ -323,7 +327,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
options.put("chosen", (Serializable)chosen);
}
game.fireSelectTargetEvent(playerId, target.getMessage(), cards, required, options);
waitForResponse();
waitForResponse(game);
if (response.getUUID() != null) {
if (target.canTarget(response.getUUID(), cards, game)) {
target.add(response.getUUID(), game);
@ -358,7 +362,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
}
}
game.fireSelectTargetEvent(playerId, target.getMessage(), cards, target.isRequired(), null);
waitForResponse();
waitForResponse(game);
if (response.getUUID() != null) {
if (target.canTarget(response.getUUID(), cards, game)) {
target.addTarget(response.getUUID(), source, game);
@ -383,7 +387,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
updateGameStatePriority("chooseTargetAmount", game);
while (!abort) {
game.fireSelectTargetEvent(playerId, target.getMessage() + "\n Amount remaining:" + target.getAmountRemaining(), target.possibleTargets(source==null?null:source.getId(), playerId, game), target.isRequired(), null);
waitForResponse();
waitForResponse(game);
if (response.getUUID() != null) {
if (target.canTarget(response.getUUID(), source, game)) {
UUID targetId = response.getUUID();
@ -412,7 +416,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
}
updateGameStatePriority("priority", game);
game.firePriorityEvent(playerId);
waitForResponse();
waitForResponse(game);
if (response.getBoolean() != null) {
pass(game);
return false;
@ -450,7 +454,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
updateGameStatePriority("chooseTriggeredAbility", game);
while (!abort) {
game.fireSelectTargetEvent(playerId, "Pick triggered ability (goes to the stack first)", abilities);
waitForResponse();
waitForResponse(game);
if (response.getUUID() != null) {
for (TriggeredAbility ability: abilities) {
if (ability.getId().equals(response.getUUID())) {
@ -466,7 +470,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
public boolean playMana(ManaCost unpaid, Game game) {
updateGameStatePriority("playMana", game);
game.firePlayManaEvent(playerId, "Pay " + unpaid.getText());
waitForResponse();
waitForResponse(game);
if (response.getBoolean() != null) {
return false;
} else if (response.getUUID() != null) {
@ -500,7 +504,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
public int announceXMana(int min, int max, String message, Game game, Ability ability) {
updateGameStatePriority("announceXMana", game);
game.fireGetAmountEvent(playerId, message, min, max);
waitForIntegerResponse();
waitForIntegerResponse(game);
return response.getInteger();
}
@ -530,7 +534,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
return;
}
game.fireSelectEvent(playerId, "Select attackers");
waitForResponse();
waitForResponse(game);
if (response.getBoolean() != null) {
return;
} else if (response.getInteger() != null) {
@ -593,7 +597,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
filter.add(new ControllerIdPredicate(defendingPlayerId));
while (!abort) {
game.fireSelectEvent(playerId, "Select blockers");
waitForResponse();
waitForResponse(game);
if (response.getBoolean() != null) {
return;
} else if (response.getInteger() != null) {
@ -622,7 +626,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
updateGameStatePriority("chooseAttackerOrder", game);
while (!abort) {
game.fireSelectTargetEvent(playerId, "Pick attacker", attackers, true);
waitForResponse();
waitForResponse(game);
if (response.getUUID() != null) {
for (Permanent perm: attackers) {
if (perm.getId().equals(response.getUUID())) {
@ -640,7 +644,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
updateGameStatePriority("chooseBlockerOrder", game);
while (!abort) {
game.fireSelectTargetEvent(playerId, "Pick blocker", blockers, true);
waitForResponse();
waitForResponse(game);
if (response.getUUID() != null) {
for (Permanent perm: blockers) {
if (perm.getId().equals(response.getUUID())) {
@ -656,7 +660,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
updateGameStatePriority("selectCombatGroup", game);
TargetAttackingCreature target = new TargetAttackingCreature();
game.fireSelectTargetEvent(playerId, "Select attacker to block", target.possibleTargets(null, playerId, game), target.isRequired(), null);
waitForResponse();
waitForResponse(game);
if (response.getBoolean() != null) {
// do nothing
} else if (response.getUUID() != null) {
@ -696,7 +700,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
public int getAmount(int min, int max, String message, Game game) {
updateGameStatePriority("getAmount", game);
game.fireGetAmountEvent(playerId, message, min, max);
waitForIntegerResponse();
waitForIntegerResponse(game);
return response.getInteger();
}
@ -719,7 +723,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
updateGameStatePriority("specialAction", game);
LinkedHashMap<UUID, SpecialAction> specialActions = game.getState().getSpecialActions().getControlledBy(playerId);
game.fireGetChoiceEvent(playerId, name, new ArrayList<SpecialAction>(specialActions.values()));
waitForResponse();
waitForResponse(game);
if (response.getUUID() != null) {
if (specialActions.containsKey(response.getUUID())) {
activateAbility(specialActions.get(response.getUUID()), game);
@ -737,7 +741,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
}
}
game.fireGetChoiceEvent(playerId, name, new ArrayList<ActivatedAbility>(abilities.values()));
waitForResponse();
waitForResponse(game);
if (response.getUUID() != null) {
if (abilities.containsKey(response.getUUID())) {
activateAbility(abilities.get(response.getUUID()), game);
@ -757,7 +761,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
return (SpellAbility) useableAbilities.values().iterator().next();
} else if (useableAbilities != null && useableAbilities.size() > 0) {
game.fireGetChoiceEvent(playerId, name, new ArrayList<ActivatedAbility>(useableAbilities.values()));
waitForResponse();
waitForResponse(game);
if (response.getUUID() != null) {
if (useableAbilities.containsKey(response.getUUID())) {
return (SpellAbility) useableAbilities.get(response.getUUID());
@ -785,7 +789,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
modeMap.put(mode.getId(), modeText);
}
game.fireGetModeEvent(playerId, "Choose Mode", modeMap);
waitForResponse();
waitForResponse(game);
if (response.getUUID() != null) {
for (Mode mode: modes.values()) {
if (mode.getId().equals(response.getUUID())) {
@ -802,7 +806,7 @@ public class HumanPlayer extends PlayerImpl<HumanPlayer> {
public boolean choosePile(Outcome outcome, String message, List<? extends Card> pile1, List<? extends Card> pile2, Game game) {
updateGameStatePriority("choosePile", game);
game.fireChoosePileEvent(playerId, message, pile1, pile2);
waitForBooleanResponse();
waitForBooleanResponse(game);
if (!abort) {
return response.getBoolean();
}

View file

@ -1,5 +1,5 @@
#default levels
log4j.rootLogger=info, console, logfile
log4j.rootLogger=warn, console, logfile
#console log
log4j.appender.console=org.apache.log4j.ConsoleAppender

View file

@ -28,7 +28,6 @@
package mage.server.game;
import mage.constants.Zone;
import mage.MageException;
import mage.abilities.Ability;
import mage.cards.Card;
@ -37,17 +36,21 @@ import mage.cards.decks.Deck;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.constants.Constants;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.GameException;
import mage.game.events.Listener;
import mage.game.events.PlayerQueryEvent;
import mage.game.events.TableEvent;
import mage.game.permanent.Permanent;
import mage.interfaces.Action;
import mage.players.Player;
import mage.server.*;
import mage.server.util.Splitter;
import mage.server.util.SystemUtil;
import mage.server.util.ThreadExecutor;
import mage.utils.timer.PriorityTimer;
import mage.view.AbilityPickerView;
import mage.view.CardsView;
import mage.view.ChatMessage.MessageColor;
@ -75,6 +78,8 @@ public class GameController implements GameCallback {
private ConcurrentHashMap<UUID, GameSession> gameSessions = new ConcurrentHashMap<UUID, GameSession>();
private ConcurrentHashMap<UUID, GameWatcher> watchers = new ConcurrentHashMap<UUID, GameWatcher>();
private ConcurrentHashMap<UUID, PriorityTimer> timers = new ConcurrentHashMap<UUID, PriorityTimer>();
private ConcurrentHashMap<UUID, UUID> userPlayerMap;
private UUID gameSessionId;
private Game game;
@ -106,6 +111,8 @@ public class GameController implements GameCallback {
@Override
public void event(TableEvent event) {
try {
PriorityTimer timer;
UUID playerId;
switch (event.getEventType()) {
case UPDATE:
updateGame();
@ -124,6 +131,44 @@ public class GameController implements GameCallback {
case ERROR:
error(event.getMessage(), event.getException());
break;
case INIT_TIMER:
final UUID initPlayerId = event.getPlayerId();
if (initPlayerId == null) {
throw new IllegalStateException("INIT_TIMER: playerId can't be null");
}
long delay = 250L; // run each 250 ms
timer = new PriorityTimer(Constants.PRIORITY_TIME_SEC, delay, new Action() {
@Override
public void execute() throws MageException {
game.concede(initPlayerId);
logger.info("Game timeout for player: " + initPlayerId + ". Conceding.");
}
});
timers.put(initPlayerId, timer);
timer.init();
break;
case RESUME_TIMER:
playerId = event.getPlayerId();
if (playerId == null) {
throw new IllegalStateException("RESUME_TIMER: playerId can't be null");
}
timer = timers.get(playerId);
if (timer == null) {
throw new IllegalStateException("RESUME_TIMER: couldn't find timer for player: " + playerId);
}
timer.resume();
break;
case PAUSE_TIMER:
playerId = event.getPlayerId();
if (playerId == null) {
throw new IllegalStateException("PAUSE_TIMER: playerId can't be null");
}
timer = timers.get(playerId);
if (timer == null) {
throw new IllegalStateException("PAUSE_TIMER: couldn't find timer for player: " + playerId);
}
timer.pause();
break;
}
} catch (MageException ex) {
logger.fatal("Table event listener error ", ex);
@ -388,6 +433,12 @@ public class GameController implements GameCallback {
}
private synchronized void updateGame() {
for (Player player: game.getState().getPlayers().values()) {
PriorityTimer timer = timers.get(player.getId());
if (timer != null) {
player.setPriorityTimeLeft(timer.getCount());
}
}
for (final GameSession gameSession: gameSessions.values()) {
gameSession.update();
}

View file

@ -0,0 +1,80 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.alarareborn;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.SacrificeSourceEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.UnearthAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.game.permanent.token.GoblinToken;
/**
*
* @author LevelX2
*/
public class KathariBomber extends CardImpl<KathariBomber> {
public KathariBomber(UUID ownerId) {
super(ownerId, 41, "Kathari Bomber", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{B}{R}");
this.expansionSetCode = "ARB";
this.subtype.add("Bird");
this.subtype.add("Shaman");
this.color.setRed(true);
this.color.setBlack(true);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// Flying
this.addAbility(FlyingAbility.getInstance());
// When Kathari Bomber deals combat damage to a player, put two 1/1 red Goblin creature tokens onto the battlefield and sacrifice Kathari Bomber.
Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new CreateTokenEffect(new GoblinToken(), 2), false);
ability.addEffect(new SacrificeSourceEffect());
this.addAbility(ability);
// Unearth {3}{B}{R}
this.addAbility(new UnearthAbility(new ManaCostsImpl("{3}{B}{R}")));
}
public KathariBomber(final KathariBomber card) {
super(card);
}
@Override
public KathariBomber copy() {
return new KathariBomber(this);
}
}

View file

@ -62,6 +62,7 @@ public class Earthshaker extends CardImpl<Earthshaker> {
this.color.setRed(true);
this.power = new MageInt(4);
this.toughness = new MageInt(5);
// Whenever you cast a Spirit or Arcane spell, Earthshaker deals 2 damage to each creature without flying.
this.addAbility(new SpellCastTriggeredAbility(new DamageAllEffect(new StaticValue(2) , creatureFilter), filter, false));
}

View file

@ -0,0 +1,108 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.conflux;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.common.AttacksOrBlocksTriggeredAbility;
import mage.abilities.common.delayed.AtEndOfTurnDelayedTriggeredAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.SacrificeTargetEffect;
import mage.abilities.keyword.UnearthAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author LevelX2
*/
public class BrackwaterElemental extends CardImpl<BrackwaterElemental> {
public BrackwaterElemental(UUID ownerId) {
super(ownerId, 21, "Brackwater Elemental", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{U}");
this.expansionSetCode = "CON";
this.subtype.add("Elemental");
this.color.setBlue(true);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// When Brackwater Elemental attacks or blocks, sacrifice it at the beginning of the next end step.
this.addAbility(new AttacksOrBlocksTriggeredAbility(new BrackwaterElementalSacrificeEffect(), false));
// Unearth {2}{U}
this.addAbility(new UnearthAbility(new ManaCostsImpl("{2}{U}")));
}
public BrackwaterElemental(final BrackwaterElemental card) {
super(card);
}
@Override
public BrackwaterElemental copy() {
return new BrackwaterElemental(this);
}
}
class BrackwaterElementalSacrificeEffect extends OneShotEffect<BrackwaterElementalSacrificeEffect> {
public BrackwaterElementalSacrificeEffect() {
super(Outcome.Sacrifice);
this.staticText = "sacrifice it at the beginning of the next end step";
}
public BrackwaterElementalSacrificeEffect(final BrackwaterElementalSacrificeEffect effect) {
super(effect);
}
@Override
public BrackwaterElementalSacrificeEffect copy() {
return new BrackwaterElementalSacrificeEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
if (sourcePermanent != null) {
SacrificeTargetEffect sacrificeEffect = new SacrificeTargetEffect("sacrifice {this}");
sacrificeEffect.setTargetPointer(new FixedTarget(sourcePermanent.getId()));
DelayedTriggeredAbility delayedAbility = new AtEndOfTurnDelayedTriggeredAbility(sacrificeEffect);
delayedAbility.setSourceId(source.getSourceId());
delayedAbility.setControllerId(source.getControllerId());
game.addDelayedTriggeredAbility(delayedAbility);
}
return false;
}
}

View file

@ -32,8 +32,7 @@ import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.DiscardControllerEffect;
import mage.abilities.effects.common.DrawCardControllerEffect;
import mage.abilities.effects.common.DrawDiscardControllerEffect;
import mage.abilities.keyword.FlashbackAbility;
import mage.cards.CardImpl;
import mage.constants.TimingRule;
@ -51,8 +50,7 @@ public class FaithlessLooting extends CardImpl<FaithlessLooting> {
this.color.setRed(true);
// Draw two cards, then discard two cards.
this.getSpellAbility().addEffect(new DrawCardControllerEffect(2));
this.getSpellAbility().addEffect(new DiscardControllerEffect(2));
this.getSpellAbility().addEffect(new DrawDiscardControllerEffect(2,2));
// Flashback {2}{R}
this.addAbility(new FlashbackAbility(new ManaCostsImpl("{2}{R}"), TimingRule.SORCERY));
}

View file

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

View file

@ -35,8 +35,8 @@ import mage.abilities.Ability;
import mage.abilities.StateTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SkipUntapOptionalAbility;
import mage.abilities.condition.Condition;
import mage.abilities.condition.common.ControlsPermanentCondition;
import mage.abilities.condition.common.SourceTappedCondition;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.decorator.ConditionalContinousEffect;
import mage.abilities.effects.common.SacrificeSourceEffect;
@ -89,7 +89,7 @@ public class Seasinger extends CardImpl<Seasinger> {
this.addAbility(new SkipUntapOptionalAbility());
// {tap}: Gain control of target creature whose controller controls an Island for as long as you control Seasinger and Seasinger remains tapped.
ConditionalContinousEffect effect = new ConditionalContinousEffect(new GainControlTargetEffect(Duration.Custom), new ControlsPermanentCondition(seasinger, ControlsPermanentCondition.CountType.EQUAL_TO, 1, TappedCondition.getInstance()), rule);
ConditionalContinousEffect effect = new ConditionalContinousEffect(new GainControlTargetEffect(Duration.Custom), new ControlsPermanentCondition(seasinger, ControlsPermanentCondition.CountType.EQUAL_TO, 1, SourceTappedCondition.getInstance()), rule);
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost());
creatureWhoseControllerControlsIsland.add(new ControllerControlsIslandPredicate());
ability.addTarget(new TargetCreaturePermanent(creatureWhoseControllerControlsIsland));
@ -148,21 +148,3 @@ class SeasingerTriggeredAbility extends StateTriggeredAbility<SeasingerTriggered
return "When you control no islands, sacrifice {this}.";
}
}
class TappedCondition implements Condition {
private static TappedCondition fInstance = new TappedCondition();
public static Condition getInstance() {
return fInstance;
}
@Override
public boolean apply(Game game, Ability source) {
Permanent seasinger = game.getBattlefield().getPermanent(source.getSourceId());
if (seasinger != null) {
return seasinger.isTapped();
}
return false;
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -28,14 +28,13 @@
package mage.sets.innistrad;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.common.FilterNonlandCard;
import mage.game.Game;
@ -98,7 +97,7 @@ class NightTerrorsEffect extends OneShotEffect<NightTerrorsEffect> {
if (player.choose(Outcome.Exile, targetPlayer.getHand(), target, game)) {
Card card = targetPlayer.getHand().get(target.getFirstTarget(), game);
if (card != null) {
card.moveToExile(null, "", source.getId(), game);
card.moveToExile(null, "", source.getSourceId(), game);
}
}

View file

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

View file

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

View file

@ -29,9 +29,6 @@
package mage.sets.mirrodinbesieged;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.MageInt;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
@ -41,9 +38,13 @@ import mage.abilities.effects.common.continious.BoostSourceEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.HasteAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Rarity;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.common.FilterArtifactPermanent;
import mage.filter.predicate.permanent.ControllerPredicate;
/**
*
@ -51,6 +52,11 @@ import mage.filter.common.FilterArtifactPermanent;
*/
public class HellkiteIgniter extends CardImpl<HellkiteIgniter> {
private static final FilterArtifactPermanent filter = new FilterArtifactPermanent("artifact you control");
static {
filter.add(new ControllerPredicate(TargetController.YOU));
}
public HellkiteIgniter (UUID ownerId) {
super(ownerId, 65, "Hellkite Igniter", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{5}{R}{R}");
this.expansionSetCode = "MBS";
@ -58,11 +64,14 @@ public class HellkiteIgniter extends CardImpl<HellkiteIgniter> {
this.color.setRed(true);
this.power = new MageInt(5);
this.toughness = new MageInt(5);
// Flying, haste
this.addAbility(FlyingAbility.getInstance());
this.addAbility(HasteAbility.getInstance());
// {1}{R}: Hellkite Igniter gets +X/+0 until end of turn, where X is the number of artifacts you control.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD,
new BoostSourceEffect(
new PermanentsOnBattlefieldCount(new FilterArtifactPermanent()),
new PermanentsOnBattlefieldCount(filter),
new StaticValue(0),
Duration.EndOfTurn),
new ManaCostsImpl("{1}{R}")));

View file

@ -28,12 +28,13 @@
package mage.sets.mirrodinbesieged;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.MageInt;
import mage.abilities.common.SpellCastTriggeredAbility;
import mage.abilities.effects.common.UntapTargetEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.filter.FilterSpell;
import mage.target.common.TargetCreaturePermanent;
@ -53,6 +54,11 @@ public class MirranSpy extends CardImpl<MirranSpy> {
this.color.setBlue(true);
this.power = new MageInt(1);
this.toughness = new MageInt(3);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Whenever you cast an artifact spell, you may untap target creature.
SpellCastTriggeredAbility ability = new SpellCastTriggeredAbility(new UntapTargetEffect(), filter, true);
ability.addTarget(new TargetCreaturePermanent());
this.addAbility(ability);

View file

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

View file

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

View file

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

View file

@ -0,0 +1,67 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.modernmasters;
import java.util.UUID;
import mage.abilities.dynamicvalue.common.ManacostVariableValue;
import mage.abilities.effects.common.CounterUnlessPaysEffect;
import mage.abilities.keyword.DelveAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.target.TargetSpell;
/**
*
* @author LevelX2
*/
public class LogicKnot extends CardImpl<LogicKnot> {
public LogicKnot(UUID ownerId) {
super(ownerId, 51, "Logic Knot", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{X}{U}{U}");
this.expansionSetCode = "MMA";
this.color.setBlue(true);
// Delve
this.addAbility(new DelveAbility());
// Counter target spell unless its controller pays {X}.
this.getSpellAbility().addEffect(new CounterUnlessPaysEffect(new ManacostVariableValue()));
this.getSpellAbility().addTarget(new TargetSpell());
}
public LogicKnot(final LogicKnot card) {
super(card);
}
@Override
public LogicKnot copy() {
return new LogicKnot(this);
}
}

View file

@ -0,0 +1,152 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.modernmasters;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.condition.common.KickedCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continious.GainAbilitySourceEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.KickerAbility;
import mage.abilities.keyword.SplitSecondAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.AbilityPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author LevelX2
*/
public class MoltenDisaster extends CardImpl<MoltenDisaster> {
public MoltenDisaster(UUID ownerId) {
super(ownerId, 123, "Molten Disaster", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{X}{R}{R}");
this.expansionSetCode = "MMA";
this.color.setRed(true);
// If Molten Disaster was kicked, it has split second.
Ability ability = new ConditionalTriggeredAbility(new MoltenDisasterTriggeredAbility(), KickedCondition.getInstance(), "");
ability.setRuleAtTheTop(true);
this.addAbility(ability);
// Kicker {R}
this.addAbility(new KickerAbility("{R}"));
// Molten Disaster deals X damage to each creature without flying and each player.
this.getSpellAbility().addEffect(new MoltenDisasterEffect());
}
public MoltenDisaster(final MoltenDisaster card) {
super(card);
}
@Override
public MoltenDisaster copy() {
return new MoltenDisaster(this);
}
}
class MoltenDisasterTriggeredAbility extends TriggeredAbilityImpl<MoltenDisasterTriggeredAbility> {
public MoltenDisasterTriggeredAbility() {
super(Zone.HAND, new GainAbilitySourceEffect(SplitSecondAbility.getInstance(), Duration.WhileOnStack), false);
}
public MoltenDisasterTriggeredAbility(final MoltenDisasterTriggeredAbility ability) {
super(ability);
}
@Override
public MoltenDisasterTriggeredAbility copy() {
return new MoltenDisasterTriggeredAbility(this);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getType().equals(GameEvent.EventType.CAST_SPELL) && event.getSourceId().equals(this.getSourceId())) {
return true;
}
return false;
}
@Override
public String getRule() {
return "If Molten Disaster was kicked, it has split second <i>(As long as this spell is on the stack, players can't cast spells or activate abilities that aren't mana abilities.)</i>";
}
}
class MoltenDisasterEffect extends OneShotEffect<MoltenDisasterEffect> {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
static {
filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class)));
}
public MoltenDisasterEffect() {
super(Outcome.Damage);
staticText = "{this} deals X damage to each creature without flying and each player";
}
public MoltenDisasterEffect(final MoltenDisasterEffect effect) {
super(effect);
}
@Override
public MoltenDisasterEffect copy() {
return new MoltenDisasterEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
int amount = source.getManaCostsToPay().getX();
for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) {
permanent.damage(amount, source.getId(), game, true, false);
}
for (UUID playerId: game.getPlayer(source.getControllerId()).getInRange()) {
Player player = game.getPlayer(playerId);
if (player != null) {
player.damage(amount, source.getSourceId(), game, false, true);
}
}
return true;
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.modernmasters;
import java.util.UUID;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.continious.BoostEquippedEffect;
import mage.abilities.effects.common.continious.GainAbilityAttachedEffect;
import mage.abilities.keyword.ChangelingAbility;
import mage.abilities.keyword.EquipAbility;
import mage.cards.CardImpl;
import mage.constants.AttachmentType;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
/**
*
* @author LevelX2
*/
public class RunedStalactite extends CardImpl<RunedStalactite> {
public RunedStalactite(UUID ownerId) {
super(ownerId, 214, "Runed Stalactite", Rarity.COMMON, new CardType[]{CardType.ARTIFACT}, "{1}");
this.expansionSetCode = "MMA";
this.subtype.add("Equipment");
// Equipped creature gets +1/+1 and is every creature type.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(1,1)));
Effect effect = new GainAbilityAttachedEffect(ChangelingAbility.getInstance(), AttachmentType.EQUIPMENT, Duration.WhileOnBattlefield);
effect.setText("Equipped creature is every creature type (Changeling)");
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect));
// Equip {2}
this.addAbility(new EquipAbility(Outcome.BoostCreature, new ManaCostsImpl("{2}")));
}
public RunedStalactite(final RunedStalactite card) {
super(card);
}
@Override
public RunedStalactite copy() {
return new RunedStalactite(this);
}
}

View file

@ -0,0 +1,83 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.modernmasters;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DamageAllEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.TargetController;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.filter.predicate.permanent.ControllerPredicate;
/**
*
* @author LevelX2
*/
public class ThundercloudShaman extends CardImpl<ThundercloudShaman> {
private static final FilterCreaturePermanent filterGiants = new FilterCreaturePermanent("equal to the number of Giants you control");
private static final FilterCreaturePermanent filterNonGiants = new FilterCreaturePermanent("non-Giant creature");
static {
filterGiants.add(new ControllerPredicate(TargetController.YOU));
filterGiants.add(new SubtypePredicate("Giant"));
filterNonGiants.add(Predicates.not(new SubtypePredicate("Giant")));
}
public ThundercloudShaman(UUID ownerId) {
super(ownerId, 135, "Thundercloud Shaman", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{R}{R}");
this.expansionSetCode = "MMA";
this.subtype.add("Giant");
this.subtype.add("Shaman");
this.color.setRed(true);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// When Thundercloud Shaman enters the battlefield, it deals damage equal to the number of Giants you control to each non-Giant creature.
Effect effect = new DamageAllEffect(new PermanentsOnBattlefieldCount(filterGiants),filterNonGiants);
effect.setText("it deals damage equal to the number of Giants you control to each non-Giant creature");
this.addAbility(new EntersBattlefieldTriggeredAbility(effect, false));
}
public ThundercloudShaman(final ThundercloudShaman card) {
super(card);
}
@Override
public ThundercloudShaman copy() {
return new ThundercloudShaman(this);
}
}

View file

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

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.sets.modernmasters;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.keyword.DelveAbility;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
/**
*
* @author LevelX2
*/
public class Tombstalker extends CardImpl<Tombstalker> {
public Tombstalker(UUID ownerId) {
super(ownerId, 102, "Tombstalker", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{6}{B}{B}");
this.expansionSetCode = "MMA";
this.subtype.add("Demon");
this.color.setBlack(true);
this.power = new MageInt(5);
this.toughness = new MageInt(5);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Delve
Ability ability = new DelveAbility();
ability.setRuleAtTheTop(false);
this.addAbility(ability);
}
public Tombstalker(final Tombstalker card) {
super(card);
}
@Override
public Tombstalker copy() {
return new Tombstalker(this);
}
}

View file

@ -0,0 +1,121 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.modernmasters;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game;
import mage.game.events.DamagedPlayerEvent;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.players.Player;
import mage.target.TargetPermanent;
/**
*
* @author LevelX2
*/
public class TrygonPredator extends CardImpl<TrygonPredator> {
public TrygonPredator(UUID ownerId) {
super(ownerId, 185, "Trygon Predator", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{G}{U}");
this.expansionSetCode = "MMA";
this.subtype.add("Beast");
this.color.setBlue(true);
this.color.setGreen(true);
this.power = new MageInt(2);
this.toughness = new MageInt(3);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Whenever Trygon Predator deals combat damage to a player, you may destroy target artifact or enchantment that player controls.
this.addAbility(new TrygonPredatorTriggeredAbility());
}
public TrygonPredator(final TrygonPredator card) {
super(card);
}
@Override
public TrygonPredator copy() {
return new TrygonPredator(this);
}
}
class TrygonPredatorTriggeredAbility extends TriggeredAbilityImpl<TrygonPredatorTriggeredAbility> {
public TrygonPredatorTriggeredAbility() {
super(Zone.BATTLEFIELD, new DestroyTargetEffect(), true);
}
public TrygonPredatorTriggeredAbility(final TrygonPredatorTriggeredAbility ability) {
super(ability);
}
@Override
public TrygonPredatorTriggeredAbility copy() {
return new TrygonPredatorTriggeredAbility(this);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getType() == EventType.DAMAGED_PLAYER && event.getSourceId().equals(this.sourceId)
&& ((DamagedPlayerEvent) event).isCombatDamage()) {
Player player = game.getPlayer(event.getTargetId());
if (player != null) {
FilterPermanent filter = new FilterPermanent("an artifact or enchantment controlled by " + player.getName());
filter.add(Predicates.or(
new CardTypePredicate(CardType.ARTIFACT),
new CardTypePredicate(CardType.ENCHANTMENT)));
filter.add(new ControllerIdPredicate(event.getTargetId()));
this.getTargets().clear();
this.addTarget(new TargetPermanent(filter));
return true;
}
}
return false;
}
@Override
public String getRule() {
return "Whenever {this} deals combat damage to a player, you may destroy target artifact or enchantment that player controls.";
}
}

View file

@ -0,0 +1,113 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.modernmasters;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SkipUntapOptionalAbility;
import mage.abilities.condition.common.SourceTappedCondition;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.decorator.ConditionalContinousEffect;
import mage.abilities.effects.common.continious.GainControlTargetEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.common.FilterLandPermanent;
import mage.filter.predicate.ObjectPlayer;
import mage.filter.predicate.ObjectPlayerPredicate;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author LevelX2
*/
public class VedalkenShackles extends CardImpl<VedalkenShackles> {
private static final FilterCreaturePermanent controllableCreatures = new FilterCreaturePermanent("creature with power less than or equal to the number of Islands you control");
static {
controllableCreatures.add(new PowerIslandPredicate());
}
public VedalkenShackles(UUID ownerId) {
super(ownerId, 218, "Vedalken Shackles", Rarity.MYTHIC, new CardType[]{CardType.ARTIFACT}, "{3}");
this.expansionSetCode = "MMA";
// You may choose not to untap Vedalken Shackles during your untap step.
this.addAbility(new SkipUntapOptionalAbility());
// {2}, {tap}: Gain control of target creature with power less than or equal to the number of Islands you control for as long as Vedalken Shackles remains tapped.
ConditionalContinousEffect effect = new ConditionalContinousEffect(
new GainControlTargetEffect(Duration.Custom), SourceTappedCondition.getInstance(),
"Gain control of target creature with power less than or equal to the number of Islands you control for as long as {this} remains tapped");
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost());
ability.addTarget(new TargetCreaturePermanent(controllableCreatures));
this.addAbility(ability);
}
public VedalkenShackles(final VedalkenShackles card) {
super(card);
}
@Override
public VedalkenShackles copy() {
return new VedalkenShackles(this);
}
}
class PowerIslandPredicate implements ObjectPlayerPredicate<ObjectPlayer<Permanent>> {
public static final FilterLandPermanent filter = new FilterLandPermanent("Island");
static {
filter.add(new SubtypePredicate("Island"));
}
@Override
public boolean apply(ObjectPlayer<Permanent> input, Game game) {
Permanent permanent = input.getObject();
if (permanent != null) {
int islands = game.getBattlefield().countAll(filter, input.getPlayerId(), game);
if (permanent.getPower().getValue() <= islands) {
return true;
}
}
return false;
}
@Override
public String toString() {
return "creature with power less than or equal to the number of Islands you control";
}
}

View file

@ -36,11 +36,13 @@ import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.constants.TurnPhase;
import mage.game.Game;
import mage.game.turn.TurnMod;
import mage.players.Player;
/**
*
@ -80,15 +82,15 @@ class SkipNextCombatEffect extends OneShotEffect<SkipNextCombatEffect> {
staticText = "that player skips his or her next combat phase";
}
public SkipNextCombatEffect(SkipNextCombatEffect effect) {
public SkipNextCombatEffect(final SkipNextCombatEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
UUID targetId = source.getFirstTarget();
if (targetId != null) {
game.getState().getTurnMods().add(new TurnMod(targetId, TurnPhase.COMBAT, null, true));
Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
if (player != null) {
game.getState().getTurnMods().add(new TurnMod(player.getId(), TurnPhase.COMBAT, null, true));
return true;
}
return false;
@ -96,6 +98,6 @@ class SkipNextCombatEffect extends OneShotEffect<SkipNextCombatEffect> {
@Override
public SkipNextCombatEffect copy() {
return new SkipNextCombatEffect();
return new SkipNextCombatEffect(this);
}
}

View file

@ -29,23 +29,22 @@
package mage.sets.newphyrexia;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.ExileFromGraveCost;
import mage.abilities.costs.common.RemoveCountersSourceCost;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.filter.common.FilterArtifactCard;
import mage.game.permanent.token.GolemToken;
import mage.target.common.TargetCardInYourGraveyard;
import mage.target.common.TargetCardInGraveyard;
/**
*
@ -56,10 +55,13 @@ public class ConversionChamber extends CardImpl<ConversionChamber> {
public ConversionChamber (UUID ownerId) {
super(ownerId, 133, "Conversion Chamber", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT}, "{3}");
this.expansionSetCode = "NPH";
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.CHARGE.createInstance()), new GenericManaCost(2));
// {2}, {T}: Exile target artifact card from a graveyard. Put a charge counter on Conversion Chamber.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), new GenericManaCost(2));
ability.addEffect(new AddCountersSourceEffect(CounterType.CHARGE.createInstance()));
ability.addTarget(new TargetCardInGraveyard(new FilterArtifactCard("artifact card from a graveyard")));
ability.addCost(new TapSourceCost());
ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(new FilterArtifactCard("artifact card from a graveyard"))));
this.addAbility(ability);
// {2}, {T}, Remove a charge counter from Conversion Chamber: Put a 3/3 colorless Golem artifact creature token onto the battlefield.
ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new GolemToken()), new GenericManaCost(2));
ability.addCost(new TapSourceCost());
ability.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance()));

View file

@ -28,15 +28,16 @@
package mage.sets.newphyrexia;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.MageInt;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.continious.BoostSourceEffect;
import mage.abilities.keyword.InfectAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Rarity;
import mage.constants.Zone;
/**
*
@ -52,7 +53,10 @@ public class OgreMenial extends CardImpl<OgreMenial> {
this.color.setRed(true);
this.power = new MageInt(0);
this.toughness = new MageInt(4);
// Infect (This creature deals damage to creatures in the form of -1/-1 counters and to players in the form of poison counters.)
this.addAbility(InfectAbility.getInstance());
// {R}: Ogre Menial gets +1/+0 until end of turn.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{R}")));
}

View file

@ -27,13 +27,11 @@
*/
package mage.sets.odyssey;
import java.util.UUID;
import mage.abilities.effects.common.DrawDiscardControllerEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.effects.common.DiscardControllerEffect;
import mage.abilities.effects.common.DrawCardControllerEffect;
import mage.cards.CardImpl;
import java.util.UUID;
/**
* @author magenoxx_at_gmail.com
@ -47,8 +45,7 @@ public class CarefulStudy extends CardImpl<CarefulStudy> {
this.color.setBlue(true);
// Draw two cards, then discard two cards.
this.getSpellAbility().addEffect(new DrawCardControllerEffect(2));
this.getSpellAbility().addEffect(new DiscardControllerEffect(2));
this.getSpellAbility().addEffect(new DrawDiscardControllerEffect(2,2));
}
public CarefulStudy(final CarefulStudy card) {

View file

@ -56,7 +56,7 @@ public class EtchedOracle extends CardImpl<EtchedOracle> {
this.power = new MageInt(0);
this.toughness = new MageInt(0);
// Sunburst
// Sunburst (This enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.)
this.addAbility(new SunburstAbility());
// {1}, Remove four +1/+1 counters from Etched Oracle: Target player draws three cards.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardTargetEffect(3), new ManaCostsImpl("{1}"));

View file

@ -28,11 +28,6 @@
package mage.sets.returntoravnica;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
@ -41,6 +36,10 @@ import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
import mage.filter.common.FilterCreaturePermanent;
@ -52,6 +51,7 @@ import mage.game.permanent.Permanent;
import mage.target.Target;
import mage.target.common.TargetCardInGraveyard;
import mage.target.common.TargetCreaturePermanent;
import mage.util.CardUtil;
/**
*
@ -156,7 +156,7 @@ class AngelOfSerenityEnterEffect extends OneShotEffect<AngelOfSerenityEnterEffec
for (UUID permanentId : target.getTargets()) {
Permanent permanent = game.getPermanent(permanentId);
if (permanent != null) {
result |= permanent.moveToExile(source.getSourceId(), "Angel of Serenity", source.getId(), game);
result |= permanent.moveToExile(CardUtil.getCardExileZoneId(game, source), "Angel of Serenity", source.getSourceId(), game);
}
}
@ -164,7 +164,7 @@ class AngelOfSerenityEnterEffect extends OneShotEffect<AngelOfSerenityEnterEffec
for (UUID cardId : target.getTargets()) {
Card card = game.getCard(cardId);
if (card != null) {
result |= card.moveToExile(source.getSourceId(), "Angel of Serenity", source.getId(), game);
result |= card.moveToExile(CardUtil.getCardExileZoneId(game, source), "Angel of Serenity", source.getSourceId(), game);
}
}
}
@ -192,15 +192,15 @@ class AngelOfSerenityLeaveEffect extends OneShotEffect<AngelOfSerenityLeaveEffec
@Override
public boolean apply(Game game, Ability source) {
ExileZone exZone = game.getExile().getExileZone(source.getSourceId());
ExileZone exZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source));
if (exZone != null) {
for (Card card : exZone.getCards(game)) {
if (card != null) {
card.moveToZone(Zone.HAND, source.getId(), game, false);
card.moveToZone(Zone.HAND, source.getSourceId(), game, false);
}
}
return true;
}
return false;
}
}
}

View file

@ -34,8 +34,7 @@ import mage.abilities.Mode;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.common.CounterUnlessPaysEffect;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.effects.common.DiscardControllerEffect;
import mage.abilities.effects.common.DrawCardControllerEffect;
import mage.abilities.effects.common.DrawDiscardControllerEffect;
import mage.cards.CardImpl;
import mage.filter.FilterSpell;
import mage.filter.predicate.Predicates;
@ -73,8 +72,7 @@ public class IzzetCharm extends CardImpl<IzzetCharm> {
// or draw two cards, then discard two cards.
mode = new Mode();
mode.getEffects().add(new DrawCardControllerEffect(2));
mode.getEffects().add(new DiscardControllerEffect(2));
mode.getEffects().add(new DrawDiscardControllerEffect(2, 2));
this.getSpellAbility().addMode(mode);
}

View file

@ -28,10 +28,9 @@
package mage.sets.returntoravnica;
import java.util.UUID;
import mage.abilities.effects.common.DrawDiscardControllerEffect;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.effects.common.DiscardControllerEffect;
import mage.abilities.effects.common.DrawCardControllerEffect;
import mage.cards.CardImpl;
/**
@ -47,8 +46,7 @@ public class Thoughtflare extends CardImpl<Thoughtflare> {
this.color.setRed(true);
// Draw four cards, then discard two cards.
this.getSpellAbility().addEffect(new DrawCardControllerEffect(4));
this.getSpellAbility().addEffect(new DiscardControllerEffect(2));
this.getSpellAbility().addEffect(new DrawDiscardControllerEffect(4, 2));
}
public Thoughtflare(final Thoughtflare card) {

View file

@ -28,12 +28,6 @@
package mage.sets.scarsofmirrodin;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.*;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.MageInt;
import mage.Mana;
import mage.abilities.Ability;
@ -51,6 +45,7 @@ import mage.abilities.effects.common.UntapTargetEffect;
import mage.abilities.effects.common.continious.BecomesCreatureTargetEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.cards.CardImpl;
import mage.constants.*;
import mage.counters.CounterType;
import mage.filter.common.FilterLandPermanent;
import mage.filter.predicate.mageobject.SubtypePredicate;
@ -62,6 +57,8 @@ import mage.game.permanent.token.Token;
import mage.target.common.TargetCreatureOrPlayer;
import mage.target.common.TargetLandPermanent;
import java.util.UUID;
/**
*
* @author Loki, North
@ -95,7 +92,7 @@ public class KothOfTheHammer extends CardImpl<KothOfTheHammer> {
// -2: Add {R} to your mana pool for each Mountain you control.
this.addAbility(new LoyaltyAbility(new DynamicManaEffect(Mana.RedMana, new PermanentsOnBattlefieldCount(filterCount)), -2));
// -5: You get an emblem with "Mountains you control have {T}: This land deals 1 damage to target creature or player.'
// -5: You get an emblem with "Mountains you control have '{T}: This land deals 1 damage to target creature or player.'
this.addAbility(new LoyaltyAbility(new GetEmblemEffect(new KothOfTheHammerEmblem()), -5));
}

View file

@ -0,0 +1,145 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.shadowmoor;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.HasteAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.predicate.mageobject.NamePredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.players.Player;
/**
*
* @author LevelX2
*/
public class DemigodOfRevenge extends CardImpl<DemigodOfRevenge> {
public DemigodOfRevenge(UUID ownerId) {
super(ownerId, 183, "Demigod of Revenge", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{B/R}{B/R}{B/R}{B/R}{B/R}");
this.expansionSetCode = "SHM";
this.subtype.add("Spirit");
this.subtype.add("Avatar");
this.color.setRed(true);
this.color.setBlack(true);
this.power = new MageInt(5);
this.toughness = new MageInt(4);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Haste
this.addAbility(HasteAbility.getInstance());
// When you cast Demigod of Revenge, return all cards named Demigod of Revenge from your graveyard to the battlefield.
this.addAbility(new DemigodOfRevengeTriggeredAbility());
}
public DemigodOfRevenge(final DemigodOfRevenge card) {
super(card);
}
@Override
public DemigodOfRevenge copy() {
return new DemigodOfRevenge(this);
}
}
class DemigodOfRevengeTriggeredAbility extends TriggeredAbilityImpl<DemigodOfRevengeTriggeredAbility> {
public DemigodOfRevengeTriggeredAbility() {
super(Zone.STACK, new DemigodOfRevengeReturnEffect(), false);
}
public DemigodOfRevengeTriggeredAbility(final DemigodOfRevengeTriggeredAbility ability) {
super(ability);
}
@Override
public DemigodOfRevengeTriggeredAbility copy() {
return new DemigodOfRevengeTriggeredAbility(this);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getType().equals(GameEvent.EventType.SPELL_CAST) && event.getSourceId().equals(this.getSourceId())) {
return true;
}
return false;
}
@Override
public String getRule() {
return "When you cast Demigod of Revenge, " + super.getRule();
}
}
class DemigodOfRevengeReturnEffect extends OneShotEffect<DemigodOfRevengeReturnEffect> {
private static final FilterCard filter = new FilterCard();
static {
filter.add(new NamePredicate("Demigod of Revenge"));
}
public DemigodOfRevengeReturnEffect() {
super(Outcome.PutCreatureInPlay);
staticText = "return all cards named Demigod of Revenge from your graveyard to the battlefield";
}
public DemigodOfRevengeReturnEffect(final DemigodOfRevengeReturnEffect effect) {
super(effect);
}
@Override
public DemigodOfRevengeReturnEffect copy() {
return new DemigodOfRevengeReturnEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
for (Card creature : player.getGraveyard().getCards(filter, game)) {
creature.putOntoBattlefield(game, Zone.GRAVEYARD, source.getId(), source.getControllerId());
}
return true;
}
return false;
}
}

View file

@ -0,0 +1,155 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.shardsofalara;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.common.FilterNonlandCard;
import mage.game.ExileZone;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetOpponent;
import mage.util.CardUtil;
/**
*
* @author LevelX2
*/
public class TidehollowSculler extends CardImpl<TidehollowSculler> {
public TidehollowSculler(UUID ownerId) {
super(ownerId, 202, "Tidehollow Sculler", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{W}{B}");
this.expansionSetCode = "ALA";
this.subtype.add("Zombie");
this.color.setBlack(true);
this.color.setWhite(true);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// When Tidehollow Sculler enters the battlefield, target opponent reveals his or her hand and you choose a nonland card from it. Exile that card.
Ability ability = new EntersBattlefieldTriggeredAbility(new TidehollowScullerExileEffect(), false);
ability.addTarget(new TargetOpponent(true));
this.addAbility(ability);
// When Tidehollow Sculler leaves the battlefield, return the exiled card to its owner's hand.
this.addAbility(new LeavesBattlefieldTriggeredAbility(new TidehollowScullerLeaveEffect(), false ));
}
public TidehollowSculler(final TidehollowSculler card) {
super(card);
}
@Override
public TidehollowSculler copy() {
return new TidehollowSculler(this);
}
}
class TidehollowScullerExileEffect extends OneShotEffect<TidehollowScullerExileEffect> {
public TidehollowScullerExileEffect() {
super(Outcome.Exile);
this.staticText = "target opponent reveals his or her hand and you choose a nonland card from it. Exile that card";
}
public TidehollowScullerExileEffect(final TidehollowScullerExileEffect effect) {
super(effect);
}
@Override
public TidehollowScullerExileEffect copy() {
return new TidehollowScullerExileEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
Player targetPlayer = game.getPlayer(source.getFirstTarget());
if (player != null && targetPlayer != null) {
targetPlayer.revealCards("Tidehollow Sculler", targetPlayer.getHand(), game);
TargetCard target = new TargetCard(Zone.PICK, new FilterNonlandCard("nonland card to exile"));
target.setRequired(true);
if (player.choose(Outcome.Exile, targetPlayer.getHand(), target, game)) {
Card card = targetPlayer.getHand().get(target.getFirstTarget(), game);
if (card != null) {
card.moveToExile(CardUtil.getCardExileZoneId(game, source), "Tidehollow Sculler", source.getSourceId(), game);
}
}
return true;
}
return false;
}
}
class TidehollowScullerLeaveEffect extends OneShotEffect<TidehollowScullerLeaveEffect> {
public TidehollowScullerLeaveEffect() {
super(Outcome.ReturnToHand);
this.staticText = "return the exiled card to its owner's hand";
}
public TidehollowScullerLeaveEffect(final TidehollowScullerLeaveEffect effect) {
super(effect);
}
@Override
public TidehollowScullerLeaveEffect copy() {
return new TidehollowScullerLeaveEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
ExileZone exZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source));
if (exZone != null) {
for (Card card : exZone.getCards(game)) {
if (card != null) {
card.moveToZone(Zone.HAND, source.getId(), game, false);
}
}
return true;
}
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.sets.tempest;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.DrawDiscardControllerEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.Zone;
/**
*
* @author LevelX2
*/
public class EmmessiTome extends CardImpl<EmmessiTome> {
public EmmessiTome(UUID ownerId) {
super(ownerId, 274, "Emmessi Tome", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{4}");
this.expansionSetCode = "TMP";
// {5}, {tap}: Draw two cards, then discard a card.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawDiscardControllerEffect(2,1), new ManaCostsImpl("{5}"));
ability.addCost(new TapSourceCost());
this.addAbility(ability);
}
public EmmessiTome(final EmmessiTome card) {
super(card);
}
@Override
public EmmessiTome copy() {
return new EmmessiTome(this);
}
}

View file

@ -31,7 +31,7 @@ import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.effects.common.DiscardControllerEffect;
import mage.abilities.effects.common.DrawCardControllerEffect;
import mage.abilities.effects.common.DrawDiscardControllerEffect;
import mage.cards.CardImpl;
/**
@ -47,8 +47,7 @@ public class Sift extends CardImpl<Sift> {
this.color.setBlue(true);
// Draw three cards, then discard a card.
this.getSpellAbility().addEffect(new DrawCardControllerEffect(3));
this.getSpellAbility().addEffect(new DiscardControllerEffect(1));
this.getSpellAbility().addEffect(new DrawDiscardControllerEffect(3,1));
}
public Sift(final Sift card) {

View file

@ -0,0 +1,68 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.timespiral;
import java.util.UUID;
import mage.abilities.condition.common.MyMainPhaseCondition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.effects.common.DrawDiscardTargetEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.target.TargetPlayer;
/**
*
* @author LevelX2
*/
public class CarefulConsideration extends CardImpl<CarefulConsideration> {
public CarefulConsideration(UUID ownerId) {
super(ownerId, 52, "Careful Consideration", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{U}{U}");
this.expansionSetCode = "TSP";
this.color.setBlue(true);
// Target player draws four cards, then discards three cards. If you cast this spell during your main phase, instead that player draws four cards, then discards two cards.
this.getSpellAbility().addEffect(new ConditionalOneShotEffect(
new DrawDiscardTargetEffect(4,2),
new DrawDiscardTargetEffect(4,3),
MyMainPhaseCondition.getInstance(),
"Target player draws four cards, then discards three cards. If you cast this spell during your main phase, instead that player draws four cards, then discards two cards"));
this.getSpellAbility().addTarget(new TargetPlayer(true));
}
public CarefulConsideration(final CarefulConsideration card) {
super(card);
}
@Override
public CarefulConsideration copy() {
return new CarefulConsideration(this);
}
}

View file

@ -28,10 +28,9 @@
package mage.sets.urzaslegacy;
import java.util.UUID;
import mage.abilities.effects.common.DrawDiscardControllerEffect;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.effects.common.DiscardControllerEffect;
import mage.abilities.effects.common.DrawCardControllerEffect;
import mage.abilities.effects.common.UntapLandsEffect;
import mage.cards.CardImpl;
@ -48,8 +47,7 @@ public class FranticSearch extends CardImpl<FranticSearch> {
this.color.setBlue(true);
// Draw two cards, then discard two cards. Untap up to three lands.
this.getSpellAbility().addEffect(new DrawCardControllerEffect(2));
this.getSpellAbility().addEffect(new DiscardControllerEffect(2));
this.getSpellAbility().addEffect(new DrawDiscardControllerEffect(2, 2));
this.getSpellAbility().addEffect(new UntapLandsEffect(3));
}

View file

@ -33,6 +33,7 @@ import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DrawDiscardControllerEffect;
import mage.cards.CardImpl;
import mage.game.Game;
import mage.players.Player;
@ -49,7 +50,7 @@ public class Catalog extends CardImpl<Catalog> {
this.color.setBlue(true);
// Draw two cards, then discard a card.
this.getSpellAbility().addEffect(new CatalogEffect());
this.getSpellAbility().addEffect(new DrawDiscardControllerEffect(2,1));
}
public Catalog(final Catalog card) {
@ -61,31 +62,3 @@ public class Catalog extends CardImpl<Catalog> {
return new Catalog(this);
}
}
class CatalogEffect extends OneShotEffect<CatalogEffect> {
public CatalogEffect() {
super(Outcome.DrawCard);
this.staticText = "Draw two cards, then discard a card";
}
public CatalogEffect(final CatalogEffect effect) {
super(effect);
}
@Override
public CatalogEffect copy() {
return new CatalogEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
player.drawCards(2, game);
player.discard(1, source, game);
return true;
}
return false;
}
}

View file

@ -47,6 +47,7 @@ public class ChainReaction extends CardImpl<ChainReaction> {
this.color.setRed(true);
// Chain Reaction deals X damage to each creature, where X is the number of creatures on the battlefield.
this.getSpellAbility().addEffect(new DamageAllEffect(new PermanentsOnBattlefieldCount(new FilterCreaturePermanent()), new FilterCreaturePermanent()));
}

View file

@ -0,0 +1,58 @@
/*
* 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.condition.common;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
* @author LevelX2
*/
public class SourceTappedCondition implements Condition {
private static SourceTappedCondition fInstance = new SourceTappedCondition();
public static Condition getInstance() {
return fInstance;
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId());
if (permanent != null) {
return permanent.isTapped();
}
return false;
}
}

View file

@ -1,12 +1,12 @@
package mage.abilities.decorator;
import mage.constants.Duration;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.condition.Condition;
import mage.abilities.condition.common.FixedCondition;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.SubLayer;
import mage.game.Game;
@ -84,6 +84,9 @@ public class ConditionalContinousEffect extends ContinuousEffectImpl<Conditional
if (!condition.apply(game, source) && effect.getDuration() == Duration.OneUse) {
used = true;
}
if (!condition.apply(game, source) && effect.getDuration() == Duration.Custom) {
this.discard();
}
return false;
}

View file

@ -156,7 +156,8 @@ public class ContinuousEffects implements Serializable {
case WhileInGraveyard:
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
for (Ability ability: abilities) {
if (ability.isInUseableZone(game, null, false)) {
// If e.g. triggerd abilities (non static) created the effect, the ability must not be in usable zone (e.g. Unearth giving Haste effect)
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false)) {
layerEffects.add(effect);
break;
}

View file

@ -28,11 +28,12 @@
package mage.abilities.effects.common;
import mage.constants.Outcome;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
/**
*
@ -40,13 +41,29 @@ import mage.players.Player;
*/
public class DrawDiscardControllerEffect extends OneShotEffect<DrawDiscardControllerEffect> {
private int cardsToDraw;
private int cardsToDiscard;
public DrawDiscardControllerEffect() {
this(1,1);
}
public DrawDiscardControllerEffect(int cardsToDraw, int cardsToDiscard) {
super(Outcome.DrawCard);
staticText = "Draw a card, then discard a card";
this.cardsToDraw = cardsToDraw;
this.cardsToDiscard = cardsToDiscard;
staticText = new StringBuilder("Draw ")
.append(cardsToDraw == 1?"a": CardUtil.numberToText(cardsToDraw))
.append(" card").append(cardsToDraw == 1?" ": "s")
.append(", then discard ")
.append(cardsToDiscard == 1?"a": CardUtil.numberToText(cardsToDiscard))
.append(" card").append(cardsToDiscard == 1?" ": "s").toString();
}
public DrawDiscardControllerEffect(final DrawDiscardControllerEffect effect) {
super(effect);
this.cardsToDraw = effect.cardsToDraw;
this.cardsToDiscard = effect.cardsToDiscard;
}
@Override
@ -58,8 +75,8 @@ public class DrawDiscardControllerEffect extends OneShotEffect<DrawDiscardContro
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
player.drawCards(1, game);
player.discard(1, source, game);
player.drawCards(cardsToDraw, game);
player.discard(cardsToDiscard, source, game);
return true;
}
return false;

View file

@ -0,0 +1,85 @@
/*
* Copyright 2011 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
/**
*
* @author LevelX2
*/
public class DrawDiscardTargetEffect extends OneShotEffect<DrawDiscardTargetEffect> {
private int cardsToDraw;
private int cardsToDiscard;
public DrawDiscardTargetEffect() {
this(1,1);
}
public DrawDiscardTargetEffect(int cardsToDraw, int cardsToDiscard) {
super(Outcome.DrawCard);
this.cardsToDraw = cardsToDraw;
this.cardsToDiscard = cardsToDiscard;
staticText = new StringBuilder("Target player draws ")
.append(cardsToDraw == 1?"a": CardUtil.numberToText(cardsToDraw))
.append(" card").append(cardsToDraw == 1?" ": "s")
.append(", then discard ")
.append(cardsToDiscard == 1?"a": CardUtil.numberToText(cardsToDiscard))
.append(" card").append(cardsToDiscard == 1?" ": "s").toString();
}
public DrawDiscardTargetEffect(final DrawDiscardTargetEffect effect) {
super(effect);
this.cardsToDraw = effect.cardsToDraw;
this.cardsToDiscard = effect.cardsToDiscard;
}
@Override
public DrawDiscardTargetEffect copy() {
return new DrawDiscardTargetEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
if (player != null) {
player.drawCards(cardsToDraw, game);
player.discard(cardsToDiscard, source, game);
return true;
}
return false;
}
}

View file

@ -75,7 +75,7 @@ public class SacrificeTargetEffect extends OneShotEffect<SacrificeTargetEffect>
@Override
public String getText(Mode mode) {
if ("".equals(staticText) && !mode.getTargets().isEmpty()) {
if (staticText.isEmpty() && !mode.getTargets().isEmpty()) {
if (mode.getTargets().get(0).getNumberOfTargets() == 1) {
return "The controller of target " + mode.getTargets().get(0).getTargetName() + " sacrifices it";
} else {

View file

@ -28,12 +28,12 @@
package mage.abilities.effects.common.continious;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.game.Game;
import mage.game.permanent.Permanent;
@ -73,7 +73,7 @@ public class GainAbilitySourceEffect extends ContinuousEffectImpl<GainAbilitySou
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null) {
permanent.addAbility(ability, game);
permanent.addAbility(ability, source.getSourceId(), game);
return true;
}
return false;

View file

@ -0,0 +1,111 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.keyword;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.AdjustingSourceCosts;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.game.Game;
import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetCardInYourGraveyard;
import mage.util.CardUtil;
/**
* 702.64. Delve
*
* 702.64a Delve is a static ability that functions while the spell that has delve is on
* the stack. "Delve" means "As an additional cost to cast this spell, you may exile any
* number of cards from your graveyard. Each card exiled this way reduces the cost to cast
* this spell by {1}." Using the delve ability follows the rules for paying additional
* costs in rules 601.2b and 601.2e-g. #
*
* 702.64b Multiple instances of delve on the same spell are redundant.
*
* @author LevelX2
*
*/
public class DelveAbility extends SimpleStaticAbility implements AdjustingSourceCosts {
public DelveAbility() {
super(Zone.STACK, null);
this.setRuleAtTheTop(true);
}
public DelveAbility(final DelveAbility ability) {
super(ability);
}
@Override
public DelveAbility copy() {
return new DelveAbility(this);
}
@Override
public void adjustCosts(Ability ability, Game game) {
Player player = game.getPlayer(controllerId);
if (player == null || !(ability instanceof SpellAbility)) {
return;
}
Target target = new TargetCardInYourGraveyard(1, Integer.MAX_VALUE, new FilterCard());
target.setTargetName("cards to delve from your graveyard");
if (!target.canChoose(sourceId, controllerId, game)) {
return;
}
if (player.chooseUse(Outcome.Detriment, "Delve cards from your graveyard?", game)) {
player.chooseTarget(Outcome.Detriment, target, ability, game);
if (target.getTargets().size() > 0) {
int adjCost = 0;
for (UUID cardId: target.getTargets()) {
Card card = game.getCard(cardId);
if (card == null) {
continue;
}
card.moveToExile(null, null, this.getSourceId(), game);
++adjCost;
}
game.informPlayers(new StringBuilder(player.getName()).append(" delved ")
.append(adjCost).append(" creature").append(adjCost != 1?"s":"").append(" from his or her graveyard").toString());
CardUtil.adjustCost((SpellAbility)ability, adjCost);
}
}
}
@Override
public String getRule() {
return "Delve <i>(You may exile any number of cards from your graveyard as you cast this spell. It costs {1} less to cast for each card exiled this way.)</i>";
}
}

View file

@ -75,7 +75,7 @@ public class InfectAbility extends StaticAbility<InfectAbility> implements MageS
@Override
public String getRule() {
return "Infect";
return "Infect <i>(This creature deals damage to creatures in the form of -1/-1 counters and to players in the form of poison counters.)</i>";
}
@Override

View file

@ -28,10 +28,6 @@
package mage.abilities.keyword;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.TimingRule;
import mage.constants.Zone;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbilityImpl;
import mage.abilities.DelayedTriggeredAbility;
@ -39,8 +35,12 @@ import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.ExileSourceEffect;
import mage.abilities.effects.common.continious.GainAbilitySourceEffect;
import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect;
import mage.abilities.effects.common.continious.GainAbilitySourceEffect;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.TimingRule;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
@ -49,11 +49,19 @@ import mage.game.events.ZoneChangeEvent;
/**
*
* @author BetaSteward_at_googlemail.com
*
*
* 702.82. Unearth
*
* 702.82a Unearth is an activated ability that functions while the card with unearth
* is in a graveyard. "Unearth [cost]" means "[Cost]: Return this card from your graveyard
* to the battlefield. It gains haste. Exile it at the beginning of the next end step.
* If it would leave the battlefield, exile it instead of putting it anywhere else.
* Activate this ability only any time you could cast a sorcery."
*
*/
public class UnearthAbility extends ActivatedAbilityImpl<UnearthAbility> {
protected boolean unearthed;
public UnearthAbility(ManaCosts costs) {
super(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(), costs);
this.timing = TimingRule.SORCERY;
@ -64,7 +72,6 @@ public class UnearthAbility extends ActivatedAbilityImpl<UnearthAbility> {
public UnearthAbility(final UnearthAbility ability) {
super(ability);
this.unearthed = ability.unearthed;
}
@Override
@ -72,13 +79,12 @@ public class UnearthAbility extends ActivatedAbilityImpl<UnearthAbility> {
return new UnearthAbility(this);
}
public boolean isUnearthed() {
return unearthed;
}
@Override
public String getRule() {
return "Unearth " + super.getRule();
StringBuilder sb = new StringBuilder("Unearth ").append(this.getManaCosts().getText());
sb.append(" <i>(").append(this.getManaCosts().getText());
sb.append(": Return this card from your graveyard to the battlefield. It gains haste. Exile it at the beginning of the next end step or if it would leave the battlefield. Unearth only as a sorcery.)");
return sb.toString();
}
}
@ -133,8 +139,9 @@ class UnearthLeavesBattlefieldEffect extends ReplacementEffectImpl<UnearthLeaves
public boolean applies(GameEvent event, Ability source, Game game) {
if (event.getType() == EventType.ZONE_CHANGE && event.getTargetId().equals(source.getSourceId())) {
ZoneChangeEvent zEvent = (ZoneChangeEvent)event;
if (zEvent.getFromZone() == Zone.BATTLEFIELD && zEvent.getToZone() != Zone.EXILED)
if (zEvent.getFromZone() == Zone.BATTLEFIELD && zEvent.getToZone() != Zone.EXILED) {
return true;
}
}
return false;
}
@ -149,5 +156,4 @@ class UnearthLeavesBattlefieldEffect extends ReplacementEffectImpl<UnearthLeaves
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
return apply(game, source);
}
}
}

View file

@ -30,9 +30,11 @@ package mage.cards.repository;
import com.j256.ormlite.field.DataType;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.ObjectColor;
@ -84,7 +86,17 @@ public class CardInfo {
@DatabaseField
protected boolean white;
@DatabaseField
protected boolean splitCard;
@DatabaseField
protected boolean flipCard;
@DatabaseField
protected boolean doubleFaced;
@DatabaseField
protected boolean nightCard;
@DatabaseField
protected String flipCardName;
@DatabaseField
protected String secondSideName;
public CardInfo() {
}
@ -98,7 +110,17 @@ public class CardInfo {
this.toughness = card.getToughness().toString();
this.convertedManaCost = card.getManaCost().convertedManaCost();
this.rarity = card.getRarity();
this.splitCard = card.isSplitCard();
this.flipCard = card.isFlipCard();
this.flipCardName = card.getFlipCardName();
this.doubleFaced = card.canTransform();
this.nightCard = card.isNightCard();
Card secondSide = card.getSecondCardFace();
if (secondSide != null) {
this.secondSideName = secondSide.getName();
}
this.blue = card.getColor().isBlue();
this.black = card.getColor().isBlack();
@ -144,7 +166,7 @@ public class CardInfo {
for (String type : this.types.split(SEPARATOR)) {
try {
list.add(CardType.valueOf(type));
} catch (Exception e) {
} catch (IllegalArgumentException e) {
}
}
return list;
@ -217,4 +239,32 @@ public class CardInfo {
public String getClassName() {
return className;
}
public int getCardNumber() {
return cardNumber;
}
public boolean isSplitCard() {
return splitCard;
}
public boolean isFlipCard() {
return flipCard;
}
public String getFlipCardName() {
return flipCardName;
}
public boolean isDoubleFaced() {
return doubleFaced;
}
public boolean isNightCard() {
return nightCard;
}
public String getSecondSideName() {
return secondSideName;
}
}

View file

@ -54,7 +54,7 @@ public enum CardRepository {
instance;
private static final String JDBC_URL = "jdbc:sqlite:db/cards.db";
private static final long DB_VERSION = 6;
private static final long DB_VERSION = 7;
private Random random = new Random();
private Dao<CardInfo, Object> cardDao;

View file

@ -28,9 +28,6 @@
package mage.game;
import mage.constants.MultiplayerAttackOption;
import mage.constants.RangeOfInfluence;
import mage.constants.Zone;
import mage.MageItem;
import mage.MageObject;
import mage.abilities.Ability;
@ -44,6 +41,10 @@ import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.decks.Deck;
import mage.choices.Choice;
import mage.constants.Duration;
import mage.constants.MultiplayerAttackOption;
import mage.constants.RangeOfInfluence;
import mage.constants.Zone;
import mage.game.combat.Combat;
import mage.game.command.Emblem;
import mage.game.events.GameEvent;
@ -65,7 +66,6 @@ import mage.util.functions.ApplyToPermanent;
import java.io.Serializable;
import java.util.*;
import mage.constants.Duration;
public interface Game extends MageItem, Serializable {
@ -225,4 +225,9 @@ public interface Game extends MageItem, Serializable {
// controlling the behaviour of replacement effects
void setScopeRelevant(boolean scopeRelevant);
public boolean getScopeRelevant();
// players' timers
void initTimer(UUID playerId);
void resumeTimer(UUID playerId);
void pauseTimer(UUID playerId);
}

View file

@ -29,7 +29,6 @@
package mage.game;
import mage.Constants;
import mage.constants.CardType;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
@ -49,12 +48,14 @@ import mage.actions.impl.MageAction;
import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.cards.SplitCard;
import mage.cards.decks.Deck;
import mage.choices.Choice;
import mage.constants.*;
import mage.counters.CounterType;
import mage.filter.Filter;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.common.FilterPlaneswalkerPermanent;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.filter.predicate.mageobject.NamePredicate;
@ -89,8 +90,6 @@ import java.io.IOException;
import java.io.Serializable;
import java.util.*;
import java.util.Map.Entry;
import mage.cards.SplitCard;
import mage.filter.common.FilterControlledCreaturePermanent;
public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializable {
@ -590,6 +589,7 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
protected void init(UUID choosingPlayerId, GameOptions gameOptions) {
for (Player player: state.getPlayers().values()) {
player.beginTurn(this);
initTimer(player.getId());
}
if (startMessage == null || startMessage.isEmpty()) {
startMessage = "Game has started";
@ -1946,4 +1946,20 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
public void setStartMessage(String startMessage) {
this.startMessage = startMessage;
}
@Override
public void initTimer(UUID playerId) {
tableEventSource.fireTableEvent(EventType.INIT_TIMER, playerId, null, this);
}
@Override
public void resumeTimer(UUID playerId) {
tableEventSource.fireTableEvent(EventType.RESUME_TIMER, playerId, null, this);
}
@Override
public void pauseTimer(UUID playerId) {
tableEventSource.fireTableEvent(EventType.PAUSE_TIMER, playerId, null, this);
}
}

View file

@ -46,7 +46,8 @@ import java.util.UUID;
public class TableEvent extends EventObject implements ExternalEvent, Serializable {
public enum EventType {
UPDATE, INFO, STATUS, REVEAL, LOOK, START_DRAFT, START_MATCH, SIDEBOARD, CONSTRUCT, SUBMIT_DECK, END, ERROR
UPDATE, INFO, STATUS, REVEAL, LOOK, START_DRAFT, START_MATCH, SIDEBOARD, CONSTRUCT, SUBMIT_DECK, END, ERROR,
INIT_TIMER, RESUME_TIMER, PAUSE_TIMER
}
private Game game;

View file

@ -299,4 +299,18 @@ public interface Player extends MageItem, Copyable<Player> {
*
*/
void revealFaceDownCard(Card card, Game game);
/**
* Set seconds left to play the game.
*
* @return
*/
void setPriorityTimeLeft(int timeLeft);
/**
* Returns seconds left to play the game.
*
* @return
*/
int getPriorityTimeLeft();
}

View file

@ -28,8 +28,6 @@
package mage.players;
import mage.constants.*;
import mage.constants.Zone;
import mage.MageObject;
import mage.Mana;
import mage.abilities.*;
@ -46,10 +44,13 @@ import mage.actions.MageDrawAction;
import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.cards.SplitCard;
import mage.cards.decks.Deck;
import mage.constants.*;
import mage.counters.Counter;
import mage.counters.CounterType;
import mage.counters.Counters;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureForCombat;
import mage.game.ExileZone;
import mage.game.Game;
@ -59,11 +60,13 @@ import mage.game.events.DamagedPlayerEvent;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.game.stack.Spell;
import mage.game.stack.StackAbility;
import mage.game.stack.StackObject;
import mage.players.net.UserData;
import mage.target.Target;
import mage.target.TargetAmount;
import mage.target.TargetCard;
import mage.target.common.TargetCardInLibrary;
import mage.target.common.TargetDiscard;
import mage.watchers.common.BloodthirstWatcher;
@ -72,11 +75,6 @@ import org.apache.log4j.Logger;
import java.io.Serializable;
import java.util.*;
import mage.cards.SplitCard;
import mage.filter.FilterCard;
import mage.game.stack.Spell;
import mage.target.TargetCard;
public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Serializable {
@ -108,6 +106,7 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
protected boolean passedTurn;
protected int turns;
protected int storedBookmark = -1;
protected int priorityTimeLeft;
/**
* This indicates that player passed all turns until his own turn starts.
@ -1793,4 +1792,14 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
this.revealCards(name, cards, game);
}
}
@Override
public void setPriorityTimeLeft(int timeLeft) {
priorityTimeLeft = timeLeft;
}
@Override
public int getPriorityTimeLeft() {
return priorityTimeLeft;
}
}

View file

@ -29,6 +29,7 @@
package mage.util;
import java.util.Iterator;
import java.util.UUID;
import mage.Mana;
import mage.abilities.Ability;
@ -43,6 +44,7 @@ import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.costs.mana.VariableManaCost;
import mage.cards.Card;
import mage.constants.CardType;
import mage.game.Game;
import mage.game.permanent.token.Token;
import mage.util.functions.CopyFunction;
import mage.util.functions.CopyTokenFunction;
@ -365,4 +367,25 @@ public class CardUtil {
}
return true;
}
/**
* Creates and saves a (card + zoneChangeCounter) specific exileId.
*
*
* @param game
* @param source - source ability
* @return - the specific UUID
*/
public static UUID getCardExileZoneId(Game game, Ability source) {
UUID exileId = null;
Card card = game.getCard(source.getSourceId());
if (card != null) {
exileId = (UUID) game.getState().getValue(new StringBuilder("SourceExileZone").append(source.getSourceId()).append(card.getZoneChangeCounter()).toString());
if (exileId == null) {
exileId = UUID.randomUUID();
game.getState().setValue(new StringBuilder("SourceExileZone").append(source.getSourceId()).append(card.getZoneChangeCounter()).toString(), exileId);
}
}
return exileId;
}
}